rt2x00: Fix max TX power settings

During initialization each driver reads the default TX power
for each individual channel. However mac80211 only accepts the
maximum value (which is also handled as default value).

As a result, the TX power of the device was being limited to
the default value, which is often quite low compared to the
real maximum acceptable value.

This patch allows each driver to set the maximum value on a
per-channel basis which is forwarded to mac80211. The default
value will be preserved for now, in case we want to update
mac80211 to differentiate between the maximum and default txpower.

This fixes bug complaining about limited TX power values like:
https://bugzilla.kernel.org/show_bug.cgi?id=16358

Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Acked-by: Helmut Schaa <helmut.schaa@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Ivo van Doorn 2010-08-23 19:56:07 +02:00 committed by John W. Linville
parent 46323e112c
commit 8d1331b37d
9 changed files with 86 additions and 47 deletions

View File

@ -1487,8 +1487,10 @@ static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
spec->channels_info = info; spec->channels_info = info;
tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START);
for (i = 0; i < 14; i++) for (i = 0; i < 14; i++) {
info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]); info[i].max_power = TXPOWER_FROM_DEV(MAX_TXPOWER);
info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
}
return 0; return 0;
} }

View File

@ -1801,12 +1801,16 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
spec->channels_info = info; spec->channels_info = info;
tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START);
for (i = 0; i < 14; i++) for (i = 0; i < 14; i++) {
info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]); info[i].max_power = MAX_TXPOWER;
info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
}
if (spec->num_channels > 14) { if (spec->num_channels > 14) {
for (i = 14; i < spec->num_channels; i++) for (i = 14; i < spec->num_channels; i++) {
info[i].tx_power1 = DEFAULT_TXPOWER; info[i].max_power = MAX_TXPOWER;
info[i].default_power1 = DEFAULT_TXPOWER;
}
} }
return 0; return 0;

View File

@ -1706,12 +1706,16 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
spec->channels_info = info; spec->channels_info = info;
tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START);
for (i = 0; i < 14; i++) for (i = 0; i < 14; i++) {
info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]); info[i].max_power = MAX_TXPOWER;
info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
}
if (spec->num_channels > 14) { if (spec->num_channels > 14) {
for (i = 14; i < spec->num_channels; i++) for (i = 14; i < spec->num_channels; i++) {
info[i].tx_power1 = DEFAULT_TXPOWER; info[i].max_power = MAX_TXPOWER;
info[i].default_power1 = DEFAULT_TXPOWER;
}
} }
return 0; return 0;

View File

@ -1858,6 +1858,13 @@ struct mac_iveiv_entry {
#define EEPROM_RSSI_A2_OFFSET2 FIELD16(0x00ff) #define EEPROM_RSSI_A2_OFFSET2 FIELD16(0x00ff)
#define EEPROM_RSSI_A2_LNA_A2 FIELD16(0xff00) #define EEPROM_RSSI_A2_LNA_A2 FIELD16(0xff00)
/*
* EEPROM Maximum TX power values
*/
#define EEPROM_MAX_TX_POWER 0x0027
#define EEPROM_MAX_TX_POWER_24GHZ FIELD16(0x00ff)
#define EEPROM_MAX_TX_POWER_5GHZ FIELD16(0xff00)
/* /*
* EEPROM TXpower delta: 20MHZ AND 40 MHZ use different power. * EEPROM TXpower delta: 20MHZ AND 40 MHZ use different power.
* This is delta in 40MHZ. * This is delta in 40MHZ.

View File

@ -1239,23 +1239,23 @@ static void rt2800_config_channel_rf2xxx(struct rt2x00_dev *rt2x00dev,
* double meaning, and we should set a 7DBm boost flag. * double meaning, and we should set a 7DBm boost flag.
*/ */
rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A_7DBM_BOOST, rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A_7DBM_BOOST,
(info->tx_power1 >= 0)); (info->default_power1 >= 0));
if (info->tx_power1 < 0) if (info->default_power1 < 0)
info->tx_power1 += 7; info->default_power1 += 7;
rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A, info->tx_power1); rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A, info->default_power1);
rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A_7DBM_BOOST, rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A_7DBM_BOOST,
(info->tx_power2 >= 0)); (info->default_power2 >= 0));
if (info->tx_power2 < 0) if (info->default_power2 < 0)
info->tx_power2 += 7; info->default_power2 += 7;
rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A, info->tx_power2); rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A, info->default_power2);
} else { } else {
rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_G, info->tx_power1); rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_G, info->default_power1);
rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_G, info->tx_power2); rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_G, info->default_power2);
} }
rt2x00_set_field32(&rf->rf4, RF4_HT40, conf_is_ht40(conf)); rt2x00_set_field32(&rf->rf4, RF4_HT40, conf_is_ht40(conf));
@ -1295,11 +1295,11 @@ static void rt2800_config_channel_rf3xxx(struct rt2x00_dev *rt2x00dev,
rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); rt2800_rfcsr_write(rt2x00dev, 6, rfcsr);
rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr); rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr);
rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, info->tx_power1); rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, info->default_power1);
rt2800_rfcsr_write(rt2x00dev, 12, rfcsr); rt2800_rfcsr_write(rt2x00dev, 12, rfcsr);
rt2800_rfcsr_read(rt2x00dev, 13, &rfcsr); rt2800_rfcsr_read(rt2x00dev, 13, &rfcsr);
rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, info->tx_power2); rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, info->default_power2);
rt2800_rfcsr_write(rt2x00dev, 13, rfcsr); rt2800_rfcsr_write(rt2x00dev, 13, rfcsr);
rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr); rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr);
@ -1324,11 +1324,11 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
u8 bbp; u8 bbp;
if (rf->channel <= 14) { if (rf->channel <= 14) {
info->tx_power1 = TXPOWER_G_TO_DEV(info->tx_power1); info->default_power1 = TXPOWER_G_TO_DEV(info->default_power1);
info->tx_power2 = TXPOWER_G_TO_DEV(info->tx_power2); info->default_power2 = TXPOWER_G_TO_DEV(info->default_power2);
} else { } else {
info->tx_power1 = TXPOWER_A_TO_DEV(info->tx_power1); info->default_power1 = TXPOWER_A_TO_DEV(info->default_power1);
info->tx_power2 = TXPOWER_A_TO_DEV(info->tx_power2); info->default_power2 = TXPOWER_A_TO_DEV(info->default_power2);
} }
if (rt2x00_rf(rt2x00dev, RF2020) || if (rt2x00_rf(rt2x00dev, RF2020) ||
@ -2729,6 +2729,13 @@ int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
default_lna_gain); default_lna_gain);
rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word);
rt2x00_eeprom_read(rt2x00dev, EEPROM_MAX_TX_POWER, &word);
if (rt2x00_get_field16(word, EEPROM_MAX_TX_POWER_24GHZ) == 0xff)
rt2x00_set_field16(&word, EEPROM_MAX_TX_POWER_24GHZ, MAX_G_TXPOWER);
if (rt2x00_get_field16(word, EEPROM_MAX_TX_POWER_5GHZ) == 0xff)
rt2x00_set_field16(&word, EEPROM_MAX_TX_POWER_5GHZ, MAX_A_TXPOWER);
rt2x00_eeprom_write(rt2x00dev, EEPROM_MAX_TX_POWER, word);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(rt2800_validate_eeprom); EXPORT_SYMBOL_GPL(rt2800_validate_eeprom);
@ -2968,9 +2975,10 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
{ {
struct hw_mode_spec *spec = &rt2x00dev->spec; struct hw_mode_spec *spec = &rt2x00dev->spec;
struct channel_info *info; struct channel_info *info;
char *tx_power1; char *default_power1;
char *tx_power2; char *default_power2;
unsigned int i; unsigned int i;
unsigned short max_power;
u16 eeprom; u16 eeprom;
/* /*
@ -3084,21 +3092,26 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
spec->channels_info = info; spec->channels_info = info;
tx_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1); rt2x00_eeprom_read(rt2x00dev, EEPROM_MAX_TX_POWER, &eeprom);
tx_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2); max_power = rt2x00_get_field16(eeprom, EEPROM_MAX_TX_POWER_24GHZ);
default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1);
default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2);
for (i = 0; i < 14; i++) { for (i = 0; i < 14; i++) {
info[i].tx_power1 = TXPOWER_G_FROM_DEV(tx_power1[i]); info[i].max_power = max_power;
info[i].tx_power2 = TXPOWER_G_FROM_DEV(tx_power2[i]); info[i].default_power1 = TXPOWER_G_FROM_DEV(default_power1[i]);
info[i].default_power2 = TXPOWER_G_FROM_DEV(default_power2[i]);
} }
if (spec->num_channels > 14) { if (spec->num_channels > 14) {
tx_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A1); max_power = rt2x00_get_field16(eeprom, EEPROM_MAX_TX_POWER_5GHZ);
tx_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2); default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A1);
default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2);
for (i = 14; i < spec->num_channels; i++) { for (i = 14; i < spec->num_channels; i++) {
info[i].tx_power1 = TXPOWER_A_FROM_DEV(tx_power1[i]); info[i].max_power = max_power;
info[i].tx_power2 = TXPOWER_A_FROM_DEV(tx_power2[i]); info[i].default_power1 = TXPOWER_A_FROM_DEV(default_power1[i]);
info[i].default_power2 = TXPOWER_A_FROM_DEV(default_power2[i]);
} }
} }

View File

@ -213,8 +213,9 @@ struct channel_info {
unsigned int flags; unsigned int flags;
#define GEOGRAPHY_ALLOWED 0x00000001 #define GEOGRAPHY_ALLOWED 0x00000001
short tx_power1; short max_power;
short tx_power2; short default_power1;
short default_power2;
}; };
/* /*

View File

@ -711,7 +711,7 @@ static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev,
for (i = 0; i < spec->num_channels; i++) { for (i = 0; i < spec->num_channels; i++) {
rt2x00lib_channel(&channels[i], rt2x00lib_channel(&channels[i],
spec->channels[i].channel, spec->channels[i].channel,
spec->channels_info[i].tx_power1, i); spec->channels_info[i].max_power, i);
} }
/* /*

View File

@ -2656,13 +2656,17 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
spec->channels_info = info; spec->channels_info = info;
tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START);
for (i = 0; i < 14; i++) for (i = 0; i < 14; i++) {
info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]); info[i].max_power = MAX_TXPOWER;
info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
}
if (spec->num_channels > 14) { if (spec->num_channels > 14) {
tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START);
for (i = 14; i < spec->num_channels; i++) for (i = 14; i < spec->num_channels; i++) {
info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]); info[i].max_power = MAX_TXPOWER;
info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
}
} }
return 0; return 0;

View File

@ -2090,13 +2090,17 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
spec->channels_info = info; spec->channels_info = info;
tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START);
for (i = 0; i < 14; i++) for (i = 0; i < 14; i++) {
info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]); info[i].max_power = MAX_TXPOWER;
info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
}
if (spec->num_channels > 14) { if (spec->num_channels > 14) {
tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START);
for (i = 14; i < spec->num_channels; i++) for (i = 14; i < spec->num_channels; i++) {
info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]); info[i].max_power = MAX_TXPOWER;
info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
}
} }
return 0; return 0;