📄 rt2x00dev.c
字号:
/* Copyright (C) 2004 - 2007 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 generic device routines. *//* * Set enviroment defines for rt2x00.h */#define DRV_NAME "rt2x00lib"#include <linux/kernel.h>#include <linux/module.h>#include "rt2x00.h"#include "rt2x00lib.h"/* * Ring handler. */struct data_ring *rt2x00lib_get_ring(struct rt2x00_dev *rt2x00dev, const unsigned int queue){ int beacon = test_bit(DRIVER_REQUIRE_BEACON_RING, &rt2x00dev->flags); /* * Check if we are requesting a reqular TX ring, * or if we are requesting a Beacon or Atim ring. * For Atim rings, we should check if it is supported. */ if (queue < rt2x00dev->hw->queues && rt2x00dev->tx) return &rt2x00dev->tx[queue]; if (!rt2x00dev->bcn || !beacon) return NULL; if (queue == IEEE80211_TX_QUEUE_BEACON) return &rt2x00dev->bcn[0]; else if (queue == IEEE80211_TX_QUEUE_AFTER_BEACON) return &rt2x00dev->bcn[1]; return NULL;}EXPORT_SYMBOL_GPL(rt2x00lib_get_ring);/* * Link tuning handlers */static void rt2x00lib_start_link_tuner(struct rt2x00_dev *rt2x00dev){ rt2x00_clear_link(&rt2x00dev->link); /* * Reset the link tuner. */ rt2x00dev->ops->lib->reset_tuner(rt2x00dev); queue_delayed_work(rt2x00dev->hw->workqueue, &rt2x00dev->link.work, LINK_TUNE_INTERVAL);}static void rt2x00lib_stop_link_tuner(struct rt2x00_dev *rt2x00dev){ cancel_delayed_work_sync(&rt2x00dev->link.work);}void rt2x00lib_reset_link_tuner(struct rt2x00_dev *rt2x00dev){ if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) return; rt2x00lib_stop_link_tuner(rt2x00dev); rt2x00lib_start_link_tuner(rt2x00dev);}/* * Radio control handlers. */int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev){ int status; /* * Don't enable the radio twice. * And check if the hardware button has been disabled. */ if (test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags) || test_bit(DEVICE_DISABLED_RADIO_HW, &rt2x00dev->flags)) return 0; /* * Enable radio. */ status = rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_ON); if (status) return status; __set_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags); /* * Enable RX. */ rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); /* * Start the TX queues. */ ieee80211_start_queues(rt2x00dev->hw); return 0;}void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev){ if (!__test_and_clear_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) return; /* * Stop all scheduled work. */ if (work_pending(&rt2x00dev->beacon_work)) cancel_work_sync(&rt2x00dev->beacon_work); if (work_pending(&rt2x00dev->filter_work)) cancel_work_sync(&rt2x00dev->filter_work); if (work_pending(&rt2x00dev->config_work)) cancel_work_sync(&rt2x00dev->config_work); /* * Stop the TX queues. */ ieee80211_stop_queues(rt2x00dev->hw); /* * Disable RX. */ rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); /* * Disable radio. */ rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_OFF);}void rt2x00lib_toggle_rx(struct rt2x00_dev *rt2x00dev, enum dev_state state){ /* * When we are disabling the RX, we should also stop the link tuner. */ if (state == STATE_RADIO_RX_OFF) rt2x00lib_stop_link_tuner(rt2x00dev); rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); /* * When we are enabling the RX, we should also start the link tuner. */ if (state == STATE_RADIO_RX_ON && is_interface_present(&rt2x00dev->interface)) rt2x00lib_start_link_tuner(rt2x00dev);}static void rt2x00lib_precalculate_link_signal(struct link *link){ if (link->rx_failed || link->rx_success) link->rx_percentage = (link->rx_success * 100) / (link->rx_failed + link->rx_success); else link->rx_percentage = 50; if (link->tx_failed || link->tx_success) link->tx_percentage = (link->tx_success * 100) / (link->tx_failed + link->tx_success); else link->tx_percentage = 50; link->rx_success = 0; link->rx_failed = 0; link->tx_success = 0; link->tx_failed = 0;}static int rt2x00lib_calculate_link_signal(struct rt2x00_dev *rt2x00dev, int rssi){ int rssi_percentage = 0; int signal; /* * We need a positive value for the RSSI. */ if (rssi < 0) rssi += rt2x00dev->rssi_offset; /* * Calculate the different percentages, * which will be used for the signal. */ if (rt2x00dev->rssi_offset) rssi_percentage = (rssi * 100) / rt2x00dev->rssi_offset; /* * Add the individual percentages and use the WEIGHT * defines to calculate the current link signal. */ signal = ((WEIGHT_RSSI * rssi_percentage) + (WEIGHT_TX * rt2x00dev->link.tx_percentage) + (WEIGHT_RX * rt2x00dev->link.rx_percentage)) / 100; return (signal > 100) ? 100 : signal;}static void rt2x00lib_link_tuner(struct work_struct *work){ struct rt2x00_dev *rt2x00dev = container_of(work, struct rt2x00_dev, link.work.work); /* * When the radio is shutting down we should * immediately cease all link tuning. */ if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) return; /* * Update statistics. */ rt2x00dev->ops->lib->link_stats(rt2x00dev); rt2x00dev->low_level_stats.dot11FCSErrorCount += rt2x00dev->link.rx_failed; /* * Only perform the link tuning when Link tuning * has been enabled (This could have been disabled from the EEPROM). */ if (!test_bit(CONFIG_DISABLE_LINK_TUNING, &rt2x00dev->flags)) rt2x00dev->ops->lib->link_tuner(rt2x00dev); /* * Precalculate a portion of the link signal which is * in based on the tx/rx success/failure counters. */ rt2x00lib_precalculate_link_signal(&rt2x00dev->link); /* * Increase tuner counter, and reschedule the next link tuner run. */ rt2x00dev->link.count++; queue_delayed_work(rt2x00dev->hw->workqueue, &rt2x00dev->link.work, LINK_TUNE_INTERVAL);}static void rt2x00lib_packetfilter_scheduled(struct work_struct *work){ struct rt2x00_dev *rt2x00dev = container_of(work, struct rt2x00_dev, filter_work); unsigned int filter = rt2x00dev->interface.filter; /* * Since we had stored the filter inside interface.filter, * we should now clear that field. Otherwise the driver will * assume nothing has changed (*total_flags will be compared * to interface.filter to determine if any action is required). */ rt2x00dev->interface.filter = 0; rt2x00dev->ops->hw->configure_filter(rt2x00dev->hw, filter, &filter, 0, NULL);}static void rt2x00lib_configuration_scheduled(struct work_struct *work){ struct rt2x00_dev *rt2x00dev = container_of(work, struct rt2x00_dev, config_work); int preamble = !test_bit(CONFIG_SHORT_PREAMBLE, &rt2x00dev->flags); rt2x00mac_erp_ie_changed(rt2x00dev->hw, IEEE80211_ERP_CHANGE_PREAMBLE, 0, preamble);}/* * Interrupt context handlers. */static void rt2x00lib_beacondone_scheduled(struct work_struct *work){ struct rt2x00_dev *rt2x00dev = container_of(work, struct rt2x00_dev, beacon_work); struct data_ring *ring = rt2x00lib_get_ring(rt2x00dev, IEEE80211_TX_QUEUE_BEACON); struct data_entry *entry = rt2x00_get_data_entry(ring); struct sk_buff *skb; skb = ieee80211_beacon_get(rt2x00dev->hw, rt2x00dev->interface.id, &entry->tx_status.control); if (!skb) return; rt2x00dev->ops->hw->beacon_update(rt2x00dev->hw, skb, &entry->tx_status.control); dev_kfree_skb(skb);}void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev){ if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) return; queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->beacon_work);}EXPORT_SYMBOL_GPL(rt2x00lib_beacondone);void rt2x00lib_txdone(struct data_entry *entry, const int status, const int retry){ struct rt2x00_dev *rt2x00dev = entry->ring->rt2x00dev; struct ieee80211_tx_status *tx_status = &entry->tx_status; struct ieee80211_low_level_stats *stats = &rt2x00dev->low_level_stats; int success = !!(status == TX_SUCCESS || status == TX_SUCCESS_RETRY); int fail = !!(status == TX_FAIL_RETRY || status == TX_FAIL_INVALID || status == TX_FAIL_OTHER); /* * Update TX statistics. */ tx_status->flags = 0; tx_status->ack_signal = 0; tx_status->excessive_retries = (status == TX_FAIL_RETRY); tx_status->retry_count = retry; rt2x00dev->link.tx_success += success; rt2x00dev->link.tx_failed += retry + fail; if (!(tx_status->control.flags & IEEE80211_TXCTL_NO_ACK)) { if (success) tx_status->flags |= IEEE80211_TX_STATUS_ACK; else stats->dot11ACKFailureCount++; } tx_status->queue_length = entry->ring->stats.limit; tx_status->queue_number = tx_status->control.queue; if (tx_status->control.flags & IEEE80211_TXCTL_USE_RTS_CTS) { if (success) stats->dot11RTSSuccessCount++; else stats->dot11RTSFailureCount++; } /* * Send the tx_status to mac80211, * that method also cleans up the skb structure. */ ieee80211_tx_status_irqsafe(rt2x00dev->hw, entry->skb, tx_status); entry->skb = NULL;}EXPORT_SYMBOL_GPL(rt2x00lib_txdone);void rt2x00lib_rxdone(struct data_entry *entry, struct sk_buff *skb, struct rxdata_entry_desc *desc){ struct rt2x00_dev *rt2x00dev = entry->ring->rt2x00dev; struct ieee80211_rx_status *rx_status = &rt2x00dev->rx_status; struct ieee80211_hw_mode *mode; struct ieee80211_rate *rate; unsigned int i; int val = 0; /* * Update RX statistics. */ mode = &rt2x00dev->hwmodes[rt2x00dev->curr_hwmode]; for (i = 0; i < mode->num_rates; i++) { rate = &mode->rates[i]; /* * When frame was received with an OFDM bitrate, * the signal is the PLCP value. If it was received with * a CCK bitrate the signal is the rate in 0.5kbit/s. */ if (!desc->ofdm) val = DEVICE_GET_RATE_FIELD(rate->val, RATE); else val = DEVICE_GET_RATE_FIELD(rate->val, PLCP); if (val == desc->signal) { val = rate->val; break; } } rt2x00_update_link_rssi(&rt2x00dev->link, desc->rssi); rt2x00dev->link.rx_success++; rx_status->rate = val; rx_status->signal = rt2x00lib_calculate_link_signal(rt2x00dev, desc->rssi); rx_status->ssi = desc->rssi; rx_status->flag = desc->flags; /* * Send frame to mac80211 */ ieee80211_rx_irqsafe(rt2x00dev->hw, skb, rx_status);}EXPORT_SYMBOL_GPL(rt2x00lib_rxdone);/* * TX descriptor initializer */void rt2x00lib_write_tx_desc(struct rt2x00_dev *rt2x00dev, struct data_desc *txd, struct ieee80211_hdr *ieee80211hdr, unsigned int length, struct ieee80211_tx_control *control){ struct txdata_entry_desc desc; struct data_ring *ring; int tx_rate; int bitrate; int duration; int residual; u16 frame_control; u16 seq_ctrl; /* * Make sure the descriptor is properly cleared. */ memset(&desc, 0x00, sizeof(desc)); /* * Get ring pointer, if we fail to obtain the * correct ring, then use the first TX ring. */ ring = rt2x00lib_get_ring(rt2x00dev, control->queue); if (!ring) ring = rt2x00lib_get_ring(rt2x00dev, IEEE80211_TX_QUEUE_DATA0); desc.cw_min = ring->tx_params.cw_min; desc.cw_max = ring->tx_params.cw_max; desc.aifs = ring->tx_params.aifs; /* * Identify queue */ if (control->queue < rt2x00dev->hw->queues) desc.queue = control->queue; else if (control->queue == IEEE80211_TX_QUEUE_BEACON || control->queue == IEEE80211_TX_QUEUE_AFTER_BEACON) desc.queue = QUEUE_MGMT; else desc.queue = QUEUE_OTHER; /* * Read required fields from ieee80211 header. */ frame_control = le16_to_cpu(ieee80211hdr->frame_control); seq_ctrl = le16_to_cpu(ieee80211hdr->seq_ctrl); tx_rate = control->tx_rate; /* * Check if this is a RTS/CTS frame */ if (is_rts_frame(frame_control) || is_cts_frame(frame_control)) { __set_bit(ENTRY_TXD_BURST, &desc.flags); if (is_rts_frame(frame_control)) __set_bit(ENTRY_TXD_RTS_FRAME, &desc.flags); if (control->rts_cts_rate) tx_rate = control->rts_cts_rate; } /* * Check for OFDM */ if (DEVICE_GET_RATE_FIELD(tx_rate, RATEMASK) & DEV_OFDM_RATEMASK) __set_bit(ENTRY_TXD_OFDM_RATE, &desc.flags); /* * Check if more fragments are pending */ if (ieee80211_get_morefrag(ieee80211hdr)) { __set_bit(ENTRY_TXD_BURST, &desc.flags); __set_bit(ENTRY_TXD_MORE_FRAG, &desc.flags); } /* * Beacons and probe responses require the tsf timestamp * to be inserted into the frame. */ if (control->queue == IEEE80211_TX_QUEUE_BEACON || is_probe_resp(frame_control)) __set_bit(ENTRY_TXD_REQ_TIMESTAMP, &desc.flags); /* * Determine with what IFS priority this frame should be send. * Set ifs to IFS_SIFS when the this is not the first fragment, * or this fragment came after RTS/CTS. */ if ((seq_ctrl & IEEE80211_SCTL_FRAG) > 0 || test_bit(ENTRY_TXD_RTS_FRAME, &desc.flags)) desc.ifs = IFS_SIFS; else desc.ifs = IFS_BACKOFF; /* * PLCP setup * Length calculation depends on OFDM/CCK rate. */ desc.signal = DEVICE_GET_RATE_FIELD(tx_rate, PLCP); desc.service = 0x04; if (test_bit(ENTRY_TXD_OFDM_RATE, &desc.flags)) { desc.length_high = ((length + FCS_LEN) >> 6) & 0x3f; desc.length_low = ((length + FCS_LEN) & 0x3f); } else { bitrate = DEVICE_GET_RATE_FIELD(tx_rate, RATE); /* * Convert length to microseconds. */ residual = get_duration_res(length + FCS_LEN, bitrate); duration = get_duration(length + FCS_LEN, bitrate); if (residual != 0) { duration++; /* * Check if we need to set the Length Extension */ if (bitrate == 110 && residual <= 30) desc.service |= 0x80; } desc.length_high = (duration >> 8) & 0xff; desc.length_low = duration & 0xff; /* * When preamble is enabled we should set the * preamble bit for the signal. */ if (DEVICE_GET_RATE_FIELD(tx_rate, PREAMBLE)) desc.signal |= 0x08; } rt2x00dev->ops->lib->write_tx_desc(rt2x00dev, txd, &desc, ieee80211hdr, length, control);}EXPORT_SYMBOL_GPL(rt2x00lib_write_tx_desc);/* * Driver initialization handlers. */static void rt2x00lib_channel(struct ieee80211_channel *entry, const int channel, const int tx_power, const int value){ entry->chan = channel; if (channel <= 14) entry->freq = 2407 + (5 * channel); else entry->freq = 5000 + (5 * channel); entry->val = value; entry->flag = IEEE80211_CHAN_W_IBSS | IEEE80211_CHAN_W_ACTIVE_SCAN | IEEE80211_CHAN_W_SCAN; entry->power_level = tx_power; entry->antenna_max = 0xff;}static void rt2x00lib_rate(struct ieee80211_rate *entry, const int rate, const int mask, const int plcp, const int flags){ entry->rate = rate; entry->val = DEVICE_SET_RATE_FIELD(rate, RATE) | DEVICE_SET_RATE_FIELD(mask, RATEMASK) | DEVICE_SET_RATE_FIELD(plcp, PLCP);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -