Skip to content

Commit

Permalink
rt2x00: Implement support for 802.11n
Browse files Browse the repository at this point in the history
Extend rt2x00lib capabilities to support 802.11n,
it still lacks aggregation support, but that can
be added in the future.

Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Ivo van Doorn authored and John W. Linville committed May 6, 2009
1 parent 9f16617 commit 35f00cf
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 34 deletions.
3 changes: 3 additions & 0 deletions drivers/net/wireless/rt2x00/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ config RT2X00_LIB_USB
config RT2X00_LIB
tristate

config RT2X00_LIB_HT
boolean

config RT2X00_LIB_FIRMWARE
boolean
select FW_LOADER
Expand Down
1 change: 1 addition & 0 deletions drivers/net/wireless/rt2x00/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ rt2x00lib-$(CONFIG_RT2X00_LIB_CRYPTO) += rt2x00crypto.o
rt2x00lib-$(CONFIG_RT2X00_LIB_RFKILL) += rt2x00rfkill.o
rt2x00lib-$(CONFIG_RT2X00_LIB_FIRMWARE) += rt2x00firmware.o
rt2x00lib-$(CONFIG_RT2X00_LIB_LEDS) += rt2x00leds.o
rt2x00lib-$(CONFIG_RT2X00_LIB_HT) += rt2x00ht.o

obj-$(CONFIG_RT2X00_LIB) += rt2x00lib.o
obj-$(CONFIG_RT2X00_LIB_PCI) += rt2x00pci.o
Expand Down
11 changes: 11 additions & 0 deletions drivers/net/wireless/rt2x00/rt2x00.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ static inline struct rt2x00_intf* vif_to_intf(struct ieee80211_vif *vif)
* 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;
Expand All @@ -379,6 +380,8 @@ struct hw_mode_spec {
unsigned int num_channels;
const struct rf_channel *channels;
const struct channel_info *channels_info;

struct ieee80211_sta_ht_cap ht;
};

/*
Expand Down Expand Up @@ -616,6 +619,7 @@ enum rt2x00_flags {
CONFIG_EXTERNAL_LNA_BG,
CONFIG_DOUBLE_ANTENNA,
CONFIG_DISABLE_LINK_TUNING,
CONFIG_CHANNEL_HT40,
};

/*
Expand Down Expand Up @@ -787,6 +791,13 @@ struct rt2x00_dev {
*/
u8 freq_offset;

/*
* Calibration information (for rt2800usb & rt2800pci).
* [0] -> BW20
* [1] -> BW40
*/
u8 calibration[2];

/*
* Low level statistics which will have
* to be kept up to date while device is running.
Expand Down
5 changes: 5 additions & 0 deletions drivers/net/wireless/rt2x00/rt2x00config.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev,
libconf.conf = conf;

if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) {
if (conf_is_ht40(conf))
__set_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags);
else
__clear_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags);

memcpy(&libconf.rf,
&rt2x00dev->spec.channels[conf->channel->hw_value],
sizeof(libconf.rf));
Expand Down
92 changes: 67 additions & 25 deletions drivers/net/wireless/rt2x00/rt2x00dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -323,19 +323,54 @@ void rt2x00lib_txdone(struct queue_entry *entry,
}
EXPORT_SYMBOL_GPL(rt2x00lib_txdone);

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;
int type;

/*
* 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, rxdesc->signal);
else
signal = rxdesc->signal;

type = (rxdesc->dev_flags & RXDONE_SIGNAL_MASK);

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;
}
}

WARNING(rt2x00dev, "Frame received with unrecognized signal, "
"signal=0x%.4x, type=%d.\n", signal, type);
return 0;
}

void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
struct queue_entry *entry)
{
struct rxdone_entry_desc rxdesc;
struct sk_buff *skb;
struct ieee80211_rx_status *rx_status = &rt2x00dev->rx_status;
struct ieee80211_supported_band *sband;
const struct rt2x00_rate *rate;
unsigned int header_length;
bool l2pad;
unsigned int i;
int idx = -1;

int rate_idx;
/*
* Allocate a new sk_buffer. If no new buffer available, drop the
* received frame and reuse the existing buffer.
Expand Down Expand Up @@ -379,26 +414,17 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
rt2x00queue_payload_align(entry->skb, l2pad, header_length);

/*
* Update RX statistics.
* Check if the frame was received using HT. In that case,
* the rate is the MCS index and should be passed to mac80211
* directly. Otherwise we need to translate the signal to
* the correct bitrate index.
*/
sband = &rt2x00dev->bands[rt2x00dev->curr_band];
for (i = 0; i < sband->n_bitrates; i++) {
rate = rt2x00_get_rate(sband->bitrates[i].hw_value);

if (((rxdesc.dev_flags & RXDONE_SIGNAL_PLCP) &&
(rate->plcp == rxdesc.signal)) ||
((rxdesc.dev_flags & RXDONE_SIGNAL_BITRATE) &&
(rate->bitrate == rxdesc.signal))) {
idx = i;
break;
}
}

if (idx < 0) {
WARNING(rt2x00dev, "Frame received with unrecognized signal,"
"signal=0x%.2x, type=%d.\n", rxdesc.signal,
(rxdesc.dev_flags & RXDONE_SIGNAL_MASK));
idx = 0;
if (rxdesc.rate_mode == RATE_MODE_CCK ||
rxdesc.rate_mode == RATE_MODE_OFDM) {
rate_idx = rt2x00lib_rxdone_read_signal(rt2x00dev, &rxdesc);
} else {
rxdesc.flags |= RX_FLAG_HT;
rate_idx = rxdesc.signal;
}

/*
Expand All @@ -408,7 +434,7 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
rt2x00debug_update_crypto(rt2x00dev, &rxdesc);

rx_status->mactime = rxdesc.timestamp;
rx_status->rate_idx = idx;
rx_status->rate_idx = rate_idx;
rx_status->qual = rt2x00link_calculate_signal(rt2x00dev, rxdesc.rssi);
rx_status->signal = rxdesc.rssi;
rx_status->noise = rxdesc.noise;
Expand Down Expand Up @@ -443,72 +469,84 @@ const struct rt2x00_rate rt2x00_supported_rates[12] = {
.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),
},
};

Expand Down Expand Up @@ -584,6 +622,8 @@ static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev,
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));
}

/*
Expand All @@ -600,6 +640,8 @@ static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev,
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;
Expand Down
69 changes: 69 additions & 0 deletions drivers/net/wireless/rt2x00/rt2x00ht.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
<http://rt2x00.serialmonkey.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; if not, write to the
Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

/*
Module: rt2x00lib
Abstract: rt2x00 HT specific routines.
*/

#include <linux/kernel.h>
#include <linux/module.h>

#include "rt2x00.h"
#include "rt2x00lib.h"

void rt2x00ht_create_tx_descriptor(struct queue_entry *entry,
struct txentry_desc *txdesc,
const struct rt2x00_rate *hwrate)
{
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0];

if (tx_info->control.sta)
txdesc->mpdu_density =
tx_info->control.sta->ht_cap.ampdu_density;
else
txdesc->mpdu_density = 0;

txdesc->ba_size = 7; /* FIXME: What value is needed? */
txdesc->stbc = 0; /* FIXME: What value is needed? */

txdesc->mcs = rt2x00_get_rate_mcs(hwrate->mcs);
if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
txdesc->mcs |= 0x08;

/*
* Convert flags
*/
if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
__set_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags);

/*
* Determine HT Mix/Greenfield rate mode
*/
if (txrate->flags & IEEE80211_TX_RC_MCS)
txdesc->rate_mode = RATE_MODE_HT_MIX;
if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD)
txdesc->rate_mode = RATE_MODE_HT_GREENFIELD;
if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
__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);
}
24 changes: 24 additions & 0 deletions drivers/net/wireless/rt2x00/rt2x00lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ struct rt2x00_rate {
unsigned short ratemask;

unsigned short plcp;
unsigned short mcs;
};

extern const struct rt2x00_rate rt2x00_supported_rates[12];
Expand All @@ -57,6 +58,14 @@ 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.
*/
Expand Down Expand Up @@ -359,6 +368,21 @@ static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, bool l2pad,
}
#endif /* CONFIG_RT2X00_LIB_CRYPTO */

/*
* HT handlers.
*/
#ifdef CONFIG_RT2X00_LIB_HT
void rt2x00ht_create_tx_descriptor(struct queue_entry *entry,
struct txentry_desc *txdesc,
const struct rt2x00_rate *hwrate);
#else
static inline void rt2x00ht_create_tx_descriptor(struct queue_entry *entry,
struct txentry_desc *txdesc,
const struct rt2x00_rate *hwrate)
{
}
#endif /* CONFIG_RT2X00_LIB_HT */

/*
* RFkill handlers.
*/
Expand Down
Loading

0 comments on commit 35f00cf

Please sign in to comment.