aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/iwl-4965.c
diff options
context:
space:
mode:
authorZhu Yi <yi.zhu@intel.com>2007-12-19 22:27:32 -0500
committerDavid S. Miller <davem@davemloft.net>2008-01-28 18:07:53 -0500
commit12342c475f5de17071eaf24ea2938ba8dfe285f2 (patch)
treea2cdfd191069397e093f2410009092e7e96c9325 /drivers/net/wireless/iwlwifi/iwl-4965.c
parent7e94041ca17685cf12c658b8edc008dd0bdb00c7 (diff)
iwlwifi: proper monitor support
This patch changes the iwlwifi driver to properly support monitor interfaces after the filter flags change. The patch is originally created by Johannes Berg for iwl4965. I fixed some of the comments and created a similar patch for iwl3945. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: Zhu Yi <yi.zhu@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-4965.c')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965.c120
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
3592static 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
3591static void iwl4965_handle_data_packet(struct iwl4965_priv *priv, int is_data, 3697static 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;