| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | Copyright (C) 2004 - 2007 rt2x00 SourceForge Project | 
|  | 3 | <http://rt2x00.serialmonkey.com> | 
|  | 4 |  | 
|  | 5 | This program is free software; you can redistribute it and/or modify | 
|  | 6 | it under the terms of the GNU General Public License as published by | 
|  | 7 | the Free Software Foundation; either version 2 of the License, or | 
|  | 8 | (at your option) any later version. | 
|  | 9 |  | 
|  | 10 | This program is distributed in the hope that it will be useful, | 
|  | 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
|  | 13 | GNU General Public License for more details. | 
|  | 14 |  | 
|  | 15 | You should have received a copy of the GNU General Public License | 
|  | 16 | along with this program; if not, write to the | 
|  | 17 | Free Software Foundation, Inc., | 
|  | 18 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 
|  | 19 | */ | 
|  | 20 |  | 
|  | 21 | /* | 
|  | 22 | Module: rt2x00mac | 
|  | 23 | Abstract: rt2x00 generic mac80211 routines. | 
|  | 24 | */ | 
|  | 25 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 26 | #include <linux/kernel.h> | 
|  | 27 | #include <linux/module.h> | 
|  | 28 |  | 
|  | 29 | #include "rt2x00.h" | 
|  | 30 | #include "rt2x00lib.h" | 
|  | 31 |  | 
|  | 32 | static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, | 
|  | 33 | struct data_ring *ring, | 
|  | 34 | struct sk_buff *frag_skb, | 
|  | 35 | struct ieee80211_tx_control *control) | 
|  | 36 | { | 
|  | 37 | struct sk_buff *skb; | 
|  | 38 | int size; | 
|  | 39 |  | 
|  | 40 | if (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) | 
|  | 41 | size = sizeof(struct ieee80211_cts); | 
|  | 42 | else | 
|  | 43 | size = sizeof(struct ieee80211_rts); | 
|  | 44 |  | 
|  | 45 | skb = dev_alloc_skb(size + rt2x00dev->hw->extra_tx_headroom); | 
|  | 46 | if (!skb) { | 
|  | 47 | WARNING(rt2x00dev, "Failed to create RTS/CTS frame.\n"); | 
|  | 48 | return NETDEV_TX_BUSY; | 
|  | 49 | } | 
|  | 50 |  | 
|  | 51 | skb_reserve(skb, rt2x00dev->hw->extra_tx_headroom); | 
|  | 52 | skb_put(skb, size); | 
|  | 53 |  | 
|  | 54 | if (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) | 
|  | 55 | ieee80211_ctstoself_get(rt2x00dev->hw, rt2x00dev->interface.id, | 
|  | 56 | frag_skb->data, frag_skb->len, control, | 
|  | 57 | (struct ieee80211_cts *)(skb->data)); | 
|  | 58 | else | 
|  | 59 | ieee80211_rts_get(rt2x00dev->hw, rt2x00dev->interface.id, | 
|  | 60 | frag_skb->data, frag_skb->len, control, | 
|  | 61 | (struct ieee80211_rts *)(skb->data)); | 
|  | 62 |  | 
|  | 63 | if (rt2x00dev->ops->lib->write_tx_data(rt2x00dev, ring, skb, control)) { | 
|  | 64 | WARNING(rt2x00dev, "Failed to send RTS/CTS frame.\n"); | 
|  | 65 | return NETDEV_TX_BUSY; | 
|  | 66 | } | 
|  | 67 |  | 
|  | 68 | return NETDEV_TX_OK; | 
|  | 69 | } | 
|  | 70 |  | 
|  | 71 | int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb, | 
|  | 72 | struct ieee80211_tx_control *control) | 
|  | 73 | { | 
|  | 74 | struct rt2x00_dev *rt2x00dev = hw->priv; | 
|  | 75 | struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data; | 
|  | 76 | struct data_ring *ring; | 
|  | 77 | u16 frame_control; | 
|  | 78 |  | 
|  | 79 | /* | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 80 | * Mac80211 might be calling this function while we are trying | 
|  | 81 | * to remove the device or perhaps suspending it. | 
|  | 82 | * Note that we can only stop the TX queues inside the TX path | 
|  | 83 | * due to possible race conditions in mac80211. | 
|  | 84 | */ | 
|  | 85 | if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) { | 
|  | 86 | ieee80211_stop_queues(hw); | 
| Ivo van Doorn | 1230cb8 | 2008-01-06 23:38:34 +0100 | [diff] [blame] | 87 | return NETDEV_TX_OK; | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 88 | } | 
|  | 89 |  | 
|  | 90 | /* | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 91 | * Determine which ring to put packet on. | 
|  | 92 | */ | 
|  | 93 | ring = rt2x00lib_get_ring(rt2x00dev, control->queue); | 
|  | 94 | if (unlikely(!ring)) { | 
|  | 95 | ERROR(rt2x00dev, | 
|  | 96 | "Attempt to send packet over invalid queue %d.\n" | 
|  | 97 | "Please file bug report to %s.\n", | 
|  | 98 | control->queue, DRV_PROJECT); | 
|  | 99 | dev_kfree_skb_any(skb); | 
|  | 100 | return NETDEV_TX_OK; | 
|  | 101 | } | 
|  | 102 |  | 
|  | 103 | /* | 
|  | 104 | * If CTS/RTS is required. and this frame is not CTS or RTS, | 
|  | 105 | * create and queue that frame first. But make sure we have | 
|  | 106 | * at least enough entries available to send this CTS/RTS | 
|  | 107 | * frame as well as the data frame. | 
|  | 108 | */ | 
|  | 109 | frame_control = le16_to_cpu(ieee80211hdr->frame_control); | 
|  | 110 | if (!is_rts_frame(frame_control) && !is_cts_frame(frame_control) && | 
|  | 111 | (control->flags & (IEEE80211_TXCTL_USE_RTS_CTS | | 
|  | 112 | IEEE80211_TXCTL_USE_CTS_PROTECT))) { | 
| Ivo van Doorn | 1230cb8 | 2008-01-06 23:38:34 +0100 | [diff] [blame] | 113 | if (rt2x00_ring_free(ring) <= 1) { | 
|  | 114 | ieee80211_stop_queue(rt2x00dev->hw, control->queue); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 115 | return NETDEV_TX_BUSY; | 
| Ivo van Doorn | 1230cb8 | 2008-01-06 23:38:34 +0100 | [diff] [blame] | 116 | } | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 117 |  | 
| Ivo van Doorn | 1230cb8 | 2008-01-06 23:38:34 +0100 | [diff] [blame] | 118 | if (rt2x00mac_tx_rts_cts(rt2x00dev, ring, skb, control)) { | 
|  | 119 | ieee80211_stop_queue(rt2x00dev->hw, control->queue); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 120 | return NETDEV_TX_BUSY; | 
| Ivo van Doorn | 1230cb8 | 2008-01-06 23:38:34 +0100 | [diff] [blame] | 121 | } | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 122 | } | 
|  | 123 |  | 
| Ivo van Doorn | 1230cb8 | 2008-01-06 23:38:34 +0100 | [diff] [blame] | 124 | if (rt2x00dev->ops->lib->write_tx_data(rt2x00dev, ring, skb, control)) { | 
|  | 125 | ieee80211_stop_queue(rt2x00dev->hw, control->queue); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 126 | return NETDEV_TX_BUSY; | 
| Ivo van Doorn | 1230cb8 | 2008-01-06 23:38:34 +0100 | [diff] [blame] | 127 | } | 
|  | 128 |  | 
|  | 129 | if (rt2x00_ring_full(ring)) | 
|  | 130 | ieee80211_stop_queue(rt2x00dev->hw, control->queue); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 131 |  | 
|  | 132 | if (rt2x00dev->ops->lib->kick_tx_queue) | 
|  | 133 | rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, control->queue); | 
|  | 134 |  | 
|  | 135 | return NETDEV_TX_OK; | 
|  | 136 | } | 
|  | 137 | EXPORT_SYMBOL_GPL(rt2x00mac_tx); | 
|  | 138 |  | 
|  | 139 | int rt2x00mac_start(struct ieee80211_hw *hw) | 
|  | 140 | { | 
|  | 141 | struct rt2x00_dev *rt2x00dev = hw->priv; | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 142 |  | 
| Ivo van Doorn | e37ea21 | 2008-01-06 23:40:07 +0100 | [diff] [blame] | 143 | if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 144 | return 0; | 
|  | 145 |  | 
| Ivo van Doorn | e37ea21 | 2008-01-06 23:40:07 +0100 | [diff] [blame] | 146 | return rt2x00lib_start(rt2x00dev); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 147 | } | 
|  | 148 | EXPORT_SYMBOL_GPL(rt2x00mac_start); | 
|  | 149 |  | 
|  | 150 | void rt2x00mac_stop(struct ieee80211_hw *hw) | 
|  | 151 | { | 
|  | 152 | struct rt2x00_dev *rt2x00dev = hw->priv; | 
|  | 153 |  | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 154 | if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) | 
|  | 155 | return; | 
|  | 156 |  | 
| Ivo van Doorn | e37ea21 | 2008-01-06 23:40:07 +0100 | [diff] [blame] | 157 | rt2x00lib_stop(rt2x00dev); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 158 | } | 
|  | 159 | EXPORT_SYMBOL_GPL(rt2x00mac_stop); | 
|  | 160 |  | 
|  | 161 | int rt2x00mac_add_interface(struct ieee80211_hw *hw, | 
|  | 162 | struct ieee80211_if_init_conf *conf) | 
|  | 163 | { | 
|  | 164 | struct rt2x00_dev *rt2x00dev = hw->priv; | 
|  | 165 | struct interface *intf = &rt2x00dev->interface; | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 166 |  | 
| Ivo van Doorn | 453a3fb | 2007-10-28 14:39:52 +0100 | [diff] [blame] | 167 | /* FIXME: Beaconing is broken in rt2x00. */ | 
|  | 168 | if (conf->type == IEEE80211_IF_TYPE_IBSS || | 
|  | 169 | conf->type == IEEE80211_IF_TYPE_AP) { | 
|  | 170 | ERROR(rt2x00dev, | 
|  | 171 | "rt2x00 does not support Adhoc or Master mode"); | 
|  | 172 | return -EOPNOTSUPP; | 
|  | 173 | } | 
|  | 174 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 175 | /* | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 176 | * Don't allow interfaces to be added while | 
|  | 177 | * either the device has disappeared or when | 
|  | 178 | * another interface is already present. | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 179 | */ | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 180 | if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags) || | 
|  | 181 | is_interface_present(intf)) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 182 | return -ENOBUFS; | 
|  | 183 |  | 
| Johannes Berg | 32bfd35 | 2007-12-19 01:31:26 +0100 | [diff] [blame] | 184 | intf->id = conf->vif; | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 185 | intf->type = conf->type; | 
|  | 186 | if (conf->type == IEEE80211_IF_TYPE_AP) | 
|  | 187 | memcpy(&intf->bssid, conf->mac_addr, ETH_ALEN); | 
|  | 188 | memcpy(&intf->mac, conf->mac_addr, ETH_ALEN); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 189 |  | 
|  | 190 | /* | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 191 | * The MAC adddress must be configured after the device | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 192 | * has been initialized. Otherwise the device can reset | 
|  | 193 | * the MAC registers. | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 194 | */ | 
|  | 195 | rt2x00lib_config_mac_addr(rt2x00dev, intf->mac); | 
|  | 196 | rt2x00lib_config_type(rt2x00dev, conf->type); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 197 |  | 
|  | 198 | return 0; | 
|  | 199 | } | 
|  | 200 | EXPORT_SYMBOL_GPL(rt2x00mac_add_interface); | 
|  | 201 |  | 
|  | 202 | void rt2x00mac_remove_interface(struct ieee80211_hw *hw, | 
|  | 203 | struct ieee80211_if_init_conf *conf) | 
|  | 204 | { | 
|  | 205 | struct rt2x00_dev *rt2x00dev = hw->priv; | 
|  | 206 | struct interface *intf = &rt2x00dev->interface; | 
|  | 207 |  | 
|  | 208 | /* | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 209 | * Don't allow interfaces to be remove while | 
|  | 210 | * either the device has disappeared or when | 
|  | 211 | * no interface is present. | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 212 | */ | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 213 | if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags) || | 
|  | 214 | !is_interface_present(intf)) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 215 | return; | 
|  | 216 |  | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 217 | intf->id = 0; | 
| Ivo van Doorn | d28c256 | 2007-11-27 21:49:50 +0100 | [diff] [blame] | 218 | intf->type = IEEE80211_IF_TYPE_INVALID; | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 219 | memset(&intf->bssid, 0x00, ETH_ALEN); | 
|  | 220 | memset(&intf->mac, 0x00, ETH_ALEN); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 221 |  | 
|  | 222 | /* | 
|  | 223 | * Make sure the bssid and mac address registers | 
|  | 224 | * are cleared to prevent false ACKing of frames. | 
|  | 225 | */ | 
|  | 226 | rt2x00lib_config_mac_addr(rt2x00dev, intf->mac); | 
|  | 227 | rt2x00lib_config_bssid(rt2x00dev, intf->bssid); | 
|  | 228 | rt2x00lib_config_type(rt2x00dev, intf->type); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 229 | } | 
|  | 230 | EXPORT_SYMBOL_GPL(rt2x00mac_remove_interface); | 
|  | 231 |  | 
|  | 232 | int rt2x00mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) | 
|  | 233 | { | 
|  | 234 | struct rt2x00_dev *rt2x00dev = hw->priv; | 
|  | 235 |  | 
|  | 236 | /* | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 237 | * Mac80211 might be calling this function while we are trying | 
|  | 238 | * to remove the device or perhaps suspending it. | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 239 | */ | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 240 | if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 241 | return 0; | 
|  | 242 |  | 
|  | 243 | /* | 
|  | 244 | * Check if we need to disable the radio, | 
|  | 245 | * if this is not the case, at least the RX must be disabled. | 
|  | 246 | */ | 
|  | 247 | if (test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) { | 
|  | 248 | if (!conf->radio_enabled) | 
|  | 249 | rt2x00lib_disable_radio(rt2x00dev); | 
|  | 250 | else | 
| Ivo van Doorn | 5cbf830 | 2007-10-06 14:16:09 +0200 | [diff] [blame] | 251 | rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 252 | } | 
|  | 253 |  | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 254 | rt2x00lib_config(rt2x00dev, conf, 0); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 255 |  | 
|  | 256 | /* | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 257 | * Reenable RX only if the radio should be on. | 
|  | 258 | */ | 
|  | 259 | if (test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) | 
| Ivo van Doorn | 5cbf830 | 2007-10-06 14:16:09 +0200 | [diff] [blame] | 260 | rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 261 | else if (conf->radio_enabled) | 
|  | 262 | return rt2x00lib_enable_radio(rt2x00dev); | 
|  | 263 |  | 
|  | 264 | return 0; | 
|  | 265 | } | 
|  | 266 | EXPORT_SYMBOL_GPL(rt2x00mac_config); | 
|  | 267 |  | 
| Johannes Berg | 32bfd35 | 2007-12-19 01:31:26 +0100 | [diff] [blame] | 268 | int rt2x00mac_config_interface(struct ieee80211_hw *hw, | 
|  | 269 | struct ieee80211_vif *vif, | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 270 | struct ieee80211_if_conf *conf) | 
|  | 271 | { | 
|  | 272 | struct rt2x00_dev *rt2x00dev = hw->priv; | 
|  | 273 | struct interface *intf = &rt2x00dev->interface; | 
|  | 274 | int status; | 
|  | 275 |  | 
|  | 276 | /* | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 277 | * Mac80211 might be calling this function while we are trying | 
|  | 278 | * to remove the device or perhaps suspending it. | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 279 | */ | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 280 | if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 281 | return 0; | 
|  | 282 |  | 
|  | 283 | /* | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 284 | * If the given type does not match the configured type, | 
|  | 285 | * there has been a problem. | 
|  | 286 | */ | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 287 | if (conf->type != intf->type) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 288 | return -EINVAL; | 
|  | 289 |  | 
|  | 290 | /* | 
|  | 291 | * If the interface does not work in master mode, | 
|  | 292 | * then the bssid value in the interface structure | 
|  | 293 | * should now be set. | 
|  | 294 | */ | 
|  | 295 | if (conf->type != IEEE80211_IF_TYPE_AP) | 
|  | 296 | memcpy(&intf->bssid, conf->bssid, ETH_ALEN); | 
|  | 297 | rt2x00lib_config_bssid(rt2x00dev, intf->bssid); | 
|  | 298 |  | 
|  | 299 | /* | 
|  | 300 | * We only need to initialize the beacon when master mode is enabled. | 
|  | 301 | */ | 
|  | 302 | if (conf->type != IEEE80211_IF_TYPE_AP || !conf->beacon) | 
|  | 303 | return 0; | 
|  | 304 |  | 
|  | 305 | status = rt2x00dev->ops->hw->beacon_update(rt2x00dev->hw, | 
|  | 306 | conf->beacon, | 
|  | 307 | conf->beacon_control); | 
|  | 308 | if (status) | 
|  | 309 | dev_kfree_skb(conf->beacon); | 
|  | 310 |  | 
|  | 311 | return status; | 
|  | 312 | } | 
|  | 313 | EXPORT_SYMBOL_GPL(rt2x00mac_config_interface); | 
|  | 314 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 315 | int rt2x00mac_get_stats(struct ieee80211_hw *hw, | 
|  | 316 | struct ieee80211_low_level_stats *stats) | 
|  | 317 | { | 
|  | 318 | struct rt2x00_dev *rt2x00dev = hw->priv; | 
|  | 319 |  | 
|  | 320 | /* | 
|  | 321 | * The dot11ACKFailureCount, dot11RTSFailureCount and | 
|  | 322 | * dot11RTSSuccessCount are updated in interrupt time. | 
|  | 323 | * dot11FCSErrorCount is updated in the link tuner. | 
|  | 324 | */ | 
|  | 325 | memcpy(stats, &rt2x00dev->low_level_stats, sizeof(*stats)); | 
|  | 326 |  | 
|  | 327 | return 0; | 
|  | 328 | } | 
|  | 329 | EXPORT_SYMBOL_GPL(rt2x00mac_get_stats); | 
|  | 330 |  | 
|  | 331 | int rt2x00mac_get_tx_stats(struct ieee80211_hw *hw, | 
|  | 332 | struct ieee80211_tx_queue_stats *stats) | 
|  | 333 | { | 
|  | 334 | struct rt2x00_dev *rt2x00dev = hw->priv; | 
|  | 335 | unsigned int i; | 
|  | 336 |  | 
|  | 337 | for (i = 0; i < hw->queues; i++) | 
|  | 338 | memcpy(&stats->data[i], &rt2x00dev->tx[i].stats, | 
|  | 339 | sizeof(rt2x00dev->tx[i].stats)); | 
|  | 340 |  | 
|  | 341 | return 0; | 
|  | 342 | } | 
|  | 343 | EXPORT_SYMBOL_GPL(rt2x00mac_get_tx_stats); | 
|  | 344 |  | 
| Johannes Berg | 471b3ef | 2007-12-28 14:32:58 +0100 | [diff] [blame] | 345 | void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, | 
|  | 346 | struct ieee80211_vif *vif, | 
|  | 347 | struct ieee80211_bss_conf *bss_conf, | 
|  | 348 | u32 changes) | 
| Ivo van Doorn | 5c58ee5 | 2007-10-06 13:34:52 +0200 | [diff] [blame] | 349 | { | 
|  | 350 | struct rt2x00_dev *rt2x00dev = hw->priv; | 
|  | 351 | int short_preamble; | 
|  | 352 | int ack_timeout; | 
|  | 353 | int ack_consume_time; | 
|  | 354 | int difs; | 
| Johannes Berg | 471b3ef | 2007-12-28 14:32:58 +0100 | [diff] [blame] | 355 | int preamble; | 
| Ivo van Doorn | 5c58ee5 | 2007-10-06 13:34:52 +0200 | [diff] [blame] | 356 |  | 
|  | 357 | /* | 
|  | 358 | * We only support changing preamble mode. | 
|  | 359 | */ | 
| Johannes Berg | 471b3ef | 2007-12-28 14:32:58 +0100 | [diff] [blame] | 360 | if (!(changes & BSS_CHANGED_ERP_PREAMBLE)) | 
| Ivo van Doorn | 5c58ee5 | 2007-10-06 13:34:52 +0200 | [diff] [blame] | 361 | return; | 
|  | 362 |  | 
| Johannes Berg | 471b3ef | 2007-12-28 14:32:58 +0100 | [diff] [blame] | 363 | short_preamble = bss_conf->use_short_preamble; | 
|  | 364 | preamble = bss_conf->use_short_preamble ? | 
|  | 365 | SHORT_PREAMBLE : PREAMBLE; | 
| Ivo van Doorn | 5c58ee5 | 2007-10-06 13:34:52 +0200 | [diff] [blame] | 366 |  | 
|  | 367 | difs = (hw->conf.flags & IEEE80211_CONF_SHORT_SLOT_TIME) ? | 
|  | 368 | SHORT_DIFS : DIFS; | 
|  | 369 | ack_timeout = difs + PLCP + preamble + get_duration(ACK_SIZE, 10); | 
|  | 370 |  | 
|  | 371 | ack_consume_time = SIFS + PLCP + preamble + get_duration(ACK_SIZE, 10); | 
|  | 372 |  | 
|  | 373 | if (short_preamble) | 
|  | 374 | __set_bit(CONFIG_SHORT_PREAMBLE, &rt2x00dev->flags); | 
|  | 375 | else | 
|  | 376 | __clear_bit(CONFIG_SHORT_PREAMBLE, &rt2x00dev->flags); | 
|  | 377 |  | 
|  | 378 | rt2x00dev->ops->lib->config_preamble(rt2x00dev, short_preamble, | 
|  | 379 | ack_timeout, ack_consume_time); | 
|  | 380 | } | 
| Johannes Berg | 471b3ef | 2007-12-28 14:32:58 +0100 | [diff] [blame] | 381 | EXPORT_SYMBOL_GPL(rt2x00mac_bss_info_changed); | 
| Ivo van Doorn | 5c58ee5 | 2007-10-06 13:34:52 +0200 | [diff] [blame] | 382 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 383 | int rt2x00mac_conf_tx(struct ieee80211_hw *hw, int queue, | 
|  | 384 | const struct ieee80211_tx_queue_params *params) | 
|  | 385 | { | 
|  | 386 | struct rt2x00_dev *rt2x00dev = hw->priv; | 
|  | 387 | struct data_ring *ring; | 
|  | 388 |  | 
|  | 389 | ring = rt2x00lib_get_ring(rt2x00dev, queue); | 
|  | 390 | if (unlikely(!ring)) | 
|  | 391 | return -EINVAL; | 
|  | 392 |  | 
|  | 393 | /* | 
|  | 394 | * The passed variables are stored as real value ((2^n)-1). | 
|  | 395 | * Ralink registers require to know the bit number 'n'. | 
|  | 396 | */ | 
|  | 397 | if (params->cw_min) | 
|  | 398 | ring->tx_params.cw_min = fls(params->cw_min); | 
|  | 399 | else | 
|  | 400 | ring->tx_params.cw_min = 5; /* cw_min: 2^5 = 32. */ | 
|  | 401 |  | 
|  | 402 | if (params->cw_max) | 
|  | 403 | ring->tx_params.cw_max = fls(params->cw_max); | 
|  | 404 | else | 
|  | 405 | ring->tx_params.cw_max = 10; /* cw_min: 2^10 = 1024. */ | 
|  | 406 |  | 
|  | 407 | if (params->aifs) | 
|  | 408 | ring->tx_params.aifs = params->aifs; | 
|  | 409 | else | 
|  | 410 | ring->tx_params.aifs = 2; | 
|  | 411 |  | 
|  | 412 | INFO(rt2x00dev, | 
|  | 413 | "Configured TX ring %d - CWmin: %d, CWmax: %d, Aifs: %d.\n", | 
|  | 414 | queue, ring->tx_params.cw_min, ring->tx_params.cw_max, | 
|  | 415 | ring->tx_params.aifs); | 
|  | 416 |  | 
|  | 417 | return 0; | 
|  | 418 | } | 
|  | 419 | EXPORT_SYMBOL_GPL(rt2x00mac_conf_tx); |