Skip to content

Commit

Permalink
b43: Rewrite TX power adjustment
Browse files Browse the repository at this point in the history
This patch rewrites the TX power recalculation algorithms to scale better
with changed enviromnent. If there's low
TX traffic, the power will be checked against the desired values
every 60 seconds.
If there is high TX traffic, the check is redone every 2 seconds. This improves
the reaction times a lot and confuses the rate control less.
It will also reduce the time it initially takes to tune to a new TX power
value. With the old algorithm it could take about 30 to 45 seconds to settle to
a new power value. This will happen in about two to four seconds now.

Signed-off-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Michael Buesch authored and John W. Linville committed Aug 29, 2008
1 parent ef1a628 commit 18c8ade
Show file tree
Hide file tree
Showing 9 changed files with 341 additions and 121 deletions.
10 changes: 10 additions & 0 deletions drivers/net/wireless/b43/b43.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ enum {
#define B43_SHM_SH_CHAN 0x00A0 /* Current channel (low 8bit only) */
#define B43_SHM_SH_CHAN_5GHZ 0x0100 /* Bit set, if 5Ghz channel */
#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 */
Expand Down Expand Up @@ -648,6 +653,11 @@ struct b43_wl {
struct b43_qos_params qos_params[4];
/* Workqueue for updating QOS parameters in hardware. */
struct work_struct qos_update_work;

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

/* In-memory representation of a cached microcode file. */
Expand Down
18 changes: 13 additions & 5 deletions drivers/net/wireless/b43/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2805,6 +2805,9 @@ static void b43_periodic_every60sec(struct b43_wldev *dev)

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)
Expand Down Expand Up @@ -2835,8 +2838,6 @@ static void b43_periodic_every15sec(struct b43_wldev *dev)
if (phy->ops->pwork_15sec)
phy->ops->pwork_15sec(dev);

phy->ops->xmitpower(dev);

atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT);
wmb();
}
Expand Down Expand Up @@ -3382,10 +3383,13 @@ static int b43_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)

/* Adjust the desired TX power level. */
if (conf->power_level != 0) {
if (conf->power_level != phy->power_level) {
phy->power_level = conf->power_level;
phy->ops->xmitpower(dev);
spin_lock_irqsave(&wl->irq_lock, flags);
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);
}
spin_unlock_irqrestore(&wl->irq_lock, flags);
}

/* Antennas for RX and management frame TX. */
Expand Down Expand Up @@ -3785,6 +3789,7 @@ 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);
}
Expand Down Expand Up @@ -4204,6 +4209,8 @@ static void b43_op_stop(struct ieee80211_hw *hw)
b43_wireless_core_stop(dev);
b43_wireless_core_exit(dev);
mutex_unlock(&wl->mutex);

cancel_work_sync(&(wl->txpower_adjust_work));
}

static int b43_op_set_retry_limit(struct ieee80211_hw *hw,
Expand Down Expand Up @@ -4581,6 +4588,7 @@ static int b43_wireless_init(struct ssb_device *dev)
INIT_LIST_HEAD(&wl->devlist);
INIT_WORK(&wl->qos_update_work, b43_qos_update_work);
INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work);
INIT_WORK(&wl->txpower_adjust_work, b43_phy_txpower_adjust_work);

ssb_set_devtypedata(dev, wl);
b43info(wl, "Broadcom %04X WLAN found\n", dev->bus->chip_id);
Expand Down
15 changes: 9 additions & 6 deletions drivers/net/wireless/b43/nphy.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,16 @@ void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna)
{//TODO
}

void b43_nphy_xmitpower(struct b43_wldev *dev)
static void b43_nphy_op_adjust_txpower(struct b43_wldev *dev)
{//TODO
}

static enum b43_txpwr_result b43_nphy_op_recalc_txpower(struct b43_wldev *dev,
bool ignore_tssi)
{//TODO
return B43_TXPWR_RES_DONE;
}

static void b43_chantab_radio_upload(struct b43_wldev *dev,
const struct b43_nphy_channeltab_entry *e)
{
Expand Down Expand Up @@ -602,10 +608,6 @@ static unsigned int b43_nphy_op_get_default_chan(struct b43_wldev *dev)
return 36;
}

static void b43_nphy_op_xmitpower(struct b43_wldev *dev)
{//TODO
}

const struct b43_phy_operations b43_phyops_n = {
.allocate = b43_nphy_op_allocate,
.init = b43_nphy_op_init,
Expand All @@ -617,5 +619,6 @@ const struct b43_phy_operations b43_phyops_n = {
.software_rfkill = b43_nphy_op_software_rfkill,
.switch_channel = b43_nphy_op_switch_channel,
.get_default_chan = b43_nphy_op_get_default_chan,
.xmitpower = b43_nphy_op_xmitpower,
.recalc_txpower = b43_nphy_op_recalc_txpower,
.adjust_txpower = b43_nphy_op_adjust_txpower,
};
11 changes: 9 additions & 2 deletions drivers/net/wireless/b43/phy_a.c
Original file line number Diff line number Diff line change
Expand Up @@ -505,10 +505,16 @@ static void b43_aphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna)
b43_hf_write(dev, hf);
}

static void b43_aphy_op_xmitpower(struct b43_wldev *dev)
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
}
Expand All @@ -530,7 +536,8 @@ const struct b43_phy_operations b43_phyops_a = {
.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,
.xmitpower = b43_aphy_op_xmitpower,
.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,
};
91 changes: 91 additions & 0 deletions drivers/net/wireless/b43/phy_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,94 @@ void b43_software_rfkill(struct b43_wldev *dev, enum rfkill_state state)
phy->ops->software_rfkill(dev, state);
phy->radio_on = (state == RFKILL_STATE_UNBLOCKED);
}

/**
* 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);
}

/* Called with wl->irq_lock locked */
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->bus->boardinfo.vendor == SSB_BOARDVENDOR_BCM) &&
(dev->dev->bus->boardinfo.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(). */
queue_work(dev->wl->hw->workqueue, &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_HOSTFLO)
& B43_HF_CCKBOOST)
average = (average >= 13) ? (average - 13) : 0;
}

return average;
}
75 changes: 72 additions & 3 deletions drivers/net/wireless/b43/phy_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ enum {
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.
*
Expand Down Expand Up @@ -96,8 +107,23 @@ enum {
* @interf_mitigation: Switch the Interference Mitigation mode.
* Can be NULL, if not supported.
*
* @xmitpower: FIXME REMOVEME
* @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 callback is called with wl->irq_lock held and must not sleep.
* 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.
Expand Down Expand Up @@ -127,7 +153,9 @@ struct b43_phy_operations {
enum b43_interference_mitigation new_mode);

/* Transmission power adjustment */
void (*xmitpower)(struct b43_wldev *dev);
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);
Expand Down Expand Up @@ -183,11 +211,15 @@ struct b43_phy {

/* Desired TX power level (in dBm).
* This is set by the user and adjusted in b43_phy_xmitpower(). */
u8 power_level;
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 */
unsigned int channel;

Expand Down Expand Up @@ -309,4 +341,41 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel);
*/
void b43_software_rfkill(struct b43_wldev *dev, enum rfkill_state state);

/**
* 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.
* Requires wl->irq_lock locked.
*
* @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);


#endif /* LINUX_B43_PHY_COMMON_H_ */
Loading

0 comments on commit 18c8ade

Please sign in to comment.