diff options
Diffstat (limited to 'package/rt2x00/src/rt2x00mac.c')
-rw-r--r-- | package/rt2x00/src/rt2x00mac.c | 377 |
1 files changed, 208 insertions, 169 deletions
diff --git a/package/rt2x00/src/rt2x00mac.c b/package/rt2x00/src/rt2x00mac.c index 8835df2e2e..85ea8a8e65 100644 --- a/package/rt2x00/src/rt2x00mac.c +++ b/package/rt2x00/src/rt2x00mac.c @@ -19,10 +19,8 @@ */ /* - Module: rt2x00lib + Module: rt2x00mac Abstract: rt2x00 generic mac80211 routines. - Supported chipsets: RT2460, RT2560, RT2570, - rt2561, rt2561s, rt2661, rt2571W & rt2671. */ /* @@ -30,15 +28,16 @@ */ #define DRV_NAME "rt2x00lib" -#include <linux/netdevice.h> +#include <linux/kernel.h> +#include <linux/module.h> #include "rt2x00.h" #include "rt2x00lib.h" -#include "rt2x00dev.h" -static int rt2x00_tx_rts_cts(struct rt2x00_dev *rt2x00dev, - struct data_ring *ring, struct sk_buff *frag_skb, - struct ieee80211_tx_control *control) +static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, + struct data_ring *ring, + struct sk_buff *frag_skb, + struct ieee80211_tx_control *control) { struct sk_buff *skb; int size; @@ -58,13 +57,13 @@ static int rt2x00_tx_rts_cts(struct rt2x00_dev *rt2x00dev, skb_put(skb, size); if (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) - ieee80211_ctstoself_get(rt2x00dev->hw, - frag_skb->data, frag_skb->len, control, - (struct ieee80211_cts*)(skb->data)); + ieee80211_ctstoself_get(rt2x00dev->hw, rt2x00dev->interface.id, + frag_skb->data, frag_skb->len, control, + (struct ieee80211_cts *)(skb->data)); else - ieee80211_rts_get(rt2x00dev->hw, - frag_skb->data, frag_skb->len, control, - (struct ieee80211_rts*)(skb->data)); + ieee80211_rts_get(rt2x00dev->hw, rt2x00dev->interface.id, + frag_skb->data, frag_skb->len, control, + (struct ieee80211_rts *)(skb->data)); if (rt2x00dev->ops->lib->write_tx_data(rt2x00dev, ring, skb, control)) { WARNING(rt2x00dev, "Failed to send RTS/CTS frame.\n"); @@ -74,23 +73,34 @@ static int rt2x00_tx_rts_cts(struct rt2x00_dev *rt2x00dev, return NETDEV_TX_OK; } -int rt2x00lib_tx(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_tx_control *control) +int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *control) { struct rt2x00_dev *rt2x00dev = hw->priv; - struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr*)skb->data; + struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data; struct data_ring *ring; u16 frame_control; /* + * Mac80211 might be calling this function while we are trying + * to remove the device or perhaps suspending it. + * Note that we can only stop the TX queues inside the TX path + * due to possible race conditions in mac80211. + */ + if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) { + ieee80211_stop_queues(hw); + return 0; + } + + /* * Determine which ring to put packet on. */ - ring = rt2x00_get_ring(rt2x00dev, control->queue); + ring = rt2x00lib_get_ring(rt2x00dev, control->queue); if (unlikely(!ring)) { ERROR(rt2x00dev, - "Attempt to send packet over invalid queue %d.\n" - "Please file bug report to %s.\n", - control->queue, DRV_PROJECT); + "Attempt to send packet over invalid queue %d.\n" + "Please file bug report to %s.\n", + control->queue, DRV_PROJECT); dev_kfree_skb_any(skb); return NETDEV_TX_OK; } @@ -102,12 +112,13 @@ int rt2x00lib_tx(struct ieee80211_hw *hw, struct sk_buff *skb, * frame as well as the data frame. */ frame_control = le16_to_cpu(ieee80211hdr->frame_control); - if (control->flags & IEEE80211_TXCTL_USE_RTS_CTS && - !is_cts_frame(frame_control) && !is_rts_frame(frame_control)) { + if (!is_rts_frame(frame_control) && !is_cts_frame(frame_control) && + (control->flags & (IEEE80211_TXCTL_USE_RTS_CTS | + IEEE80211_TXCTL_USE_CTS_PROTECT))) { if (rt2x00_ring_free(ring) <= 1) return NETDEV_TX_BUSY; - if (rt2x00_tx_rts_cts(rt2x00dev, ring, skb, control)) + if (rt2x00mac_tx_rts_cts(rt2x00dev, ring, skb, control)) return NETDEV_TX_BUSY; } @@ -119,131 +130,149 @@ int rt2x00lib_tx(struct ieee80211_hw *hw, struct sk_buff *skb, return NETDEV_TX_OK; } -EXPORT_SYMBOL_GPL(rt2x00lib_tx); +EXPORT_SYMBOL_GPL(rt2x00mac_tx); -int rt2x00lib_reset(struct ieee80211_hw *hw) +int rt2x00mac_start(struct ieee80211_hw *hw) { struct rt2x00_dev *rt2x00dev = hw->priv; + int status; + + if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags) || + test_bit(DEVICE_STARTED, &rt2x00dev->flags)) + return 0; + + /* + * If this is the first interface which is added, + * we should load the firmware now. + */ + if (test_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags)) { + status = rt2x00lib_load_firmware(rt2x00dev); + if (status) + return status; + } + + /* + * Initialize the device. + */ + status = rt2x00lib_initialize(rt2x00dev); + if (status) + return status; + + /* + * Enable radio. + */ + status = rt2x00lib_enable_radio(rt2x00dev); + if (status) { + rt2x00lib_uninitialize(rt2x00dev); + return status; + } + + __set_bit(DEVICE_STARTED, &rt2x00dev->flags); + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00mac_start); + +void rt2x00mac_stop(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) + return; + + /* + * Perhaps we can add something smarter here, + * but for now just disabling the radio should do. + */ rt2x00lib_disable_radio(rt2x00dev); - return rt2x00lib_enable_radio(rt2x00dev); + + __clear_bit(DEVICE_STARTED, &rt2x00dev->flags); } -EXPORT_SYMBOL_GPL(rt2x00lib_reset); +EXPORT_SYMBOL_GPL(rt2x00mac_stop); -int rt2x00lib_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) +int rt2x00mac_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) { struct rt2x00_dev *rt2x00dev = hw->priv; struct interface *intf = &rt2x00dev->interface; - int status; + + /* FIXME: Beaconing is broken in rt2x00. */ + if (conf->type == IEEE80211_IF_TYPE_IBSS || + conf->type == IEEE80211_IF_TYPE_AP) { + ERROR(rt2x00dev, + "rt2x00 does not support Adhoc or Master mode"); + return -EOPNOTSUPP; + } /* - * We only support 1 non-monitor interface. + * Don't allow interfaces to be added while + * either the device has disappeared or when + * another interface is already present. */ - if (conf->type != IEEE80211_IF_TYPE_MNTR && + if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags) || is_interface_present(intf)) return -ENOBUFS; - /* - * We support muliple monitor mode interfaces. - * All we need to do is increase the monitor_count. - */ - if (conf->type == IEEE80211_IF_TYPE_MNTR) { - intf->monitor_count++; - } else { - intf->id = conf->if_id; - intf->type = conf->type; - if (conf->type == IEEE80211_IF_TYPE_AP) - memcpy(&intf->bssid, conf->mac_addr, ETH_ALEN); - intf->promisc = 0; - } + intf->id = conf->if_id; + intf->type = conf->type; + if (conf->type == IEEE80211_IF_TYPE_AP) + memcpy(&intf->bssid, conf->mac_addr, ETH_ALEN); + memcpy(&intf->mac, conf->mac_addr, ETH_ALEN); /* - * Initialize interface, and enable the radio when this - * is the first interface that is brought up. + * The MAC adddress must be configured after the device + * has been initialized. Otherwise the device can reset + * the MAC registers. */ - if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) { - /* - * We must wait on the firmware before - * we can safely continue. - */ - status = rt2x00lib_load_firmware_wait(rt2x00dev); - if (status) - return status; - - /* - * Before initialization, the mac address should - * be configured. - */ - rt2x00dev->ops->lib->config_mac_addr(rt2x00dev, - conf->mac_addr); - - /* - * Initialize the device. - */ - status = rt2x00lib_initialize(rt2x00dev); - if (status) - return status; - - /* - * Enable radio. - */ - status = rt2x00lib_enable_radio(rt2x00dev); - if (status) - return status; - } + rt2x00lib_config_mac_addr(rt2x00dev, intf->mac); + rt2x00lib_config_type(rt2x00dev, conf->type); return 0; } -EXPORT_SYMBOL_GPL(rt2x00lib_add_interface); +EXPORT_SYMBOL_GPL(rt2x00mac_add_interface); -void rt2x00lib_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) +void rt2x00mac_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) { struct rt2x00_dev *rt2x00dev = hw->priv; struct interface *intf = &rt2x00dev->interface; /* - * We only support 1 non-monitor interface. + * Don't allow interfaces to be remove while + * either the device has disappeared or when + * no interface is present. */ - if (conf->type != IEEE80211_IF_TYPE_MNTR && + if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags) || !is_interface_present(intf)) return; - /* - * When removing an monitor interface, decrease monitor_count. - * For non-monitor interfaces, all interface data needs to be reset. - */ - if (conf->type == IEEE80211_IF_TYPE_MNTR) { - intf->monitor_count--; - } else if (intf->type == conf->type) { - intf->id = 0; - intf->type = -EINVAL; - memset(&intf->bssid, 0x00, ETH_ALEN); - intf->promisc = 0; - } + intf->id = 0; + intf->type = INVALID_INTERFACE; + memset(&intf->bssid, 0x00, ETH_ALEN); + memset(&intf->mac, 0x00, ETH_ALEN); /* - * If this was the last interface, - * this is the time to disable the radio. - * If this is not the last interface, then we should - * check if we should switch completely to monitor - * mode or completely switch to the non-monitor mode. + * Make sure the bssid and mac address registers + * are cleared to prevent false ACKing of frames. */ - if (!is_monitor_present(intf) && !is_interface_present(intf)) - rt2x00lib_disable_radio(rt2x00dev); - else if (is_monitor_present(intf) ^ is_interface_present(intf)) - rt2x00lib_config_type(rt2x00dev, - is_interface_present(intf) ? - intf->type : IEEE80211_IF_TYPE_MNTR); + rt2x00lib_config_mac_addr(rt2x00dev, intf->mac); + rt2x00lib_config_bssid(rt2x00dev, intf->bssid); + rt2x00lib_config_type(rt2x00dev, intf->type); } -EXPORT_SYMBOL_GPL(rt2x00lib_remove_interface); +EXPORT_SYMBOL_GPL(rt2x00mac_remove_interface); -int rt2x00lib_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) +int rt2x00mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) { struct rt2x00_dev *rt2x00dev = hw->priv; /* + * Mac80211 might be calling this function while we are trying + * to remove the device or perhaps suspending it. + */ + if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) + return 0; + + /* * Check if we need to disable the radio, * if this is not the case, at least the RX must be disabled. */ @@ -251,46 +280,42 @@ int rt2x00lib_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) if (!conf->radio_enabled) rt2x00lib_disable_radio(rt2x00dev); else - rt2x00lib_toggle_rx(rt2x00dev, 0); + rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); } - rt2x00lib_config_phymode(rt2x00dev, conf->phymode); - rt2x00lib_config_channel(rt2x00dev, conf->channel_val, - conf->channel, conf->freq, conf->power_level); - rt2x00lib_config_txpower(rt2x00dev, conf->power_level); - rt2x00lib_config_antenna(rt2x00dev, - conf->antenna_sel_tx, conf->antenna_sel_rx); - rt2x00dev->ops->lib->config_duration(rt2x00dev, - (conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME), - conf->beacon_int); + rt2x00lib_config(rt2x00dev, conf, 0); /* * Reenable RX only if the radio should be on. */ if (test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2x00lib_toggle_rx(rt2x00dev, 1); + rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); else if (conf->radio_enabled) return rt2x00lib_enable_radio(rt2x00dev); return 0; } -EXPORT_SYMBOL_GPL(rt2x00lib_config); +EXPORT_SYMBOL_GPL(rt2x00mac_config); -int rt2x00lib_config_interface(struct ieee80211_hw *hw, int if_id, - struct ieee80211_if_conf *conf) +int rt2x00mac_config_interface(struct ieee80211_hw *hw, int if_id, + struct ieee80211_if_conf *conf) { struct rt2x00_dev *rt2x00dev = hw->priv; struct interface *intf = &rt2x00dev->interface; int status; /* - * Monitor mode does not need configuring. + * Mac80211 might be calling this function while we are trying + * to remove the device or perhaps suspending it. + */ + if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) + return 0; + + /* * If the given type does not match the configured type, * there has been a problem. */ - if (conf->type == IEEE80211_IF_TYPE_MNTR) - return 0; - else if (conf->type != intf->type) + if (conf->type != intf->type) return -EINVAL; /* @@ -300,14 +325,7 @@ int rt2x00lib_config_interface(struct ieee80211_hw *hw, int if_id, */ if (conf->type != IEEE80211_IF_TYPE_AP) memcpy(&intf->bssid, conf->bssid, ETH_ALEN); - - /* - * Enable configuration. - * For Monitor mode, promisc mode will be forced on. - */ - rt2x00lib_config_type(rt2x00dev, conf->type); - rt2x00lib_config_promisc(rt2x00dev, rt2x00dev->interface.promisc); - rt2x00dev->ops->lib->config_bssid(rt2x00dev, intf->bssid); + rt2x00lib_config_bssid(rt2x00dev, intf->bssid); /* * We only need to initialize the beacon when master mode is enabled. @@ -316,65 +334,86 @@ int rt2x00lib_config_interface(struct ieee80211_hw *hw, int if_id, return 0; status = rt2x00dev->ops->hw->beacon_update(rt2x00dev->hw, - conf->beacon, conf->beacon_control); + conf->beacon, + conf->beacon_control); if (status) dev_kfree_skb(conf->beacon); return status; } -EXPORT_SYMBOL_GPL(rt2x00lib_config_interface); +EXPORT_SYMBOL_GPL(rt2x00mac_config_interface); -void rt2x00lib_set_multicast_list(struct ieee80211_hw *hw, - unsigned short flags, int mc_count) +int rt2x00mac_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) { struct rt2x00_dev *rt2x00dev = hw->priv; /* - * Promisc mode is forced on for Monitor interfaces. + * The dot11ACKFailureCount, dot11RTSFailureCount and + * dot11RTSSuccessCount are updated in interrupt time. + * dot11FCSErrorCount is updated in the link tuner. */ - if (is_monitor_present(&rt2x00dev->interface)) - return; + memcpy(stats, &rt2x00dev->low_level_stats, sizeof(*stats)); - /* - * Check if the new state is different then the old state. - */ - if (test_bit(INTERFACE_ENABLED_PROMISC, &rt2x00dev->flags) == - !!(flags & IFF_PROMISC)) - return; - - rt2x00dev->interface.promisc = !!(flags & IFF_PROMISC); - - /* - * Schedule the link tuner if this does not run - * automatically. The link tuner will be automatically - * switched off when it is not required. - */ - if (!work_pending(&rt2x00dev->link.work.work)) - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->link.work.work); + return 0; } -EXPORT_SYMBOL_GPL(rt2x00lib_set_multicast_list); +EXPORT_SYMBOL_GPL(rt2x00mac_get_stats); -int rt2x00lib_get_tx_stats(struct ieee80211_hw *hw, - struct ieee80211_tx_queue_stats *stats) +int rt2x00mac_get_tx_stats(struct ieee80211_hw *hw, + struct ieee80211_tx_queue_stats *stats) { struct rt2x00_dev *rt2x00dev = hw->priv; unsigned int i; for (i = 0; i < hw->queues; i++) memcpy(&stats->data[i], &rt2x00dev->tx[i].stats, - sizeof(rt2x00dev->tx[i].stats)); + sizeof(rt2x00dev->tx[i].stats)); return 0; } -EXPORT_SYMBOL_GPL(rt2x00lib_get_tx_stats); +EXPORT_SYMBOL_GPL(rt2x00mac_get_tx_stats); + +void rt2x00mac_erp_ie_changed(struct ieee80211_hw *hw, u8 changes, + int cts_protection, int preamble) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + int short_preamble; + int ack_timeout; + int ack_consume_time; + int difs; + + /* + * We only support changing preamble mode. + */ + if (!(changes & IEEE80211_ERP_CHANGE_PREAMBLE)) + return; + + short_preamble = !preamble; + preamble = !!(preamble) ? PREAMBLE : SHORT_PREAMBLE; + + difs = (hw->conf.flags & IEEE80211_CONF_SHORT_SLOT_TIME) ? + SHORT_DIFS : DIFS; + ack_timeout = difs + PLCP + preamble + get_duration(ACK_SIZE, 10); + + ack_consume_time = SIFS + PLCP + preamble + get_duration(ACK_SIZE, 10); + + if (short_preamble) + __set_bit(CONFIG_SHORT_PREAMBLE, &rt2x00dev->flags); + else + __clear_bit(CONFIG_SHORT_PREAMBLE, &rt2x00dev->flags); + + rt2x00dev->ops->lib->config_preamble(rt2x00dev, short_preamble, + ack_timeout, ack_consume_time); +} +EXPORT_SYMBOL_GPL(rt2x00mac_erp_ie_changed); -int rt2x00lib_conf_tx(struct ieee80211_hw *hw, int queue, - const struct ieee80211_tx_queue_params *params) +int rt2x00mac_conf_tx(struct ieee80211_hw *hw, int queue, + const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; struct data_ring *ring; - ring = rt2x00_get_ring(rt2x00dev, queue); + ring = rt2x00lib_get_ring(rt2x00dev, queue); if (unlikely(!ring)) return -EINVAL; @@ -398,10 +437,10 @@ int rt2x00lib_conf_tx(struct ieee80211_hw *hw, int queue, ring->tx_params.aifs = 2; INFO(rt2x00dev, - "Configured TX ring %d - CWmin: %d, CWmax: %d, Aifs: %d.\n", - queue, ring->tx_params.cw_min, ring->tx_params.cw_max, - ring->tx_params.aifs); + "Configured TX ring %d - CWmin: %d, CWmax: %d, Aifs: %d.\n", + queue, ring->tx_params.cw_min, ring->tx_params.cw_max, + ring->tx_params.aifs); return 0; } -EXPORT_SYMBOL_GPL(rt2x00lib_conf_tx); +EXPORT_SYMBOL_GPL(rt2x00mac_conf_tx); |