diff options
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2x00mac.c')
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00mac.c | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c new file mode 100644 index 000000000000..778ed41e21ef --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c | |||
@@ -0,0 +1,459 @@ | |||
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 | |||
26 | /* | ||
27 | * Set enviroment defines for rt2x00.h | ||
28 | */ | ||
29 | #define DRV_NAME "rt2x00lib" | ||
30 | |||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/module.h> | ||
33 | |||
34 | #include "rt2x00.h" | ||
35 | #include "rt2x00lib.h" | ||
36 | |||
37 | static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, | ||
38 | struct data_ring *ring, | ||
39 | struct sk_buff *frag_skb, | ||
40 | struct ieee80211_tx_control *control) | ||
41 | { | ||
42 | struct sk_buff *skb; | ||
43 | int size; | ||
44 | |||
45 | if (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) | ||
46 | size = sizeof(struct ieee80211_cts); | ||
47 | else | ||
48 | size = sizeof(struct ieee80211_rts); | ||
49 | |||
50 | skb = dev_alloc_skb(size + rt2x00dev->hw->extra_tx_headroom); | ||
51 | if (!skb) { | ||
52 | WARNING(rt2x00dev, "Failed to create RTS/CTS frame.\n"); | ||
53 | return NETDEV_TX_BUSY; | ||
54 | } | ||
55 | |||
56 | skb_reserve(skb, rt2x00dev->hw->extra_tx_headroom); | ||
57 | skb_put(skb, size); | ||
58 | |||
59 | if (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) | ||
60 | ieee80211_ctstoself_get(rt2x00dev->hw, rt2x00dev->interface.id, | ||
61 | frag_skb->data, frag_skb->len, control, | ||
62 | (struct ieee80211_cts *)(skb->data)); | ||
63 | else | ||
64 | ieee80211_rts_get(rt2x00dev->hw, rt2x00dev->interface.id, | ||
65 | frag_skb->data, frag_skb->len, control, | ||
66 | (struct ieee80211_rts *)(skb->data)); | ||
67 | |||
68 | if (rt2x00dev->ops->lib->write_tx_data(rt2x00dev, ring, skb, control)) { | ||
69 | WARNING(rt2x00dev, "Failed to send RTS/CTS frame.\n"); | ||
70 | return NETDEV_TX_BUSY; | ||
71 | } | ||
72 | |||
73 | return NETDEV_TX_OK; | ||
74 | } | ||
75 | |||
76 | int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb, | ||
77 | struct ieee80211_tx_control *control) | ||
78 | { | ||
79 | struct rt2x00_dev *rt2x00dev = hw->priv; | ||
80 | struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data; | ||
81 | struct data_ring *ring; | ||
82 | u16 frame_control; | ||
83 | |||
84 | /* | ||
85 | * Determine which ring to put packet on. | ||
86 | */ | ||
87 | ring = rt2x00lib_get_ring(rt2x00dev, control->queue); | ||
88 | if (unlikely(!ring)) { | ||
89 | ERROR(rt2x00dev, | ||
90 | "Attempt to send packet over invalid queue %d.\n" | ||
91 | "Please file bug report to %s.\n", | ||
92 | control->queue, DRV_PROJECT); | ||
93 | dev_kfree_skb_any(skb); | ||
94 | return NETDEV_TX_OK; | ||
95 | } | ||
96 | |||
97 | /* | ||
98 | * If CTS/RTS is required. and this frame is not CTS or RTS, | ||
99 | * create and queue that frame first. But make sure we have | ||
100 | * at least enough entries available to send this CTS/RTS | ||
101 | * frame as well as the data frame. | ||
102 | */ | ||
103 | frame_control = le16_to_cpu(ieee80211hdr->frame_control); | ||
104 | if (!is_rts_frame(frame_control) && !is_cts_frame(frame_control) && | ||
105 | (control->flags & (IEEE80211_TXCTL_USE_RTS_CTS | | ||
106 | IEEE80211_TXCTL_USE_CTS_PROTECT))) { | ||
107 | if (rt2x00_ring_free(ring) <= 1) | ||
108 | return NETDEV_TX_BUSY; | ||
109 | |||
110 | if (rt2x00mac_tx_rts_cts(rt2x00dev, ring, skb, control)) | ||
111 | return NETDEV_TX_BUSY; | ||
112 | } | ||
113 | |||
114 | if (rt2x00dev->ops->lib->write_tx_data(rt2x00dev, ring, skb, control)) | ||
115 | return NETDEV_TX_BUSY; | ||
116 | |||
117 | if (rt2x00dev->ops->lib->kick_tx_queue) | ||
118 | rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, control->queue); | ||
119 | |||
120 | return NETDEV_TX_OK; | ||
121 | } | ||
122 | EXPORT_SYMBOL_GPL(rt2x00mac_tx); | ||
123 | |||
124 | int rt2x00mac_start(struct ieee80211_hw *hw) | ||
125 | { | ||
126 | struct rt2x00_dev *rt2x00dev = hw->priv; | ||
127 | int status; | ||
128 | |||
129 | if (test_bit(DEVICE_INITIALIZED, &rt2x00dev->flags)) | ||
130 | return 0; | ||
131 | |||
132 | /* | ||
133 | * If this is the first interface which is added, | ||
134 | * we should load the firmware now. | ||
135 | */ | ||
136 | if (test_bit(REQUIRE_FIRMWARE, &rt2x00dev->flags)) { | ||
137 | status = rt2x00lib_load_firmware(rt2x00dev); | ||
138 | if (status) | ||
139 | return status; | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * Initialize the device. | ||
144 | */ | ||
145 | status = rt2x00lib_initialize(rt2x00dev); | ||
146 | if (status) | ||
147 | return status; | ||
148 | |||
149 | /* | ||
150 | * Enable radio. | ||
151 | */ | ||
152 | status = rt2x00lib_enable_radio(rt2x00dev); | ||
153 | if (status) { | ||
154 | rt2x00lib_uninitialize(rt2x00dev); | ||
155 | return status; | ||
156 | } | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | EXPORT_SYMBOL_GPL(rt2x00mac_start); | ||
161 | |||
162 | void rt2x00mac_stop(struct ieee80211_hw *hw) | ||
163 | { | ||
164 | struct rt2x00_dev *rt2x00dev = hw->priv; | ||
165 | |||
166 | /* | ||
167 | * Perhaps we can add something smarter here, | ||
168 | * but for now just disabling the radio should do. | ||
169 | */ | ||
170 | rt2x00lib_disable_radio(rt2x00dev); | ||
171 | } | ||
172 | EXPORT_SYMBOL_GPL(rt2x00mac_stop); | ||
173 | |||
174 | int rt2x00mac_add_interface(struct ieee80211_hw *hw, | ||
175 | struct ieee80211_if_init_conf *conf) | ||
176 | { | ||
177 | struct rt2x00_dev *rt2x00dev = hw->priv; | ||
178 | struct interface *intf = &rt2x00dev->interface; | ||
179 | int retval; | ||
180 | |||
181 | /* | ||
182 | * We only support 1 non-monitor interface. | ||
183 | */ | ||
184 | if (conf->type != IEEE80211_IF_TYPE_MNTR && is_interface_present(intf)) | ||
185 | return -ENOBUFS; | ||
186 | |||
187 | /* | ||
188 | * HACK: Placeholder until start/stop handler has been | ||
189 | * added to the mac80211 callback functions structure. | ||
190 | */ | ||
191 | retval = rt2x00mac_start(hw); | ||
192 | if (retval) | ||
193 | return retval; | ||
194 | |||
195 | /* | ||
196 | * We support muliple monitor mode interfaces. | ||
197 | * All we need to do is increase the monitor_count. | ||
198 | */ | ||
199 | if (conf->type == IEEE80211_IF_TYPE_MNTR) { | ||
200 | intf->monitor_count++; | ||
201 | } else { | ||
202 | intf->id = conf->if_id; | ||
203 | intf->type = conf->type; | ||
204 | if (conf->type == IEEE80211_IF_TYPE_AP) | ||
205 | memcpy(&intf->bssid, conf->mac_addr, ETH_ALEN); | ||
206 | memcpy(&intf->mac, conf->mac_addr, ETH_ALEN); | ||
207 | intf->filter = 0; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Configure interface. | ||
212 | * The MAC adddress must be configured after the device | ||
213 | * has been initialized. Else the device can reset the | ||
214 | * MAC registers. | ||
215 | */ | ||
216 | rt2x00lib_config_mac_addr(rt2x00dev, intf->mac); | ||
217 | rt2x00lib_config_type(rt2x00dev, conf->type); | ||
218 | rt2x00lib_config_packet_filter(rt2x00dev, intf->filter); | ||
219 | |||
220 | return 0; | ||
221 | } | ||
222 | EXPORT_SYMBOL_GPL(rt2x00mac_add_interface); | ||
223 | |||
224 | void rt2x00mac_remove_interface(struct ieee80211_hw *hw, | ||
225 | struct ieee80211_if_init_conf *conf) | ||
226 | { | ||
227 | struct rt2x00_dev *rt2x00dev = hw->priv; | ||
228 | struct interface *intf = &rt2x00dev->interface; | ||
229 | |||
230 | /* | ||
231 | * We only support 1 non-monitor interface. | ||
232 | */ | ||
233 | if (conf->type != IEEE80211_IF_TYPE_MNTR && !is_interface_present(intf)) | ||
234 | return; | ||
235 | |||
236 | /* | ||
237 | * When removing an monitor interface, decrease monitor_count. | ||
238 | * For non-monitor interfaces, all interface data needs to be reset. | ||
239 | */ | ||
240 | if (conf->type == IEEE80211_IF_TYPE_MNTR) { | ||
241 | intf->monitor_count--; | ||
242 | } else if (intf->type == conf->type) { | ||
243 | intf->id = 0; | ||
244 | intf->type = INVALID_INTERFACE; | ||
245 | memset(&intf->bssid, 0x00, ETH_ALEN); | ||
246 | memset(&intf->mac, 0x00, ETH_ALEN); | ||
247 | intf->filter = 0; | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * Make sure the bssid and mac address registers | ||
252 | * are cleared to prevent false ACKing of frames. | ||
253 | */ | ||
254 | rt2x00lib_config_mac_addr(rt2x00dev, intf->mac); | ||
255 | rt2x00lib_config_bssid(rt2x00dev, intf->bssid); | ||
256 | rt2x00lib_config_type(rt2x00dev, intf->type); | ||
257 | |||
258 | /* | ||
259 | * HACK: Placeholder untill start/stop handler has been | ||
260 | * added to the mac80211 callback functions structure. | ||
261 | */ | ||
262 | rt2x00mac_stop(hw); | ||
263 | } | ||
264 | EXPORT_SYMBOL_GPL(rt2x00mac_remove_interface); | ||
265 | |||
266 | int rt2x00mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) | ||
267 | { | ||
268 | struct rt2x00_dev *rt2x00dev = hw->priv; | ||
269 | |||
270 | /* | ||
271 | * If the device is not initialized we shouldn't accept | ||
272 | * any configuration changes. Mac80211 might be calling | ||
273 | * this function while we are trying to remove the device | ||
274 | * or perhaps suspending it. | ||
275 | */ | ||
276 | if (!test_bit(DEVICE_INITIALIZED, &rt2x00dev->flags)) | ||
277 | return 0; | ||
278 | |||
279 | /* | ||
280 | * Check if we need to disable the radio, | ||
281 | * if this is not the case, at least the RX must be disabled. | ||
282 | */ | ||
283 | if (test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) { | ||
284 | if (!conf->radio_enabled) | ||
285 | rt2x00lib_disable_radio(rt2x00dev); | ||
286 | else | ||
287 | rt2x00lib_toggle_rx(rt2x00dev, 0); | ||
288 | } | ||
289 | |||
290 | rt2x00lib_config(rt2x00dev, conf); | ||
291 | |||
292 | /* | ||
293 | * If promisc mode cannot be configured in irq context, | ||
294 | * then it is now the time to configure it. | ||
295 | */ | ||
296 | if (test_bit(PACKET_FILTER_SCHEDULED, &rt2x00dev->flags)) | ||
297 | rt2x00lib_config_packet_filter(rt2x00dev, | ||
298 | rt2x00dev->interface.filter); | ||
299 | |||
300 | /* | ||
301 | * Reenable RX only if the radio should be on. | ||
302 | */ | ||
303 | if (test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
304 | rt2x00lib_toggle_rx(rt2x00dev, 1); | ||
305 | else if (conf->radio_enabled) | ||
306 | return rt2x00lib_enable_radio(rt2x00dev); | ||
307 | |||
308 | return 0; | ||
309 | } | ||
310 | EXPORT_SYMBOL_GPL(rt2x00mac_config); | ||
311 | |||
312 | int rt2x00mac_config_interface(struct ieee80211_hw *hw, int if_id, | ||
313 | struct ieee80211_if_conf *conf) | ||
314 | { | ||
315 | struct rt2x00_dev *rt2x00dev = hw->priv; | ||
316 | struct interface *intf = &rt2x00dev->interface; | ||
317 | int status; | ||
318 | |||
319 | /* | ||
320 | * If the device is not initialized we shouldn't accept | ||
321 | * any configuration changes. Mac80211 might be calling | ||
322 | * this function while we are trying to remove the device | ||
323 | * or perhaps suspending it. | ||
324 | */ | ||
325 | if (!test_bit(DEVICE_INITIALIZED, &rt2x00dev->flags)) | ||
326 | return 0; | ||
327 | |||
328 | /* | ||
329 | * Monitor mode does not need configuring. | ||
330 | * If the given type does not match the configured type, | ||
331 | * there has been a problem. | ||
332 | */ | ||
333 | if (conf->type == IEEE80211_IF_TYPE_MNTR) | ||
334 | return 0; | ||
335 | else if (conf->type != intf->type) | ||
336 | return -EINVAL; | ||
337 | |||
338 | /* | ||
339 | * If the interface does not work in master mode, | ||
340 | * then the bssid value in the interface structure | ||
341 | * should now be set. | ||
342 | */ | ||
343 | if (conf->type != IEEE80211_IF_TYPE_AP) | ||
344 | memcpy(&intf->bssid, conf->bssid, ETH_ALEN); | ||
345 | rt2x00lib_config_bssid(rt2x00dev, intf->bssid); | ||
346 | |||
347 | /* | ||
348 | * We only need to initialize the beacon when master mode is enabled. | ||
349 | */ | ||
350 | if (conf->type != IEEE80211_IF_TYPE_AP || !conf->beacon) | ||
351 | return 0; | ||
352 | |||
353 | status = rt2x00dev->ops->hw->beacon_update(rt2x00dev->hw, | ||
354 | conf->beacon, | ||
355 | conf->beacon_control); | ||
356 | if (status) | ||
357 | dev_kfree_skb(conf->beacon); | ||
358 | |||
359 | return status; | ||
360 | } | ||
361 | EXPORT_SYMBOL_GPL(rt2x00mac_config_interface); | ||
362 | |||
363 | void rt2x00mac_set_multicast_list(struct ieee80211_hw *hw, | ||
364 | unsigned short flags, int mc_count) | ||
365 | { | ||
366 | struct rt2x00_dev *rt2x00dev = hw->priv; | ||
367 | |||
368 | /* | ||
369 | * Check if the new state is different then the old state. | ||
370 | */ | ||
371 | if (rt2x00dev->interface.filter == flags) | ||
372 | return; | ||
373 | |||
374 | rt2x00dev->interface.filter = flags; | ||
375 | |||
376 | /* | ||
377 | * Raise the pending bit to indicate the | ||
378 | * packet filter should be updated. | ||
379 | */ | ||
380 | __set_bit(PACKET_FILTER_PENDING, &rt2x00dev->flags); | ||
381 | |||
382 | /* | ||
383 | * Check if Packet filter actions are allowed in | ||
384 | * atomic context. If not, raise the pending flag and | ||
385 | * let it be. | ||
386 | */ | ||
387 | if (!test_bit(PACKET_FILTER_SCHEDULED, &rt2x00dev->flags) || | ||
388 | !in_atomic()) | ||
389 | rt2x00lib_config_packet_filter(rt2x00dev, flags); | ||
390 | } | ||
391 | EXPORT_SYMBOL_GPL(rt2x00mac_set_multicast_list); | ||
392 | |||
393 | int rt2x00mac_get_stats(struct ieee80211_hw *hw, | ||
394 | struct ieee80211_low_level_stats *stats) | ||
395 | { | ||
396 | struct rt2x00_dev *rt2x00dev = hw->priv; | ||
397 | |||
398 | /* | ||
399 | * The dot11ACKFailureCount, dot11RTSFailureCount and | ||
400 | * dot11RTSSuccessCount are updated in interrupt time. | ||
401 | * dot11FCSErrorCount is updated in the link tuner. | ||
402 | */ | ||
403 | memcpy(stats, &rt2x00dev->low_level_stats, sizeof(*stats)); | ||
404 | |||
405 | return 0; | ||
406 | } | ||
407 | EXPORT_SYMBOL_GPL(rt2x00mac_get_stats); | ||
408 | |||
409 | int rt2x00mac_get_tx_stats(struct ieee80211_hw *hw, | ||
410 | struct ieee80211_tx_queue_stats *stats) | ||
411 | { | ||
412 | struct rt2x00_dev *rt2x00dev = hw->priv; | ||
413 | unsigned int i; | ||
414 | |||
415 | for (i = 0; i < hw->queues; i++) | ||
416 | memcpy(&stats->data[i], &rt2x00dev->tx[i].stats, | ||
417 | sizeof(rt2x00dev->tx[i].stats)); | ||
418 | |||
419 | return 0; | ||
420 | } | ||
421 | EXPORT_SYMBOL_GPL(rt2x00mac_get_tx_stats); | ||
422 | |||
423 | int rt2x00mac_conf_tx(struct ieee80211_hw *hw, int queue, | ||
424 | const struct ieee80211_tx_queue_params *params) | ||
425 | { | ||
426 | struct rt2x00_dev *rt2x00dev = hw->priv; | ||
427 | struct data_ring *ring; | ||
428 | |||
429 | ring = rt2x00lib_get_ring(rt2x00dev, queue); | ||
430 | if (unlikely(!ring)) | ||
431 | return -EINVAL; | ||
432 | |||
433 | /* | ||
434 | * The passed variables are stored as real value ((2^n)-1). | ||
435 | * Ralink registers require to know the bit number 'n'. | ||
436 | */ | ||
437 | if (params->cw_min) | ||
438 | ring->tx_params.cw_min = fls(params->cw_min); | ||
439 | else | ||
440 | ring->tx_params.cw_min = 5; /* cw_min: 2^5 = 32. */ | ||
441 | |||
442 | if (params->cw_max) | ||
443 | ring->tx_params.cw_max = fls(params->cw_max); | ||
444 | else | ||
445 | ring->tx_params.cw_max = 10; /* cw_min: 2^10 = 1024. */ | ||
446 | |||
447 | if (params->aifs) | ||
448 | ring->tx_params.aifs = params->aifs; | ||
449 | else | ||
450 | ring->tx_params.aifs = 2; | ||
451 | |||
452 | INFO(rt2x00dev, | ||
453 | "Configured TX ring %d - CWmin: %d, CWmax: %d, Aifs: %d.\n", | ||
454 | queue, ring->tx_params.cw_min, ring->tx_params.cw_max, | ||
455 | ring->tx_params.aifs); | ||
456 | |||
457 | return 0; | ||
458 | } | ||
459 | EXPORT_SYMBOL_GPL(rt2x00mac_conf_tx); | ||