1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2008, cozybit Inc.
4*4882a593Smuzhiyun * Copyright (C) 2003-2006, Marvell International Ltd.
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/hardirq.h>
9*4882a593Smuzhiyun #include <linux/slab.h>
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/etherdevice.h>
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include "libertas_tf.h"
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun /* thinfirm version: 5.132.X.pX */
16*4882a593Smuzhiyun #define LBTF_FW_VER_MIN 0x05840300
17*4882a593Smuzhiyun #define LBTF_FW_VER_MAX 0x0584ffff
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun /* Module parameters */
20*4882a593Smuzhiyun unsigned int lbtf_debug;
21*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(lbtf_debug);
22*4882a593Smuzhiyun module_param_named(libertas_tf_debug, lbtf_debug, int, 0644);
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun struct workqueue_struct *lbtf_wq;
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun static const struct ieee80211_channel lbtf_channels[] = {
27*4882a593Smuzhiyun { .center_freq = 2412, .hw_value = 1 },
28*4882a593Smuzhiyun { .center_freq = 2417, .hw_value = 2 },
29*4882a593Smuzhiyun { .center_freq = 2422, .hw_value = 3 },
30*4882a593Smuzhiyun { .center_freq = 2427, .hw_value = 4 },
31*4882a593Smuzhiyun { .center_freq = 2432, .hw_value = 5 },
32*4882a593Smuzhiyun { .center_freq = 2437, .hw_value = 6 },
33*4882a593Smuzhiyun { .center_freq = 2442, .hw_value = 7 },
34*4882a593Smuzhiyun { .center_freq = 2447, .hw_value = 8 },
35*4882a593Smuzhiyun { .center_freq = 2452, .hw_value = 9 },
36*4882a593Smuzhiyun { .center_freq = 2457, .hw_value = 10 },
37*4882a593Smuzhiyun { .center_freq = 2462, .hw_value = 11 },
38*4882a593Smuzhiyun { .center_freq = 2467, .hw_value = 12 },
39*4882a593Smuzhiyun { .center_freq = 2472, .hw_value = 13 },
40*4882a593Smuzhiyun { .center_freq = 2484, .hw_value = 14 },
41*4882a593Smuzhiyun };
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun /* This table contains the hardware specific values for the modulation rates. */
44*4882a593Smuzhiyun static const struct ieee80211_rate lbtf_rates[] = {
45*4882a593Smuzhiyun { .bitrate = 10,
46*4882a593Smuzhiyun .hw_value = 0, },
47*4882a593Smuzhiyun { .bitrate = 20,
48*4882a593Smuzhiyun .hw_value = 1,
49*4882a593Smuzhiyun .flags = IEEE80211_RATE_SHORT_PREAMBLE },
50*4882a593Smuzhiyun { .bitrate = 55,
51*4882a593Smuzhiyun .hw_value = 2,
52*4882a593Smuzhiyun .flags = IEEE80211_RATE_SHORT_PREAMBLE },
53*4882a593Smuzhiyun { .bitrate = 110,
54*4882a593Smuzhiyun .hw_value = 3,
55*4882a593Smuzhiyun .flags = IEEE80211_RATE_SHORT_PREAMBLE },
56*4882a593Smuzhiyun { .bitrate = 60,
57*4882a593Smuzhiyun .hw_value = 5,
58*4882a593Smuzhiyun .flags = 0 },
59*4882a593Smuzhiyun { .bitrate = 90,
60*4882a593Smuzhiyun .hw_value = 6,
61*4882a593Smuzhiyun .flags = 0 },
62*4882a593Smuzhiyun { .bitrate = 120,
63*4882a593Smuzhiyun .hw_value = 7,
64*4882a593Smuzhiyun .flags = 0 },
65*4882a593Smuzhiyun { .bitrate = 180,
66*4882a593Smuzhiyun .hw_value = 8,
67*4882a593Smuzhiyun .flags = 0 },
68*4882a593Smuzhiyun { .bitrate = 240,
69*4882a593Smuzhiyun .hw_value = 9,
70*4882a593Smuzhiyun .flags = 0 },
71*4882a593Smuzhiyun { .bitrate = 360,
72*4882a593Smuzhiyun .hw_value = 10,
73*4882a593Smuzhiyun .flags = 0 },
74*4882a593Smuzhiyun { .bitrate = 480,
75*4882a593Smuzhiyun .hw_value = 11,
76*4882a593Smuzhiyun .flags = 0 },
77*4882a593Smuzhiyun { .bitrate = 540,
78*4882a593Smuzhiyun .hw_value = 12,
79*4882a593Smuzhiyun .flags = 0 },
80*4882a593Smuzhiyun };
81*4882a593Smuzhiyun
lbtf_cmd_work(struct work_struct * work)82*4882a593Smuzhiyun static void lbtf_cmd_work(struct work_struct *work)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun struct lbtf_private *priv = container_of(work, struct lbtf_private,
85*4882a593Smuzhiyun cmd_work);
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_CMD);
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun spin_lock_irq(&priv->driver_lock);
90*4882a593Smuzhiyun /* command response? */
91*4882a593Smuzhiyun if (priv->cmd_response_rxed) {
92*4882a593Smuzhiyun priv->cmd_response_rxed = 0;
93*4882a593Smuzhiyun spin_unlock_irq(&priv->driver_lock);
94*4882a593Smuzhiyun lbtf_process_rx_command(priv);
95*4882a593Smuzhiyun spin_lock_irq(&priv->driver_lock);
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun if (priv->cmd_timed_out && priv->cur_cmd) {
99*4882a593Smuzhiyun struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun if (++priv->nr_retries > 10) {
102*4882a593Smuzhiyun lbtf_complete_command(priv, cmdnode,
103*4882a593Smuzhiyun -ETIMEDOUT);
104*4882a593Smuzhiyun priv->nr_retries = 0;
105*4882a593Smuzhiyun } else {
106*4882a593Smuzhiyun priv->cur_cmd = NULL;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun /* Stick it back at the _top_ of the pending
109*4882a593Smuzhiyun * queue for immediate resubmission */
110*4882a593Smuzhiyun list_add(&cmdnode->list, &priv->cmdpendingq);
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun priv->cmd_timed_out = 0;
114*4882a593Smuzhiyun spin_unlock_irq(&priv->driver_lock);
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun /* Execute the next command */
117*4882a593Smuzhiyun if (!priv->cur_cmd)
118*4882a593Smuzhiyun lbtf_execute_next_command(priv);
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_CMD);
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun /*
124*4882a593Smuzhiyun * This function handles the timeout of command sending.
125*4882a593Smuzhiyun * It will re-send the same command again.
126*4882a593Smuzhiyun */
command_timer_fn(struct timer_list * t)127*4882a593Smuzhiyun static void command_timer_fn(struct timer_list *t)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun struct lbtf_private *priv = from_timer(priv, t, command_timer);
130*4882a593Smuzhiyun unsigned long flags;
131*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_CMD);
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun spin_lock_irqsave(&priv->driver_lock, flags);
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun if (!priv->cur_cmd) {
136*4882a593Smuzhiyun printk(KERN_DEBUG "libertastf: command timer expired; "
137*4882a593Smuzhiyun "no pending command\n");
138*4882a593Smuzhiyun goto out;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun printk(KERN_DEBUG "libertas: command %x timed out\n",
142*4882a593Smuzhiyun le16_to_cpu(priv->cur_cmd->cmdbuf->command));
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun priv->cmd_timed_out = 1;
145*4882a593Smuzhiyun queue_work(lbtf_wq, &priv->cmd_work);
146*4882a593Smuzhiyun out:
147*4882a593Smuzhiyun spin_unlock_irqrestore(&priv->driver_lock, flags);
148*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_CMD);
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
lbtf_init_adapter(struct lbtf_private * priv)151*4882a593Smuzhiyun static int lbtf_init_adapter(struct lbtf_private *priv)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_MAIN);
154*4882a593Smuzhiyun eth_broadcast_addr(priv->current_addr);
155*4882a593Smuzhiyun mutex_init(&priv->lock);
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun priv->vif = NULL;
158*4882a593Smuzhiyun timer_setup(&priv->command_timer, command_timer_fn, 0);
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun INIT_LIST_HEAD(&priv->cmdfreeq);
161*4882a593Smuzhiyun INIT_LIST_HEAD(&priv->cmdpendingq);
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun spin_lock_init(&priv->driver_lock);
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun /* Allocate the command buffers */
166*4882a593Smuzhiyun if (lbtf_allocate_cmd_buffer(priv))
167*4882a593Smuzhiyun return -1;
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MAIN);
170*4882a593Smuzhiyun return 0;
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun
lbtf_free_adapter(struct lbtf_private * priv)173*4882a593Smuzhiyun static void lbtf_free_adapter(struct lbtf_private *priv)
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_MAIN);
176*4882a593Smuzhiyun lbtf_free_cmd_buffer(priv);
177*4882a593Smuzhiyun del_timer(&priv->command_timer);
178*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MAIN);
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
lbtf_op_tx(struct ieee80211_hw * hw,struct ieee80211_tx_control * control,struct sk_buff * skb)181*4882a593Smuzhiyun static void lbtf_op_tx(struct ieee80211_hw *hw,
182*4882a593Smuzhiyun struct ieee80211_tx_control *control,
183*4882a593Smuzhiyun struct sk_buff *skb)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun struct lbtf_private *priv = hw->priv;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun priv->skb_to_tx = skb;
188*4882a593Smuzhiyun queue_work(lbtf_wq, &priv->tx_work);
189*4882a593Smuzhiyun /*
190*4882a593Smuzhiyun * queue will be restarted when we receive transmission feedback if
191*4882a593Smuzhiyun * there are no buffered multicast frames to send
192*4882a593Smuzhiyun */
193*4882a593Smuzhiyun ieee80211_stop_queues(priv->hw);
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun
lbtf_tx_work(struct work_struct * work)196*4882a593Smuzhiyun static void lbtf_tx_work(struct work_struct *work)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun struct lbtf_private *priv = container_of(work, struct lbtf_private,
199*4882a593Smuzhiyun tx_work);
200*4882a593Smuzhiyun unsigned int len;
201*4882a593Smuzhiyun struct ieee80211_tx_info *info;
202*4882a593Smuzhiyun struct txpd *txpd;
203*4882a593Smuzhiyun struct sk_buff *skb = NULL;
204*4882a593Smuzhiyun int err;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_MACOPS | LBTF_DEB_TX);
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun if ((priv->vif->type == NL80211_IFTYPE_AP) &&
209*4882a593Smuzhiyun (!skb_queue_empty(&priv->bc_ps_buf)))
210*4882a593Smuzhiyun skb = skb_dequeue(&priv->bc_ps_buf);
211*4882a593Smuzhiyun else if (priv->skb_to_tx) {
212*4882a593Smuzhiyun skb = priv->skb_to_tx;
213*4882a593Smuzhiyun priv->skb_to_tx = NULL;
214*4882a593Smuzhiyun } else {
215*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);
216*4882a593Smuzhiyun return;
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun len = skb->len;
220*4882a593Smuzhiyun info = IEEE80211_SKB_CB(skb);
221*4882a593Smuzhiyun txpd = skb_push(skb, sizeof(struct txpd));
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun if (priv->surpriseremoved) {
224*4882a593Smuzhiyun dev_kfree_skb_any(skb);
225*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);
226*4882a593Smuzhiyun return;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun memset(txpd, 0, sizeof(struct txpd));
230*4882a593Smuzhiyun /* Activate per-packet rate selection */
231*4882a593Smuzhiyun txpd->tx_control |= cpu_to_le32(MRVL_PER_PACKET_RATE |
232*4882a593Smuzhiyun ieee80211_get_tx_rate(priv->hw, info)->hw_value);
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun /* copy destination address from 802.11 header */
235*4882a593Smuzhiyun memcpy(txpd->tx_dest_addr_high, skb->data + sizeof(struct txpd) + 4,
236*4882a593Smuzhiyun ETH_ALEN);
237*4882a593Smuzhiyun txpd->tx_packet_length = cpu_to_le16(len);
238*4882a593Smuzhiyun txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd));
239*4882a593Smuzhiyun lbtf_deb_hex(LBTF_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100));
240*4882a593Smuzhiyun BUG_ON(priv->tx_skb);
241*4882a593Smuzhiyun spin_lock_irq(&priv->driver_lock);
242*4882a593Smuzhiyun priv->tx_skb = skb;
243*4882a593Smuzhiyun err = priv->ops->hw_host_to_card(priv, MVMS_DAT, skb->data, skb->len);
244*4882a593Smuzhiyun spin_unlock_irq(&priv->driver_lock);
245*4882a593Smuzhiyun if (err) {
246*4882a593Smuzhiyun dev_kfree_skb_any(skb);
247*4882a593Smuzhiyun priv->tx_skb = NULL;
248*4882a593Smuzhiyun pr_err("TX error: %d", err);
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun
lbtf_op_start(struct ieee80211_hw * hw)253*4882a593Smuzhiyun static int lbtf_op_start(struct ieee80211_hw *hw)
254*4882a593Smuzhiyun {
255*4882a593Smuzhiyun struct lbtf_private *priv = hw->priv;
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_MACOPS);
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE;
260*4882a593Smuzhiyun priv->radioon = RADIO_ON;
261*4882a593Smuzhiyun priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON;
262*4882a593Smuzhiyun lbtf_set_mac_control(priv);
263*4882a593Smuzhiyun lbtf_set_radio_control(priv);
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MACOPS);
266*4882a593Smuzhiyun return 0;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun
lbtf_op_stop(struct ieee80211_hw * hw)269*4882a593Smuzhiyun static void lbtf_op_stop(struct ieee80211_hw *hw)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun struct lbtf_private *priv = hw->priv;
272*4882a593Smuzhiyun unsigned long flags;
273*4882a593Smuzhiyun struct sk_buff *skb;
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun struct cmd_ctrl_node *cmdnode;
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_MACOPS);
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun /* Flush pending command nodes */
280*4882a593Smuzhiyun spin_lock_irqsave(&priv->driver_lock, flags);
281*4882a593Smuzhiyun list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
282*4882a593Smuzhiyun cmdnode->result = -ENOENT;
283*4882a593Smuzhiyun cmdnode->cmdwaitqwoken = 1;
284*4882a593Smuzhiyun wake_up_interruptible(&cmdnode->cmdwait_q);
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun spin_unlock_irqrestore(&priv->driver_lock, flags);
288*4882a593Smuzhiyun cancel_work_sync(&priv->cmd_work);
289*4882a593Smuzhiyun cancel_work_sync(&priv->tx_work);
290*4882a593Smuzhiyun while ((skb = skb_dequeue(&priv->bc_ps_buf)))
291*4882a593Smuzhiyun dev_kfree_skb_any(skb);
292*4882a593Smuzhiyun priv->radioon = RADIO_OFF;
293*4882a593Smuzhiyun lbtf_set_radio_control(priv);
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MACOPS);
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun
lbtf_op_add_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)298*4882a593Smuzhiyun static int lbtf_op_add_interface(struct ieee80211_hw *hw,
299*4882a593Smuzhiyun struct ieee80211_vif *vif)
300*4882a593Smuzhiyun {
301*4882a593Smuzhiyun struct lbtf_private *priv = hw->priv;
302*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_MACOPS);
303*4882a593Smuzhiyun if (priv->vif != NULL)
304*4882a593Smuzhiyun return -EOPNOTSUPP;
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun priv->vif = vif;
307*4882a593Smuzhiyun switch (vif->type) {
308*4882a593Smuzhiyun case NL80211_IFTYPE_MESH_POINT:
309*4882a593Smuzhiyun case NL80211_IFTYPE_AP:
310*4882a593Smuzhiyun lbtf_set_mode(priv, LBTF_AP_MODE);
311*4882a593Smuzhiyun break;
312*4882a593Smuzhiyun case NL80211_IFTYPE_STATION:
313*4882a593Smuzhiyun lbtf_set_mode(priv, LBTF_STA_MODE);
314*4882a593Smuzhiyun break;
315*4882a593Smuzhiyun default:
316*4882a593Smuzhiyun priv->vif = NULL;
317*4882a593Smuzhiyun return -EOPNOTSUPP;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun lbtf_set_mac_address(priv, (u8 *) vif->addr);
320*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MACOPS);
321*4882a593Smuzhiyun return 0;
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun
lbtf_op_remove_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)324*4882a593Smuzhiyun static void lbtf_op_remove_interface(struct ieee80211_hw *hw,
325*4882a593Smuzhiyun struct ieee80211_vif *vif)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun struct lbtf_private *priv = hw->priv;
328*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_MACOPS);
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun if (priv->vif->type == NL80211_IFTYPE_AP ||
331*4882a593Smuzhiyun priv->vif->type == NL80211_IFTYPE_MESH_POINT)
332*4882a593Smuzhiyun lbtf_beacon_ctrl(priv, 0, 0);
333*4882a593Smuzhiyun lbtf_set_mode(priv, LBTF_PASSIVE_MODE);
334*4882a593Smuzhiyun lbtf_set_bssid(priv, 0, NULL);
335*4882a593Smuzhiyun priv->vif = NULL;
336*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MACOPS);
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun
lbtf_op_config(struct ieee80211_hw * hw,u32 changed)339*4882a593Smuzhiyun static int lbtf_op_config(struct ieee80211_hw *hw, u32 changed)
340*4882a593Smuzhiyun {
341*4882a593Smuzhiyun struct lbtf_private *priv = hw->priv;
342*4882a593Smuzhiyun struct ieee80211_conf *conf = &hw->conf;
343*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_MACOPS);
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun if (conf->chandef.chan->center_freq != priv->cur_freq) {
346*4882a593Smuzhiyun priv->cur_freq = conf->chandef.chan->center_freq;
347*4882a593Smuzhiyun lbtf_set_channel(priv, conf->chandef.chan->hw_value);
348*4882a593Smuzhiyun }
349*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MACOPS);
350*4882a593Smuzhiyun return 0;
351*4882a593Smuzhiyun }
352*4882a593Smuzhiyun
lbtf_op_prepare_multicast(struct ieee80211_hw * hw,struct netdev_hw_addr_list * mc_list)353*4882a593Smuzhiyun static u64 lbtf_op_prepare_multicast(struct ieee80211_hw *hw,
354*4882a593Smuzhiyun struct netdev_hw_addr_list *mc_list)
355*4882a593Smuzhiyun {
356*4882a593Smuzhiyun struct lbtf_private *priv = hw->priv;
357*4882a593Smuzhiyun int i;
358*4882a593Smuzhiyun struct netdev_hw_addr *ha;
359*4882a593Smuzhiyun int mc_count = netdev_hw_addr_list_count(mc_list);
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun if (!mc_count || mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE)
362*4882a593Smuzhiyun return mc_count;
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun priv->nr_of_multicastmacaddr = mc_count;
365*4882a593Smuzhiyun i = 0;
366*4882a593Smuzhiyun netdev_hw_addr_list_for_each(ha, mc_list)
367*4882a593Smuzhiyun memcpy(&priv->multicastlist[i++], ha->addr, ETH_ALEN);
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun return mc_count;
370*4882a593Smuzhiyun }
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun #define SUPPORTED_FIF_FLAGS FIF_ALLMULTI
lbtf_op_configure_filter(struct ieee80211_hw * hw,unsigned int changed_flags,unsigned int * new_flags,u64 multicast)373*4882a593Smuzhiyun static void lbtf_op_configure_filter(struct ieee80211_hw *hw,
374*4882a593Smuzhiyun unsigned int changed_flags,
375*4882a593Smuzhiyun unsigned int *new_flags,
376*4882a593Smuzhiyun u64 multicast)
377*4882a593Smuzhiyun {
378*4882a593Smuzhiyun struct lbtf_private *priv = hw->priv;
379*4882a593Smuzhiyun int old_mac_control = priv->mac_control;
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_MACOPS);
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun changed_flags &= SUPPORTED_FIF_FLAGS;
384*4882a593Smuzhiyun *new_flags &= SUPPORTED_FIF_FLAGS;
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun if (!changed_flags) {
387*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MACOPS);
388*4882a593Smuzhiyun return;
389*4882a593Smuzhiyun }
390*4882a593Smuzhiyun
391*4882a593Smuzhiyun priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE;
392*4882a593Smuzhiyun if (*new_flags & (FIF_ALLMULTI) ||
393*4882a593Smuzhiyun multicast > MRVDRV_MAX_MULTICAST_LIST_SIZE) {
394*4882a593Smuzhiyun priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
395*4882a593Smuzhiyun priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE;
396*4882a593Smuzhiyun } else if (multicast) {
397*4882a593Smuzhiyun priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE;
398*4882a593Smuzhiyun priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
399*4882a593Smuzhiyun lbtf_cmd_set_mac_multicast_addr(priv);
400*4882a593Smuzhiyun } else {
401*4882a593Smuzhiyun priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE |
402*4882a593Smuzhiyun CMD_ACT_MAC_ALL_MULTICAST_ENABLE);
403*4882a593Smuzhiyun if (priv->nr_of_multicastmacaddr) {
404*4882a593Smuzhiyun priv->nr_of_multicastmacaddr = 0;
405*4882a593Smuzhiyun lbtf_cmd_set_mac_multicast_addr(priv);
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun
410*4882a593Smuzhiyun if (priv->mac_control != old_mac_control)
411*4882a593Smuzhiyun lbtf_set_mac_control(priv);
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MACOPS);
414*4882a593Smuzhiyun }
415*4882a593Smuzhiyun
lbtf_op_bss_info_changed(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * bss_conf,u32 changes)416*4882a593Smuzhiyun static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw,
417*4882a593Smuzhiyun struct ieee80211_vif *vif,
418*4882a593Smuzhiyun struct ieee80211_bss_conf *bss_conf,
419*4882a593Smuzhiyun u32 changes)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun struct lbtf_private *priv = hw->priv;
422*4882a593Smuzhiyun struct sk_buff *beacon;
423*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_MACOPS);
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_INT)) {
426*4882a593Smuzhiyun switch (priv->vif->type) {
427*4882a593Smuzhiyun case NL80211_IFTYPE_AP:
428*4882a593Smuzhiyun case NL80211_IFTYPE_MESH_POINT:
429*4882a593Smuzhiyun beacon = ieee80211_beacon_get(hw, vif);
430*4882a593Smuzhiyun if (beacon) {
431*4882a593Smuzhiyun lbtf_beacon_set(priv, beacon);
432*4882a593Smuzhiyun kfree_skb(beacon);
433*4882a593Smuzhiyun lbtf_beacon_ctrl(priv, 1,
434*4882a593Smuzhiyun bss_conf->beacon_int);
435*4882a593Smuzhiyun }
436*4882a593Smuzhiyun break;
437*4882a593Smuzhiyun default:
438*4882a593Smuzhiyun break;
439*4882a593Smuzhiyun }
440*4882a593Smuzhiyun }
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun if (changes & BSS_CHANGED_BSSID) {
443*4882a593Smuzhiyun bool activate = !is_zero_ether_addr(bss_conf->bssid);
444*4882a593Smuzhiyun lbtf_set_bssid(priv, activate, bss_conf->bssid);
445*4882a593Smuzhiyun }
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun if (changes & BSS_CHANGED_ERP_PREAMBLE) {
448*4882a593Smuzhiyun if (bss_conf->use_short_preamble)
449*4882a593Smuzhiyun priv->preamble = CMD_TYPE_SHORT_PREAMBLE;
450*4882a593Smuzhiyun else
451*4882a593Smuzhiyun priv->preamble = CMD_TYPE_LONG_PREAMBLE;
452*4882a593Smuzhiyun lbtf_set_radio_control(priv);
453*4882a593Smuzhiyun }
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MACOPS);
456*4882a593Smuzhiyun }
457*4882a593Smuzhiyun
lbtf_op_get_survey(struct ieee80211_hw * hw,int idx,struct survey_info * survey)458*4882a593Smuzhiyun static int lbtf_op_get_survey(struct ieee80211_hw *hw, int idx,
459*4882a593Smuzhiyun struct survey_info *survey)
460*4882a593Smuzhiyun {
461*4882a593Smuzhiyun struct lbtf_private *priv = hw->priv;
462*4882a593Smuzhiyun struct ieee80211_conf *conf = &hw->conf;
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun if (idx != 0)
465*4882a593Smuzhiyun return -ENOENT;
466*4882a593Smuzhiyun
467*4882a593Smuzhiyun survey->channel = conf->chandef.chan;
468*4882a593Smuzhiyun survey->filled = SURVEY_INFO_NOISE_DBM;
469*4882a593Smuzhiyun survey->noise = priv->noise;
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun return 0;
472*4882a593Smuzhiyun }
473*4882a593Smuzhiyun
474*4882a593Smuzhiyun static const struct ieee80211_ops lbtf_ops = {
475*4882a593Smuzhiyun .tx = lbtf_op_tx,
476*4882a593Smuzhiyun .start = lbtf_op_start,
477*4882a593Smuzhiyun .stop = lbtf_op_stop,
478*4882a593Smuzhiyun .add_interface = lbtf_op_add_interface,
479*4882a593Smuzhiyun .remove_interface = lbtf_op_remove_interface,
480*4882a593Smuzhiyun .config = lbtf_op_config,
481*4882a593Smuzhiyun .prepare_multicast = lbtf_op_prepare_multicast,
482*4882a593Smuzhiyun .configure_filter = lbtf_op_configure_filter,
483*4882a593Smuzhiyun .bss_info_changed = lbtf_op_bss_info_changed,
484*4882a593Smuzhiyun .get_survey = lbtf_op_get_survey,
485*4882a593Smuzhiyun };
486*4882a593Smuzhiyun
lbtf_rx(struct lbtf_private * priv,struct sk_buff * skb)487*4882a593Smuzhiyun int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb)
488*4882a593Smuzhiyun {
489*4882a593Smuzhiyun struct ieee80211_rx_status stats;
490*4882a593Smuzhiyun struct rxpd *prxpd;
491*4882a593Smuzhiyun int need_padding;
492*4882a593Smuzhiyun struct ieee80211_hdr *hdr;
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_RX);
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun if (priv->radioon != RADIO_ON) {
497*4882a593Smuzhiyun lbtf_deb_rx("rx before we turned on the radio");
498*4882a593Smuzhiyun goto done;
499*4882a593Smuzhiyun }
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun prxpd = (struct rxpd *) skb->data;
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun memset(&stats, 0, sizeof(stats));
504*4882a593Smuzhiyun if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK)))
505*4882a593Smuzhiyun stats.flag |= RX_FLAG_FAILED_FCS_CRC;
506*4882a593Smuzhiyun stats.freq = priv->cur_freq;
507*4882a593Smuzhiyun stats.band = NL80211_BAND_2GHZ;
508*4882a593Smuzhiyun stats.signal = prxpd->snr - prxpd->nf;
509*4882a593Smuzhiyun priv->noise = prxpd->nf;
510*4882a593Smuzhiyun /* Marvell rate index has a hole at value 4 */
511*4882a593Smuzhiyun if (prxpd->rx_rate > 4)
512*4882a593Smuzhiyun --prxpd->rx_rate;
513*4882a593Smuzhiyun stats.rate_idx = prxpd->rx_rate;
514*4882a593Smuzhiyun skb_pull(skb, sizeof(struct rxpd));
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun hdr = (struct ieee80211_hdr *)skb->data;
517*4882a593Smuzhiyun
518*4882a593Smuzhiyun need_padding = ieee80211_is_data_qos(hdr->frame_control);
519*4882a593Smuzhiyun need_padding ^= ieee80211_has_a4(hdr->frame_control);
520*4882a593Smuzhiyun need_padding ^= ieee80211_is_data_qos(hdr->frame_control) &&
521*4882a593Smuzhiyun (*ieee80211_get_qos_ctl(hdr) &
522*4882a593Smuzhiyun IEEE80211_QOS_CTL_A_MSDU_PRESENT);
523*4882a593Smuzhiyun
524*4882a593Smuzhiyun if (need_padding) {
525*4882a593Smuzhiyun memmove(skb->data + 2, skb->data, skb->len);
526*4882a593Smuzhiyun skb_reserve(skb, 2);
527*4882a593Smuzhiyun }
528*4882a593Smuzhiyun
529*4882a593Smuzhiyun memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
530*4882a593Smuzhiyun
531*4882a593Smuzhiyun lbtf_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n",
532*4882a593Smuzhiyun skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd));
533*4882a593Smuzhiyun lbtf_deb_hex(LBTF_DEB_RX, "RX Data", skb->data,
534*4882a593Smuzhiyun min_t(unsigned int, skb->len, 100));
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun ieee80211_rx_irqsafe(priv->hw, skb);
537*4882a593Smuzhiyun
538*4882a593Smuzhiyun done:
539*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_RX);
540*4882a593Smuzhiyun return 0;
541*4882a593Smuzhiyun }
542*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(lbtf_rx);
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun /*
545*4882a593Smuzhiyun * lbtf_add_card: Add and initialize the card.
546*4882a593Smuzhiyun *
547*4882a593Smuzhiyun * Returns: pointer to struct lbtf_priv.
548*4882a593Smuzhiyun */
lbtf_add_card(void * card,struct device * dmdev,const struct lbtf_ops * ops)549*4882a593Smuzhiyun struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev,
550*4882a593Smuzhiyun const struct lbtf_ops *ops)
551*4882a593Smuzhiyun {
552*4882a593Smuzhiyun struct ieee80211_hw *hw;
553*4882a593Smuzhiyun struct lbtf_private *priv = NULL;
554*4882a593Smuzhiyun
555*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_MAIN);
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun hw = ieee80211_alloc_hw(sizeof(struct lbtf_private), &lbtf_ops);
558*4882a593Smuzhiyun if (!hw)
559*4882a593Smuzhiyun goto done;
560*4882a593Smuzhiyun
561*4882a593Smuzhiyun priv = hw->priv;
562*4882a593Smuzhiyun if (lbtf_init_adapter(priv))
563*4882a593Smuzhiyun goto err_init_adapter;
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun priv->hw = hw;
566*4882a593Smuzhiyun priv->card = card;
567*4882a593Smuzhiyun priv->ops = ops;
568*4882a593Smuzhiyun priv->tx_skb = NULL;
569*4882a593Smuzhiyun priv->radioon = RADIO_OFF;
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun hw->queues = 1;
572*4882a593Smuzhiyun ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
573*4882a593Smuzhiyun ieee80211_hw_set(hw, SIGNAL_DBM);
574*4882a593Smuzhiyun hw->extra_tx_headroom = sizeof(struct txpd);
575*4882a593Smuzhiyun memcpy(priv->channels, lbtf_channels, sizeof(lbtf_channels));
576*4882a593Smuzhiyun memcpy(priv->rates, lbtf_rates, sizeof(lbtf_rates));
577*4882a593Smuzhiyun priv->band.n_bitrates = ARRAY_SIZE(lbtf_rates);
578*4882a593Smuzhiyun priv->band.bitrates = priv->rates;
579*4882a593Smuzhiyun priv->band.n_channels = ARRAY_SIZE(lbtf_channels);
580*4882a593Smuzhiyun priv->band.channels = priv->channels;
581*4882a593Smuzhiyun hw->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band;
582*4882a593Smuzhiyun hw->wiphy->interface_modes =
583*4882a593Smuzhiyun BIT(NL80211_IFTYPE_STATION) |
584*4882a593Smuzhiyun BIT(NL80211_IFTYPE_ADHOC);
585*4882a593Smuzhiyun skb_queue_head_init(&priv->bc_ps_buf);
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
588*4882a593Smuzhiyun
589*4882a593Smuzhiyun SET_IEEE80211_DEV(hw, dmdev);
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun INIT_WORK(&priv->cmd_work, lbtf_cmd_work);
592*4882a593Smuzhiyun INIT_WORK(&priv->tx_work, lbtf_tx_work);
593*4882a593Smuzhiyun
594*4882a593Smuzhiyun if (priv->ops->hw_prog_firmware(priv)) {
595*4882a593Smuzhiyun lbtf_deb_usbd(dmdev, "Error programming the firmware\n");
596*4882a593Smuzhiyun priv->ops->hw_reset_device(priv);
597*4882a593Smuzhiyun goto err_init_adapter;
598*4882a593Smuzhiyun }
599*4882a593Smuzhiyun
600*4882a593Smuzhiyun eth_broadcast_addr(priv->current_addr);
601*4882a593Smuzhiyun if (lbtf_update_hw_spec(priv))
602*4882a593Smuzhiyun goto err_init_adapter;
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun if (priv->fwrelease < LBTF_FW_VER_MIN ||
605*4882a593Smuzhiyun priv->fwrelease > LBTF_FW_VER_MAX) {
606*4882a593Smuzhiyun goto err_init_adapter;
607*4882a593Smuzhiyun }
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun /* The firmware seems to start with the radio enabled. Turn it
610*4882a593Smuzhiyun * off before an actual mac80211 start callback is invoked.
611*4882a593Smuzhiyun */
612*4882a593Smuzhiyun lbtf_set_radio_control(priv);
613*4882a593Smuzhiyun
614*4882a593Smuzhiyun if (ieee80211_register_hw(hw))
615*4882a593Smuzhiyun goto err_init_adapter;
616*4882a593Smuzhiyun
617*4882a593Smuzhiyun dev_info(dmdev, "libertastf: Marvell WLAN 802.11 thinfirm adapter\n");
618*4882a593Smuzhiyun goto done;
619*4882a593Smuzhiyun
620*4882a593Smuzhiyun err_init_adapter:
621*4882a593Smuzhiyun lbtf_free_adapter(priv);
622*4882a593Smuzhiyun ieee80211_free_hw(hw);
623*4882a593Smuzhiyun priv = NULL;
624*4882a593Smuzhiyun
625*4882a593Smuzhiyun done:
626*4882a593Smuzhiyun lbtf_deb_leave_args(LBTF_DEB_MAIN, "priv %p", priv);
627*4882a593Smuzhiyun return priv;
628*4882a593Smuzhiyun }
629*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(lbtf_add_card);
630*4882a593Smuzhiyun
631*4882a593Smuzhiyun
lbtf_remove_card(struct lbtf_private * priv)632*4882a593Smuzhiyun int lbtf_remove_card(struct lbtf_private *priv)
633*4882a593Smuzhiyun {
634*4882a593Smuzhiyun struct ieee80211_hw *hw = priv->hw;
635*4882a593Smuzhiyun
636*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_MAIN);
637*4882a593Smuzhiyun
638*4882a593Smuzhiyun priv->surpriseremoved = 1;
639*4882a593Smuzhiyun del_timer(&priv->command_timer);
640*4882a593Smuzhiyun lbtf_free_adapter(priv);
641*4882a593Smuzhiyun priv->hw = NULL;
642*4882a593Smuzhiyun ieee80211_unregister_hw(hw);
643*4882a593Smuzhiyun ieee80211_free_hw(hw);
644*4882a593Smuzhiyun
645*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MAIN);
646*4882a593Smuzhiyun return 0;
647*4882a593Smuzhiyun }
648*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(lbtf_remove_card);
649*4882a593Smuzhiyun
lbtf_send_tx_feedback(struct lbtf_private * priv,u8 retrycnt,u8 fail)650*4882a593Smuzhiyun void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail)
651*4882a593Smuzhiyun {
652*4882a593Smuzhiyun struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb);
653*4882a593Smuzhiyun
654*4882a593Smuzhiyun ieee80211_tx_info_clear_status(info);
655*4882a593Smuzhiyun /*
656*4882a593Smuzhiyun * Commented out, otherwise we never go beyond 1Mbit/s using mac80211
657*4882a593Smuzhiyun * default pid rc algorithm.
658*4882a593Smuzhiyun *
659*4882a593Smuzhiyun * info->status.retry_count = MRVL_DEFAULT_RETRIES - retrycnt;
660*4882a593Smuzhiyun */
661*4882a593Smuzhiyun if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !fail)
662*4882a593Smuzhiyun info->flags |= IEEE80211_TX_STAT_ACK;
663*4882a593Smuzhiyun skb_pull(priv->tx_skb, sizeof(struct txpd));
664*4882a593Smuzhiyun ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb);
665*4882a593Smuzhiyun priv->tx_skb = NULL;
666*4882a593Smuzhiyun if (!priv->skb_to_tx && skb_queue_empty(&priv->bc_ps_buf))
667*4882a593Smuzhiyun ieee80211_wake_queues(priv->hw);
668*4882a593Smuzhiyun else
669*4882a593Smuzhiyun queue_work(lbtf_wq, &priv->tx_work);
670*4882a593Smuzhiyun }
671*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(lbtf_send_tx_feedback);
672*4882a593Smuzhiyun
lbtf_bcn_sent(struct lbtf_private * priv)673*4882a593Smuzhiyun void lbtf_bcn_sent(struct lbtf_private *priv)
674*4882a593Smuzhiyun {
675*4882a593Smuzhiyun struct sk_buff *skb = NULL;
676*4882a593Smuzhiyun
677*4882a593Smuzhiyun if (priv->vif->type != NL80211_IFTYPE_AP)
678*4882a593Smuzhiyun return;
679*4882a593Smuzhiyun
680*4882a593Smuzhiyun if (skb_queue_empty(&priv->bc_ps_buf)) {
681*4882a593Smuzhiyun bool tx_buff_bc = false;
682*4882a593Smuzhiyun
683*4882a593Smuzhiyun while ((skb = ieee80211_get_buffered_bc(priv->hw, priv->vif))) {
684*4882a593Smuzhiyun skb_queue_tail(&priv->bc_ps_buf, skb);
685*4882a593Smuzhiyun tx_buff_bc = true;
686*4882a593Smuzhiyun }
687*4882a593Smuzhiyun if (tx_buff_bc) {
688*4882a593Smuzhiyun ieee80211_stop_queues(priv->hw);
689*4882a593Smuzhiyun queue_work(lbtf_wq, &priv->tx_work);
690*4882a593Smuzhiyun }
691*4882a593Smuzhiyun }
692*4882a593Smuzhiyun
693*4882a593Smuzhiyun skb = ieee80211_beacon_get(priv->hw, priv->vif);
694*4882a593Smuzhiyun
695*4882a593Smuzhiyun if (skb) {
696*4882a593Smuzhiyun lbtf_beacon_set(priv, skb);
697*4882a593Smuzhiyun kfree_skb(skb);
698*4882a593Smuzhiyun }
699*4882a593Smuzhiyun }
700*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(lbtf_bcn_sent);
701*4882a593Smuzhiyun
lbtf_init_module(void)702*4882a593Smuzhiyun static int __init lbtf_init_module(void)
703*4882a593Smuzhiyun {
704*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_MAIN);
705*4882a593Smuzhiyun lbtf_wq = alloc_workqueue("libertastf", WQ_MEM_RECLAIM, 0);
706*4882a593Smuzhiyun if (lbtf_wq == NULL) {
707*4882a593Smuzhiyun printk(KERN_ERR "libertastf: couldn't create workqueue\n");
708*4882a593Smuzhiyun return -ENOMEM;
709*4882a593Smuzhiyun }
710*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MAIN);
711*4882a593Smuzhiyun return 0;
712*4882a593Smuzhiyun }
713*4882a593Smuzhiyun
lbtf_exit_module(void)714*4882a593Smuzhiyun static void __exit lbtf_exit_module(void)
715*4882a593Smuzhiyun {
716*4882a593Smuzhiyun lbtf_deb_enter(LBTF_DEB_MAIN);
717*4882a593Smuzhiyun destroy_workqueue(lbtf_wq);
718*4882a593Smuzhiyun lbtf_deb_leave(LBTF_DEB_MAIN);
719*4882a593Smuzhiyun }
720*4882a593Smuzhiyun
721*4882a593Smuzhiyun module_init(lbtf_init_module);
722*4882a593Smuzhiyun module_exit(lbtf_exit_module);
723*4882a593Smuzhiyun
724*4882a593Smuzhiyun MODULE_DESCRIPTION("Libertas WLAN Thinfirm Driver Library");
725*4882a593Smuzhiyun MODULE_AUTHOR("Cozybit Inc.");
726*4882a593Smuzhiyun MODULE_LICENSE("GPL");
727