diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-4965.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-4965.c | 120 |
1 files changed, 110 insertions, 10 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index f65fd6e5fecc..74999af37914 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/wireless.h> | 36 | #include <linux/wireless.h> |
37 | #include <net/mac80211.h> | 37 | #include <net/mac80211.h> |
38 | #include <linux/etherdevice.h> | 38 | #include <linux/etherdevice.h> |
39 | #include <asm/unaligned.h> | ||
39 | 40 | ||
40 | #include "iwl-4965.h" | 41 | #include "iwl-4965.h" |
41 | #include "iwl-helpers.h" | 42 | #include "iwl-helpers.h" |
@@ -3588,6 +3589,111 @@ void iwl4965_hw_rx_statistics(struct iwl4965_priv *priv, struct iwl4965_rx_mem_b | |||
3588 | queue_work(priv->workqueue, &priv->txpower_work); | 3589 | queue_work(priv->workqueue, &priv->txpower_work); |
3589 | } | 3590 | } |
3590 | 3591 | ||
3592 | static void iwl4965_add_radiotap(struct iwl4965_priv *priv, | ||
3593 | struct sk_buff *skb, | ||
3594 | struct iwl4965_rx_phy_res *rx_start, | ||
3595 | struct ieee80211_rx_status *stats, | ||
3596 | u32 ampdu_status) | ||
3597 | { | ||
3598 | s8 signal = stats->ssi; | ||
3599 | s8 noise = 0; | ||
3600 | int rate = stats->rate; | ||
3601 | u64 tsf = stats->mactime; | ||
3602 | __le16 phy_flags_hw = rx_start->phy_flags; | ||
3603 | struct iwl4965_rt_rx_hdr { | ||
3604 | struct ieee80211_radiotap_header rt_hdr; | ||
3605 | __le64 rt_tsf; /* TSF */ | ||
3606 | u8 rt_flags; /* radiotap packet flags */ | ||
3607 | u8 rt_rate; /* rate in 500kb/s */ | ||
3608 | __le16 rt_channelMHz; /* channel in MHz */ | ||
3609 | __le16 rt_chbitmask; /* channel bitfield */ | ||
3610 | s8 rt_dbmsignal; /* signal in dBm, kluged to signed */ | ||
3611 | s8 rt_dbmnoise; | ||
3612 | u8 rt_antenna; /* antenna number */ | ||
3613 | } __attribute__ ((packed)) *iwl4965_rt; | ||
3614 | |||
3615 | /* TODO: We won't have enough headroom for HT frames. Fix it later. */ | ||
3616 | if (skb_headroom(skb) < sizeof(*iwl4965_rt)) { | ||
3617 | if (net_ratelimit()) | ||
3618 | printk(KERN_ERR "not enough headroom [%d] for " | ||
3619 | "radiotap head [%d]\n", | ||
3620 | skb_headroom(skb), sizeof(*iwl4965_rt)); | ||
3621 | return; | ||
3622 | } | ||
3623 | |||
3624 | /* put radiotap header in front of 802.11 header and data */ | ||
3625 | iwl4965_rt = (void *)skb_push(skb, sizeof(*iwl4965_rt)); | ||
3626 | |||
3627 | /* initialise radiotap header */ | ||
3628 | iwl4965_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; | ||
3629 | iwl4965_rt->rt_hdr.it_pad = 0; | ||
3630 | |||
3631 | /* total header + data */ | ||
3632 | put_unaligned(cpu_to_le16(sizeof(*iwl4965_rt)), | ||
3633 | &iwl4965_rt->rt_hdr.it_len); | ||
3634 | |||
3635 | /* Indicate all the fields we add to the radiotap header */ | ||
3636 | put_unaligned(cpu_to_le32((1 << IEEE80211_RADIOTAP_TSFT) | | ||
3637 | (1 << IEEE80211_RADIOTAP_FLAGS) | | ||
3638 | (1 << IEEE80211_RADIOTAP_RATE) | | ||
3639 | (1 << IEEE80211_RADIOTAP_CHANNEL) | | ||
3640 | (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | | ||
3641 | (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | | ||
3642 | (1 << IEEE80211_RADIOTAP_ANTENNA)), | ||
3643 | &iwl4965_rt->rt_hdr.it_present); | ||
3644 | |||
3645 | /* Zero the flags, we'll add to them as we go */ | ||
3646 | iwl4965_rt->rt_flags = 0; | ||
3647 | |||
3648 | put_unaligned(cpu_to_le64(tsf), &iwl4965_rt->rt_tsf); | ||
3649 | |||
3650 | iwl4965_rt->rt_dbmsignal = signal; | ||
3651 | iwl4965_rt->rt_dbmnoise = noise; | ||
3652 | |||
3653 | /* Convert the channel frequency and set the flags */ | ||
3654 | put_unaligned(cpu_to_le16(stats->freq), &iwl4965_rt->rt_channelMHz); | ||
3655 | if (!(phy_flags_hw & RX_RES_PHY_FLAGS_BAND_24_MSK)) | ||
3656 | put_unaligned(cpu_to_le16(IEEE80211_CHAN_OFDM | | ||
3657 | IEEE80211_CHAN_5GHZ), | ||
3658 | &iwl4965_rt->rt_chbitmask); | ||
3659 | else if (phy_flags_hw & RX_RES_PHY_FLAGS_MOD_CCK_MSK) | ||
3660 | put_unaligned(cpu_to_le16(IEEE80211_CHAN_CCK | | ||
3661 | IEEE80211_CHAN_2GHZ), | ||
3662 | &iwl4965_rt->rt_chbitmask); | ||
3663 | else /* 802.11g */ | ||
3664 | put_unaligned(cpu_to_le16(IEEE80211_CHAN_OFDM | | ||
3665 | IEEE80211_CHAN_2GHZ), | ||
3666 | &iwl4965_rt->rt_chbitmask); | ||
3667 | |||
3668 | rate = iwl4965_rate_index_from_plcp(rate); | ||
3669 | if (rate == -1) | ||
3670 | iwl4965_rt->rt_rate = 0; | ||
3671 | else | ||
3672 | iwl4965_rt->rt_rate = iwl4965_rates[rate].ieee; | ||
3673 | |||
3674 | /* | ||
3675 | * "antenna number" | ||
3676 | * | ||
3677 | * It seems that the antenna field in the phy flags value | ||
3678 | * is actually a bitfield. This is undefined by radiotap, | ||
3679 | * it wants an actual antenna number but I always get "7" | ||
3680 | * for most legacy frames I receive indicating that the | ||
3681 | * same frame was received on all three RX chains. | ||
3682 | * | ||
3683 | * I think this field should be removed in favour of a | ||
3684 | * new 802.11n radiotap field "RX chains" that is defined | ||
3685 | * as a bitmask. | ||
3686 | */ | ||
3687 | iwl4965_rt->rt_antenna = | ||
3688 | le16_to_cpu(phy_flags_hw & RX_RES_PHY_FLAGS_ANTENNA_MSK) >> 4; | ||
3689 | |||
3690 | /* set the preamble flag if appropriate */ | ||
3691 | if (phy_flags_hw & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) | ||
3692 | iwl4965_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; | ||
3693 | |||
3694 | stats->flag |= RX_FLAG_RADIOTAP; | ||
3695 | } | ||
3696 | |||
3591 | static void iwl4965_handle_data_packet(struct iwl4965_priv *priv, int is_data, | 3697 | static void iwl4965_handle_data_packet(struct iwl4965_priv *priv, int is_data, |
3592 | int include_phy, | 3698 | int include_phy, |
3593 | struct iwl4965_rx_mem_buffer *rxb, | 3699 | struct iwl4965_rx_mem_buffer *rxb, |
@@ -3630,8 +3736,7 @@ static void iwl4965_handle_data_packet(struct iwl4965_priv *priv, int is_data, | |||
3630 | rx_end = (__le32 *) (((u8 *) hdr) + len); | 3736 | rx_end = (__le32 *) (((u8 *) hdr) + len); |
3631 | } | 3737 | } |
3632 | if (len > priv->hw_setting.max_pkt_size || len < 16) { | 3738 | if (len > priv->hw_setting.max_pkt_size || len < 16) { |
3633 | IWL_WARNING("byte count out of range [16,4K]" | 3739 | IWL_WARNING("byte count out of range [16,4K] : %d\n", len); |
3634 | " : %d\n", len); | ||
3635 | return; | 3740 | return; |
3636 | } | 3741 | } |
3637 | 3742 | ||
@@ -3649,20 +3754,15 @@ static void iwl4965_handle_data_packet(struct iwl4965_priv *priv, int is_data, | |||
3649 | return; | 3754 | return; |
3650 | } | 3755 | } |
3651 | 3756 | ||
3652 | if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) { | ||
3653 | if (iwl4965_param_hwcrypto) | ||
3654 | iwl4965_set_decrypted_flag(priv, rxb->skb, | ||
3655 | ampdu_status, stats); | ||
3656 | iwl4965_handle_data_packet_monitor(priv, rxb, hdr, len, stats, 0); | ||
3657 | return; | ||
3658 | } | ||
3659 | |||
3660 | stats->flag = 0; | 3757 | stats->flag = 0; |
3661 | hdr = (struct ieee80211_hdr *)rxb->skb->data; | 3758 | hdr = (struct ieee80211_hdr *)rxb->skb->data; |
3662 | 3759 | ||
3663 | if (iwl4965_param_hwcrypto) | 3760 | if (iwl4965_param_hwcrypto) |
3664 | iwl4965_set_decrypted_flag(priv, rxb->skb, ampdu_status, stats); | 3761 | iwl4965_set_decrypted_flag(priv, rxb->skb, ampdu_status, stats); |
3665 | 3762 | ||
3763 | if (priv->add_radiotap) | ||
3764 | iwl4965_add_radiotap(priv, rxb->skb, rx_start, stats, ampdu_status); | ||
3765 | |||
3666 | ieee80211_rx_irqsafe(priv->hw, rxb->skb, stats); | 3766 | ieee80211_rx_irqsafe(priv->hw, rxb->skb, stats); |
3667 | priv->alloc_rxb_skb--; | 3767 | priv->alloc_rxb_skb--; |
3668 | rxb->skb = NULL; | 3768 | rxb->skb = NULL; |