aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/libertas_tf
diff options
context:
space:
mode:
authorLuis Carlos Cobo <luisca@cozybit.com>2008-08-14 13:40:57 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-08-22 16:29:55 -0400
commit06b16ae5319251c26377afcb401e46056d5673f4 (patch)
treef0924cbdad4a46fe2e8f458840536bddb1d9ea93 /drivers/net/wireless/libertas_tf
parent7670e62c7ed6d1a70a98c3047898712be6aa9ff8 (diff)
libertas_tf: main.c, data paths and mac80211 handlers
This patch contains most of the libertastf driver, just lacking command helper functions and usb specific functions. Currently, monitor, managed, ap and mesh interfaces are supported. Even though this driver supports the same hardware as the "libertas" driver, it uses a different (thin) firmware, that makes it suitable for a mac80211 driver. Signed-off-by: Luis Carlos Cobo <luisca@cozybit.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas_tf')
-rw-r--r--drivers/net/wireless/libertas_tf/main.c666
1 files changed, 666 insertions, 0 deletions
diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c
new file mode 100644
index 00000000000..2c1d680d2c5
--- /dev/null
+++ b/drivers/net/wireless/libertas_tf/main.c
@@ -0,0 +1,666 @@
1/*
2 * Copyright (C) 2008, cozybit Inc.
3 * Copyright (C) 2003-2006, Marvell International Ltd.
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 (at
8 * your option) any later version.
9 */
10#include "libertas_tf.h"
11#include "linux/etherdevice.h"
12
13#define DRIVER_RELEASE_VERSION "004.p0"
14/* thinfirm version: 5.132.X.pX */
15#define LBTF_FW_VER_MIN 0x05840300
16#define LBTF_FW_VER_MAX 0x0584ffff
17#define QOS_CONTROL_LEN 2
18
19static const char lbtf_driver_version[] = "THINFIRM-USB8388-" DRIVER_RELEASE_VERSION;
20struct workqueue_struct *lbtf_wq;
21
22static const struct ieee80211_channel lbtf_channels[] = {
23 { .center_freq = 2412, .hw_value = 1 },
24 { .center_freq = 2417, .hw_value = 2 },
25 { .center_freq = 2422, .hw_value = 3 },
26 { .center_freq = 2427, .hw_value = 4 },
27 { .center_freq = 2432, .hw_value = 5 },
28 { .center_freq = 2437, .hw_value = 6 },
29 { .center_freq = 2442, .hw_value = 7 },
30 { .center_freq = 2447, .hw_value = 8 },
31 { .center_freq = 2452, .hw_value = 9 },
32 { .center_freq = 2457, .hw_value = 10 },
33 { .center_freq = 2462, .hw_value = 11 },
34 { .center_freq = 2467, .hw_value = 12 },
35 { .center_freq = 2472, .hw_value = 13 },
36 { .center_freq = 2484, .hw_value = 14 },
37};
38
39/* This table contains the hardware specific values for the modulation rates. */
40static const struct ieee80211_rate lbtf_rates[] = {
41 { .bitrate = 10,
42 .hw_value = 0, },
43 { .bitrate = 20,
44 .hw_value = 1,
45 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
46 { .bitrate = 55,
47 .hw_value = 2,
48 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
49 { .bitrate = 110,
50 .hw_value = 3,
51 .flags = IEEE80211_RATE_SHORT_PREAMBLE },
52 { .bitrate = 60,
53 .hw_value = 5,
54 .flags = 0 },
55 { .bitrate = 90,
56 .hw_value = 6,
57 .flags = 0 },
58 { .bitrate = 120,
59 .hw_value = 7,
60 .flags = 0 },
61 { .bitrate = 180,
62 .hw_value = 8,
63 .flags = 0 },
64 { .bitrate = 240,
65 .hw_value = 9,
66 .flags = 0 },
67 { .bitrate = 360,
68 .hw_value = 10,
69 .flags = 0 },
70 { .bitrate = 480,
71 .hw_value = 11,
72 .flags = 0 },
73 { .bitrate = 540,
74 .hw_value = 12,
75 .flags = 0 },
76};
77
78static void lbtf_cmd_work(struct work_struct *work)
79{
80 struct lbtf_private *priv = container_of(work, struct lbtf_private,
81 cmd_work);
82 spin_lock_irq(&priv->driver_lock);
83 /* command response? */
84 if (priv->cmd_response_rxed) {
85 priv->cmd_response_rxed = 0;
86 spin_unlock_irq(&priv->driver_lock);
87 lbtf_process_rx_command(priv);
88 spin_lock_irq(&priv->driver_lock);
89 }
90
91 if (priv->cmd_timed_out && priv->cur_cmd) {
92 struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
93
94 if (++priv->nr_retries > 10) {
95 lbtf_complete_command(priv, cmdnode,
96 -ETIMEDOUT);
97 priv->nr_retries = 0;
98 } else {
99 priv->cur_cmd = NULL;
100
101 /* Stick it back at the _top_ of the pending
102 * queue for immediate resubmission */
103 list_add(&cmdnode->list, &priv->cmdpendingq);
104 }
105 }
106 priv->cmd_timed_out = 0;
107 spin_unlock_irq(&priv->driver_lock);
108
109 if (!priv->fw_ready)
110 return;
111 /* Execute the next command */
112 if (!priv->cur_cmd)
113 lbtf_execute_next_command(priv);
114}
115
116/**
117 * lbtf_setup_firmware: initialize firmware.
118 *
119 * @priv A pointer to struct lbtf_private structure
120 *
121 * Returns: 0 on success.
122 */
123static int lbtf_setup_firmware(struct lbtf_private *priv)
124{
125 int ret = -1;
126
127 /*
128 * Read priv address from HW
129 */
130 memset(priv->current_addr, 0xff, ETH_ALEN);
131 ret = lbtf_update_hw_spec(priv);
132 if (ret) {
133 ret = -1;
134 goto done;
135 }
136
137 lbtf_set_mac_control(priv);
138 lbtf_set_radio_control(priv);
139
140 ret = 0;
141done:
142 return ret;
143}
144
145/**
146 * This function handles the timeout of command sending.
147 * It will re-send the same command again.
148 */
149static void command_timer_fn(unsigned long data)
150{
151 struct lbtf_private *priv = (struct lbtf_private *)data;
152 unsigned long flags;
153
154 spin_lock_irqsave(&priv->driver_lock, flags);
155
156 if (!priv->cur_cmd) {
157 printk(KERN_DEBUG "libertastf: command timer expired; "
158 "no pending command\n");
159 goto out;
160 }
161
162 printk(KERN_DEBUG "libertas: command %x timed out\n",
163 le16_to_cpu(priv->cur_cmd->cmdbuf->command));
164
165 priv->cmd_timed_out = 1;
166 queue_work(lbtf_wq, &priv->cmd_work);
167out:
168 spin_unlock_irqrestore(&priv->driver_lock, flags);
169}
170
171static int lbtf_init_adapter(struct lbtf_private *priv)
172{
173 memset(priv->current_addr, 0xff, ETH_ALEN);
174 mutex_init(&priv->lock);
175
176 priv->vif = NULL;
177 setup_timer(&priv->command_timer, command_timer_fn,
178 (unsigned long)priv);
179
180 INIT_LIST_HEAD(&priv->cmdfreeq);
181 INIT_LIST_HEAD(&priv->cmdpendingq);
182
183 spin_lock_init(&priv->driver_lock);
184
185 /* Allocate the command buffers */
186 if (lbtf_allocate_cmd_buffer(priv))
187 return -1;
188
189 return 0;
190}
191
192static void lbtf_free_adapter(struct lbtf_private *priv)
193{
194 lbtf_free_cmd_buffer(priv);
195 del_timer(&priv->command_timer);
196}
197
198static int lbtf_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
199{
200 struct lbtf_private *priv = hw->priv;
201
202 priv->skb_to_tx = skb;
203 queue_work(lbtf_wq, &priv->tx_work);
204 /*
205 * queue will be restarted when we receive transmission feedback if
206 * there are no buffered multicast frames to send
207 */
208 ieee80211_stop_queues(priv->hw);
209 return 0;
210}
211
212static void lbtf_tx_work(struct work_struct *work)
213{
214 struct lbtf_private *priv = container_of(work, struct lbtf_private,
215 tx_work);
216 unsigned int len;
217 struct ieee80211_tx_info *info;
218 struct txpd *txpd;
219 struct sk_buff *skb = NULL;
220 int err;
221
222 if ((priv->vif->type == IEEE80211_IF_TYPE_AP) &&
223 (!skb_queue_empty(&priv->bc_ps_buf)))
224 skb = skb_dequeue(&priv->bc_ps_buf);
225 else if (priv->skb_to_tx) {
226 skb = priv->skb_to_tx;
227 priv->skb_to_tx = NULL;
228 } else
229 return;
230
231 len = skb->len;
232 info = IEEE80211_SKB_CB(skb);
233 txpd = (struct txpd *) skb_push(skb, sizeof(struct txpd));
234
235 if (priv->surpriseremoved) {
236 dev_kfree_skb_any(skb);
237 return;
238 }
239
240 memset(txpd, 0, sizeof(struct txpd));
241 /* Activate per-packet rate selection */
242 txpd->tx_control |= cpu_to_le32(MRVL_PER_PACKET_RATE |
243 ieee80211_get_tx_rate(priv->hw, info)->hw_value);
244
245 /* copy destination address from 802.11 header */
246 memcpy(txpd->tx_dest_addr_high, skb->data + sizeof(struct txpd) + 4,
247 ETH_ALEN);
248 txpd->tx_packet_length = cpu_to_le16(len);
249 txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd));
250 BUG_ON(priv->tx_skb);
251 spin_lock_irq(&priv->driver_lock);
252 priv->tx_skb = skb;
253 err = priv->hw_host_to_card(priv, MVMS_DAT, skb->data, skb->len);
254 spin_unlock_irq(&priv->driver_lock);
255 if (err) {
256 dev_kfree_skb_any(skb);
257 priv->tx_skb = NULL;
258 }
259}
260
261static int lbtf_op_start(struct ieee80211_hw *hw)
262{
263 struct lbtf_private *priv = hw->priv;
264 void *card = priv->card;
265 int ret = -1;
266
267 if (!priv->fw_ready)
268 /* Upload firmware */
269 if (priv->hw_prog_firmware(card))
270 goto err_prog_firmware;
271
272 /* poke the firmware */
273 priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE;
274 priv->radioon = RADIO_ON;
275 priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON;
276 ret = lbtf_setup_firmware(priv);
277 if (ret)
278 goto err_prog_firmware;
279
280 if ((priv->fwrelease < LBTF_FW_VER_MIN) ||
281 (priv->fwrelease > LBTF_FW_VER_MAX)) {
282 ret = -1;
283 goto err_prog_firmware;
284 }
285
286 printk(KERN_INFO "libertastf: Marvell WLAN 802.11 thinfirm adapter\n");
287 return 0;
288
289err_prog_firmware:
290 priv->hw_reset_device(card);
291 return ret;
292}
293
294static void lbtf_op_stop(struct ieee80211_hw *hw)
295{
296 struct lbtf_private *priv = hw->priv;
297 unsigned long flags;
298 struct sk_buff *skb;
299
300 struct cmd_ctrl_node *cmdnode;
301 /* Flush pending command nodes */
302 spin_lock_irqsave(&priv->driver_lock, flags);
303 list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
304 cmdnode->result = -ENOENT;
305 cmdnode->cmdwaitqwoken = 1;
306 wake_up_interruptible(&cmdnode->cmdwait_q);
307 }
308
309 spin_unlock_irqrestore(&priv->driver_lock, flags);
310 cancel_work_sync(&priv->cmd_work);
311 cancel_work_sync(&priv->tx_work);
312 while ((skb = skb_dequeue(&priv->bc_ps_buf)))
313 dev_kfree_skb_any(skb);
314 priv->radioon = RADIO_OFF;
315 lbtf_set_radio_control(priv);
316
317 return;
318}
319
320static int lbtf_op_add_interface(struct ieee80211_hw *hw,
321 struct ieee80211_if_init_conf *conf)
322{
323 struct lbtf_private *priv = hw->priv;
324 if (priv->vif != NULL)
325 return -EOPNOTSUPP;
326
327 priv->vif = conf->vif;
328 switch (conf->type) {
329 case IEEE80211_IF_TYPE_MESH_POINT:
330 case IEEE80211_IF_TYPE_AP:
331 lbtf_set_mode(priv, LBTF_AP_MODE);
332 break;
333 case IEEE80211_IF_TYPE_STA:
334 lbtf_set_mode(priv, LBTF_STA_MODE);
335 break;
336 default:
337 priv->vif = NULL;
338 return -EOPNOTSUPP;
339 }
340 lbtf_set_mac_address(priv, (u8 *) conf->mac_addr);
341 return 0;
342}
343
344static void lbtf_op_remove_interface(struct ieee80211_hw *hw,
345 struct ieee80211_if_init_conf *conf)
346{
347 struct lbtf_private *priv = hw->priv;
348
349 if (priv->vif->type == IEEE80211_IF_TYPE_AP ||
350 priv->vif->type == IEEE80211_IF_TYPE_MESH_POINT)
351 lbtf_beacon_ctrl(priv, 0, 0);
352 lbtf_set_mode(priv, LBTF_PASSIVE_MODE);
353 lbtf_set_bssid(priv, 0, NULL);
354 priv->vif = NULL;
355}
356
357static int lbtf_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
358{
359 struct lbtf_private *priv = hw->priv;
360 if (conf->channel->center_freq != priv->cur_freq) {
361 priv->cur_freq = conf->channel->center_freq;
362 lbtf_set_channel(priv, conf->channel->hw_value);
363 }
364 return 0;
365}
366
367static int lbtf_op_config_interface(struct ieee80211_hw *hw,
368 struct ieee80211_vif *vif,
369 struct ieee80211_if_conf *conf)
370{
371 struct lbtf_private *priv = hw->priv;
372 struct sk_buff *beacon;
373
374 switch (priv->vif->type) {
375 case IEEE80211_IF_TYPE_AP:
376 case IEEE80211_IF_TYPE_MESH_POINT:
377 beacon = ieee80211_beacon_get(hw, vif);
378 if (beacon) {
379 lbtf_beacon_set(priv, beacon);
380 kfree_skb(beacon);
381 lbtf_beacon_ctrl(priv, 1, hw->conf.beacon_int);
382 }
383 break;
384 default:
385 break;
386 }
387
388 if (conf->bssid) {
389 u8 null_bssid[ETH_ALEN] = {0};
390 bool activate = compare_ether_addr(conf->bssid, null_bssid);
391 lbtf_set_bssid(priv, activate, conf->bssid);
392 }
393
394 return 0;
395}
396
397#define SUPPORTED_FIF_FLAGS (FIF_PROMISC_IN_BSS | FIF_ALLMULTI)
398static void lbtf_op_configure_filter(struct ieee80211_hw *hw,
399 unsigned int changed_flags,
400 unsigned int *new_flags,
401 int mc_count, struct dev_mc_list *mclist)
402{
403 struct lbtf_private *priv = hw->priv;
404 int old_mac_control = priv->mac_control;
405 int i;
406 changed_flags &= SUPPORTED_FIF_FLAGS;
407 *new_flags &= SUPPORTED_FIF_FLAGS;
408
409 if (!changed_flags)
410 return;
411
412 if (*new_flags & (FIF_PROMISC_IN_BSS))
413 priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE;
414 else
415 priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE;
416 if (*new_flags & (FIF_ALLMULTI) ||
417 mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) {
418 priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
419 priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE;
420 } else if (mc_count) {
421 priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE;
422 priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
423 priv->nr_of_multicastmacaddr = mc_count;
424 for (i = 0; i < mc_count; i++) {
425 if (!mclist)
426 break;
427 memcpy(&priv->multicastlist[i], mclist->da_addr,
428 ETH_ALEN);
429 mclist = mclist->next;
430 }
431 lbtf_cmd_set_mac_multicast_addr(priv);
432 } else {
433 priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE |
434 CMD_ACT_MAC_ALL_MULTICAST_ENABLE);
435 if (priv->nr_of_multicastmacaddr) {
436 priv->nr_of_multicastmacaddr = 0;
437 lbtf_cmd_set_mac_multicast_addr(priv);
438 }
439 }
440
441
442 if (priv->mac_control != old_mac_control)
443 lbtf_set_mac_control(priv);
444}
445
446static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw,
447 struct ieee80211_vif *vif,
448 struct ieee80211_bss_conf *bss_conf,
449 u32 changes)
450{
451 struct lbtf_private *priv = hw->priv;
452
453 if (changes & BSS_CHANGED_ERP_PREAMBLE) {
454 if (bss_conf->use_short_preamble)
455 priv->preamble = CMD_TYPE_SHORT_PREAMBLE;
456 else
457 priv->preamble = CMD_TYPE_LONG_PREAMBLE;
458 lbtf_set_radio_control(priv);
459 }
460
461 return;
462}
463
464static const struct ieee80211_ops lbtf_ops = {
465 .tx = lbtf_op_tx,
466 .start = lbtf_op_start,
467 .stop = lbtf_op_stop,
468 .add_interface = lbtf_op_add_interface,
469 .remove_interface = lbtf_op_remove_interface,
470 .config = lbtf_op_config,
471 .config_interface = lbtf_op_config_interface,
472 .configure_filter = lbtf_op_configure_filter,
473 .bss_info_changed = lbtf_op_bss_info_changed,
474};
475
476int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb)
477{
478 struct ieee80211_rx_status stats;
479 struct rxpd *prxpd;
480 bool is_qos, is_4addr, is_amsdu, need_padding;
481 unsigned int flags;
482 u16 fc, fc_le;
483
484 prxpd = (struct rxpd *) skb->data;
485
486 stats.flag = 0;
487 if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK)))
488 stats.flag |= RX_FLAG_FAILED_FCS_CRC;
489 stats.freq = priv->cur_freq;
490 stats.band = IEEE80211_BAND_2GHZ;
491 stats.signal = prxpd->snr;
492 stats.noise = prxpd->nf;
493 stats.qual = prxpd->snr - prxpd->nf;
494 /* Marvell rate index has a hole at value 4 */
495 if (prxpd->rx_rate > 4)
496 --prxpd->rx_rate;
497 stats.rate_idx = prxpd->rx_rate;
498 skb_pull(skb, sizeof(struct rxpd));
499
500 fc_le = *((__le16 *) skb->data);
501 fc = le16_to_cpu(fc_le);
502 flags = le32_to_cpu(*(__le32 *)(skb->data + 4));
503
504 is_qos = ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
505 (fc & IEEE80211_STYPE_QOS_DATA);
506 is_4addr = (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
507 (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS);
508 is_amsdu = ((fc & 0x8C) == 0x88) &&
509 (*(skb->data + ieee80211_hdrlen(fc_le) - QOS_CONTROL_LEN)
510 & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT);
511
512 need_padding = is_qos ^ is_4addr ^ is_amsdu;
513 if (need_padding) {
514 memmove(skb->data + 2, skb->data, skb->len);
515 skb_reserve(skb, 2);
516 }
517
518 ieee80211_rx_irqsafe(priv->hw, skb, &stats);
519 return 0;
520}
521EXPORT_SYMBOL_GPL(lbtf_rx);
522
523/**
524 * lbtf_add_card: Add and initialize the card, no fw upload yet.
525 *
526 * @card A pointer to card
527 *
528 * Returns: pointer to struct lbtf_priv.
529 */
530struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev)
531{
532 struct ieee80211_hw *hw;
533 struct lbtf_private *priv = NULL;
534
535 hw = ieee80211_alloc_hw(sizeof(struct lbtf_private), &lbtf_ops);
536 if (!hw)
537 goto done;
538
539 priv = hw->priv;
540 if (lbtf_init_adapter(priv))
541 goto err_init_adapter;
542
543 priv->hw = hw;
544 priv->card = card;
545 priv->tx_skb = NULL;
546
547 hw->queues = 1;
548 hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING;
549 hw->extra_tx_headroom = sizeof(struct txpd);
550 memcpy(priv->channels, lbtf_channels, sizeof(lbtf_channels));
551 memcpy(priv->rates, lbtf_rates, sizeof(lbtf_rates));
552 priv->band.n_bitrates = ARRAY_SIZE(lbtf_rates);
553 priv->band.bitrates = priv->rates;
554 priv->band.n_channels = ARRAY_SIZE(lbtf_channels);
555 priv->band.channels = priv->channels;
556 hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
557 skb_queue_head_init(&priv->bc_ps_buf);
558
559 SET_IEEE80211_DEV(hw, dmdev);
560
561 INIT_WORK(&priv->cmd_work, lbtf_cmd_work);
562 INIT_WORK(&priv->tx_work, lbtf_tx_work);
563 if (ieee80211_register_hw(hw))
564 goto err_init_adapter;
565
566 goto done;
567
568err_init_adapter:
569 lbtf_free_adapter(priv);
570 ieee80211_free_hw(hw);
571 priv = NULL;
572
573done:
574 return priv;
575}
576EXPORT_SYMBOL_GPL(lbtf_add_card);
577
578
579int lbtf_remove_card(struct lbtf_private *priv)
580{
581 struct ieee80211_hw *hw = priv->hw;
582
583 priv->surpriseremoved = 1;
584 del_timer(&priv->command_timer);
585 lbtf_free_adapter(priv);
586 priv->hw = NULL;
587 ieee80211_unregister_hw(hw);
588 ieee80211_free_hw(hw);
589
590 return 0;
591}
592EXPORT_SYMBOL_GPL(lbtf_remove_card);
593
594void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail)
595{
596 struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb);
597 memset(&info->status, 0, sizeof(info->status));
598 /*
599 * Commented out, otherwise we never go beyond 1Mbit/s using mac80211
600 * default pid rc algorithm.
601 *
602 * info->status.retry_count = MRVL_DEFAULT_RETRIES - retrycnt;
603 */
604 info->status.excessive_retries = fail ? 1 : 0;
605 if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !fail)
606 info->flags |= IEEE80211_TX_STAT_ACK;
607 skb_pull(priv->tx_skb, sizeof(struct txpd));
608 ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb);
609 priv->tx_skb = NULL;
610 if (!priv->skb_to_tx && skb_queue_empty(&priv->bc_ps_buf))
611 ieee80211_wake_queues(priv->hw);
612 else
613 queue_work(lbtf_wq, &priv->tx_work);
614}
615EXPORT_SYMBOL_GPL(lbtf_send_tx_feedback);
616
617void lbtf_bcn_sent(struct lbtf_private *priv)
618{
619 struct sk_buff *skb = NULL;
620
621 if (priv->vif->type != IEEE80211_IF_TYPE_AP)
622 return;
623
624 if (skb_queue_empty(&priv->bc_ps_buf)) {
625 bool tx_buff_bc = 0;
626
627 while ((skb = ieee80211_get_buffered_bc(priv->hw, priv->vif))) {
628 skb_queue_tail(&priv->bc_ps_buf, skb);
629 tx_buff_bc = 1;
630 }
631 if (tx_buff_bc) {
632 ieee80211_stop_queues(priv->hw);
633 queue_work(lbtf_wq, &priv->tx_work);
634 }
635 }
636
637 skb = ieee80211_beacon_get(priv->hw, priv->vif);
638
639 if (skb) {
640 lbtf_beacon_set(priv, skb);
641 kfree_skb(skb);
642 }
643}
644EXPORT_SYMBOL_GPL(lbtf_bcn_sent);
645
646static int __init lbtf_init_module(void)
647{
648 lbtf_wq = create_workqueue("libertastf");
649 if (lbtf_wq == NULL) {
650 printk(KERN_ERR "libertastf: couldn't create workqueue\n");
651 return -ENOMEM;
652 }
653 return 0;
654}
655
656static void __exit lbtf_exit_module(void)
657{
658 destroy_workqueue(lbtf_wq);
659}
660
661module_init(lbtf_init_module);
662module_exit(lbtf_exit_module);
663
664MODULE_DESCRIPTION("Libertas WLAN Thinfirm Driver Library");
665MODULE_AUTHOR("Cozybit Inc.");
666MODULE_LICENSE("GPL");