xref: /linux/drivers/net/wireless/marvell/libertas_tf/main.c (revision 9187210eee7d87eea37b45ea93454a88681894a4)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
206b16ae5SLuis Carlos Cobo /*
306b16ae5SLuis Carlos Cobo  *  Copyright (C) 2008, cozybit Inc.
406b16ae5SLuis Carlos Cobo  *  Copyright (C) 2003-2006, Marvell International Ltd.
506b16ae5SLuis Carlos Cobo  */
6edfcba15SJohn W. Linville #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
7edfcba15SJohn W. Linville 
8a6b7a407SAlexey Dobriyan #include <linux/hardirq.h>
95a0e3ad6STejun Heo #include <linux/slab.h>
105a0e3ad6STejun Heo 
11edfcba15SJohn W. Linville #include <linux/etherdevice.h>
129d9779e7SPaul Gortmaker #include <linux/module.h>
1306b16ae5SLuis Carlos Cobo #include "libertas_tf.h"
1406b16ae5SLuis Carlos Cobo 
1506b16ae5SLuis Carlos Cobo /* thinfirm version: 5.132.X.pX */
1606b16ae5SLuis Carlos Cobo #define LBTF_FW_VER_MIN		0x05840300
1706b16ae5SLuis Carlos Cobo #define LBTF_FW_VER_MAX		0x0584ffff
1806b16ae5SLuis Carlos Cobo 
19e9bd5bcdSSteve deRosier /* Module parameters */
20e9bd5bcdSSteve deRosier unsigned int lbtf_debug;
21e9bd5bcdSSteve deRosier EXPORT_SYMBOL_GPL(lbtf_debug);
22e9bd5bcdSSteve deRosier module_param_named(libertas_tf_debug, lbtf_debug, int, 0644);
23e9bd5bcdSSteve deRosier 
2406b16ae5SLuis Carlos Cobo struct workqueue_struct *lbtf_wq;
2506b16ae5SLuis Carlos Cobo 
2606b16ae5SLuis Carlos Cobo static const struct ieee80211_channel lbtf_channels[] = {
2706b16ae5SLuis Carlos Cobo 	{ .center_freq = 2412, .hw_value = 1 },
2806b16ae5SLuis Carlos Cobo 	{ .center_freq = 2417, .hw_value = 2 },
2906b16ae5SLuis Carlos Cobo 	{ .center_freq = 2422, .hw_value = 3 },
3006b16ae5SLuis Carlos Cobo 	{ .center_freq = 2427, .hw_value = 4 },
3106b16ae5SLuis Carlos Cobo 	{ .center_freq = 2432, .hw_value = 5 },
3206b16ae5SLuis Carlos Cobo 	{ .center_freq = 2437, .hw_value = 6 },
3306b16ae5SLuis Carlos Cobo 	{ .center_freq = 2442, .hw_value = 7 },
3406b16ae5SLuis Carlos Cobo 	{ .center_freq = 2447, .hw_value = 8 },
3506b16ae5SLuis Carlos Cobo 	{ .center_freq = 2452, .hw_value = 9 },
3606b16ae5SLuis Carlos Cobo 	{ .center_freq = 2457, .hw_value = 10 },
3706b16ae5SLuis Carlos Cobo 	{ .center_freq = 2462, .hw_value = 11 },
3806b16ae5SLuis Carlos Cobo 	{ .center_freq = 2467, .hw_value = 12 },
3906b16ae5SLuis Carlos Cobo 	{ .center_freq = 2472, .hw_value = 13 },
4006b16ae5SLuis Carlos Cobo 	{ .center_freq = 2484, .hw_value = 14 },
4106b16ae5SLuis Carlos Cobo };
4206b16ae5SLuis Carlos Cobo 
4306b16ae5SLuis Carlos Cobo /* This table contains the hardware specific values for the modulation rates. */
4406b16ae5SLuis Carlos Cobo static const struct ieee80211_rate lbtf_rates[] = {
4506b16ae5SLuis Carlos Cobo 	{ .bitrate = 10,
4606b16ae5SLuis Carlos Cobo 	  .hw_value = 0, },
4706b16ae5SLuis Carlos Cobo 	{ .bitrate = 20,
4806b16ae5SLuis Carlos Cobo 	  .hw_value = 1,
4906b16ae5SLuis Carlos Cobo 	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
5006b16ae5SLuis Carlos Cobo 	{ .bitrate = 55,
5106b16ae5SLuis Carlos Cobo 	  .hw_value = 2,
5206b16ae5SLuis Carlos Cobo 	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
5306b16ae5SLuis Carlos Cobo 	{ .bitrate = 110,
5406b16ae5SLuis Carlos Cobo 	  .hw_value = 3,
5506b16ae5SLuis Carlos Cobo 	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
5606b16ae5SLuis Carlos Cobo 	{ .bitrate = 60,
5706b16ae5SLuis Carlos Cobo 	  .hw_value = 5,
5806b16ae5SLuis Carlos Cobo 	  .flags = 0 },
5906b16ae5SLuis Carlos Cobo 	{ .bitrate = 90,
6006b16ae5SLuis Carlos Cobo 	  .hw_value = 6,
6106b16ae5SLuis Carlos Cobo 	  .flags = 0 },
6206b16ae5SLuis Carlos Cobo 	{ .bitrate = 120,
6306b16ae5SLuis Carlos Cobo 	  .hw_value = 7,
6406b16ae5SLuis Carlos Cobo 	  .flags = 0 },
6506b16ae5SLuis Carlos Cobo 	{ .bitrate = 180,
6606b16ae5SLuis Carlos Cobo 	  .hw_value = 8,
6706b16ae5SLuis Carlos Cobo 	  .flags = 0 },
6806b16ae5SLuis Carlos Cobo 	{ .bitrate = 240,
6906b16ae5SLuis Carlos Cobo 	  .hw_value = 9,
7006b16ae5SLuis Carlos Cobo 	  .flags = 0 },
7106b16ae5SLuis Carlos Cobo 	{ .bitrate = 360,
7206b16ae5SLuis Carlos Cobo 	  .hw_value = 10,
7306b16ae5SLuis Carlos Cobo 	  .flags = 0 },
7406b16ae5SLuis Carlos Cobo 	{ .bitrate = 480,
7506b16ae5SLuis Carlos Cobo 	  .hw_value = 11,
7606b16ae5SLuis Carlos Cobo 	  .flags = 0 },
7706b16ae5SLuis Carlos Cobo 	{ .bitrate = 540,
7806b16ae5SLuis Carlos Cobo 	  .hw_value = 12,
7906b16ae5SLuis Carlos Cobo 	  .flags = 0 },
8006b16ae5SLuis Carlos Cobo };
8106b16ae5SLuis Carlos Cobo 
lbtf_cmd_work(struct work_struct * work)8206b16ae5SLuis Carlos Cobo static void lbtf_cmd_work(struct work_struct *work)
8306b16ae5SLuis Carlos Cobo {
8406b16ae5SLuis Carlos Cobo 	struct lbtf_private *priv = container_of(work, struct lbtf_private,
8506b16ae5SLuis Carlos Cobo 					 cmd_work);
86e9bd5bcdSSteve deRosier 
87e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_CMD);
88e9bd5bcdSSteve deRosier 
8906b16ae5SLuis Carlos Cobo 	spin_lock_irq(&priv->driver_lock);
9006b16ae5SLuis Carlos Cobo 	/* command response? */
9106b16ae5SLuis Carlos Cobo 	if (priv->cmd_response_rxed) {
9206b16ae5SLuis Carlos Cobo 		priv->cmd_response_rxed = 0;
9306b16ae5SLuis Carlos Cobo 		spin_unlock_irq(&priv->driver_lock);
9406b16ae5SLuis Carlos Cobo 		lbtf_process_rx_command(priv);
9506b16ae5SLuis Carlos Cobo 		spin_lock_irq(&priv->driver_lock);
9606b16ae5SLuis Carlos Cobo 	}
9706b16ae5SLuis Carlos Cobo 
9806b16ae5SLuis Carlos Cobo 	if (priv->cmd_timed_out && priv->cur_cmd) {
9906b16ae5SLuis Carlos Cobo 		struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
10006b16ae5SLuis Carlos Cobo 
10106b16ae5SLuis Carlos Cobo 		if (++priv->nr_retries > 10) {
10206b16ae5SLuis Carlos Cobo 			lbtf_complete_command(priv, cmdnode,
10306b16ae5SLuis Carlos Cobo 					      -ETIMEDOUT);
10406b16ae5SLuis Carlos Cobo 			priv->nr_retries = 0;
10506b16ae5SLuis Carlos Cobo 		} else {
10606b16ae5SLuis Carlos Cobo 			priv->cur_cmd = NULL;
10706b16ae5SLuis Carlos Cobo 
10806b16ae5SLuis Carlos Cobo 			/* Stick it back at the _top_ of the pending
10906b16ae5SLuis Carlos Cobo 			 * queue for immediate resubmission */
11006b16ae5SLuis Carlos Cobo 			list_add(&cmdnode->list, &priv->cmdpendingq);
11106b16ae5SLuis Carlos Cobo 		}
11206b16ae5SLuis Carlos Cobo 	}
11306b16ae5SLuis Carlos Cobo 	priv->cmd_timed_out = 0;
11406b16ae5SLuis Carlos Cobo 	spin_unlock_irq(&priv->driver_lock);
11506b16ae5SLuis Carlos Cobo 
11606b16ae5SLuis Carlos Cobo 	/* Execute the next command */
11706b16ae5SLuis Carlos Cobo 	if (!priv->cur_cmd)
11806b16ae5SLuis Carlos Cobo 		lbtf_execute_next_command(priv);
119e9bd5bcdSSteve deRosier 
120e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_CMD);
12106b16ae5SLuis Carlos Cobo }
12206b16ae5SLuis Carlos Cobo 
1239833f503SLee Jones /*
12406b16ae5SLuis Carlos Cobo  *  This function handles the timeout of command sending.
12506b16ae5SLuis Carlos Cobo  *  It will re-send the same command again.
12606b16ae5SLuis Carlos Cobo  */
command_timer_fn(struct timer_list * t)12778ce6a90SKees Cook static void command_timer_fn(struct timer_list *t)
12806b16ae5SLuis Carlos Cobo {
12978ce6a90SKees Cook 	struct lbtf_private *priv = timer_container_of(priv, t, command_timer);
13006b16ae5SLuis Carlos Cobo 	unsigned long flags;
131e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_CMD);
13206b16ae5SLuis Carlos Cobo 
13306b16ae5SLuis Carlos Cobo 	spin_lock_irqsave(&priv->driver_lock, flags);
13406b16ae5SLuis Carlos Cobo 
13506b16ae5SLuis Carlos Cobo 	if (!priv->cur_cmd) {
13606b16ae5SLuis Carlos Cobo 		printk(KERN_DEBUG "libertastf: command timer expired; "
13706b16ae5SLuis Carlos Cobo 				  "no pending command\n");
13806b16ae5SLuis Carlos Cobo 		goto out;
13906b16ae5SLuis Carlos Cobo 	}
14006b16ae5SLuis Carlos Cobo 
14106b16ae5SLuis Carlos Cobo 	printk(KERN_DEBUG "libertas: command %x timed out\n",
14206b16ae5SLuis Carlos Cobo 		le16_to_cpu(priv->cur_cmd->cmdbuf->command));
14306b16ae5SLuis Carlos Cobo 
14406b16ae5SLuis Carlos Cobo 	priv->cmd_timed_out = 1;
14506b16ae5SLuis Carlos Cobo 	queue_work(lbtf_wq, &priv->cmd_work);
14606b16ae5SLuis Carlos Cobo out:
14706b16ae5SLuis Carlos Cobo 	spin_unlock_irqrestore(&priv->driver_lock, flags);
148e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_CMD);
14906b16ae5SLuis Carlos Cobo }
15006b16ae5SLuis Carlos Cobo 
lbtf_init_adapter(struct lbtf_private * priv)15106b16ae5SLuis Carlos Cobo static int lbtf_init_adapter(struct lbtf_private *priv)
15206b16ae5SLuis Carlos Cobo {
153e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_MAIN);
15493803b33SJoe Perches 	eth_broadcast_addr(priv->current_addr);
15506b16ae5SLuis Carlos Cobo 	mutex_init(&priv->lock);
15606b16ae5SLuis Carlos Cobo 
15706b16ae5SLuis Carlos Cobo 	priv->vif = NULL;
15878ce6a90SKees Cook 	timer_setup(&priv->command_timer, command_timer_fn, 0);
15906b16ae5SLuis Carlos Cobo 
16006b16ae5SLuis Carlos Cobo 	INIT_LIST_HEAD(&priv->cmdfreeq);
16106b16ae5SLuis Carlos Cobo 	INIT_LIST_HEAD(&priv->cmdpendingq);
16206b16ae5SLuis Carlos Cobo 
16306b16ae5SLuis Carlos Cobo 	spin_lock_init(&priv->driver_lock);
16406b16ae5SLuis Carlos Cobo 
16506b16ae5SLuis Carlos Cobo 	/* Allocate the command buffers */
16606b16ae5SLuis Carlos Cobo 	if (lbtf_allocate_cmd_buffer(priv))
16706b16ae5SLuis Carlos Cobo 		return -1;
16806b16ae5SLuis Carlos Cobo 
169e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_MAIN);
17006b16ae5SLuis Carlos Cobo 	return 0;
17106b16ae5SLuis Carlos Cobo }
17206b16ae5SLuis Carlos Cobo 
lbtf_free_adapter(struct lbtf_private * priv)17306b16ae5SLuis Carlos Cobo static void lbtf_free_adapter(struct lbtf_private *priv)
17406b16ae5SLuis Carlos Cobo {
175e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_MAIN);
17606b16ae5SLuis Carlos Cobo 	lbtf_free_cmd_buffer(priv);
17706b16ae5SLuis Carlos Cobo 	timer_delete(&priv->command_timer);
178e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_MAIN);
17906b16ae5SLuis Carlos Cobo }
18006b16ae5SLuis Carlos Cobo 
lbtf_op_tx(struct ieee80211_hw * hw,struct ieee80211_tx_control * control,struct sk_buff * skb)18136323f81SThomas Huehn static void lbtf_op_tx(struct ieee80211_hw *hw,
18236323f81SThomas Huehn 		       struct ieee80211_tx_control *control,
18336323f81SThomas Huehn 		       struct sk_buff *skb)
18406b16ae5SLuis Carlos Cobo {
18506b16ae5SLuis Carlos Cobo 	struct lbtf_private *priv = hw->priv;
18606b16ae5SLuis Carlos Cobo 
18706b16ae5SLuis Carlos Cobo 	priv->skb_to_tx = skb;
18806b16ae5SLuis Carlos Cobo 	queue_work(lbtf_wq, &priv->tx_work);
18906b16ae5SLuis Carlos Cobo 	/*
19006b16ae5SLuis Carlos Cobo 	 * queue will be restarted when we receive transmission feedback if
19106b16ae5SLuis Carlos Cobo 	 * there are no buffered multicast frames to send
19206b16ae5SLuis Carlos Cobo 	 */
19306b16ae5SLuis Carlos Cobo 	ieee80211_stop_queues(priv->hw);
19406b16ae5SLuis Carlos Cobo }
19506b16ae5SLuis Carlos Cobo 
lbtf_tx_work(struct work_struct * work)19606b16ae5SLuis Carlos Cobo static void lbtf_tx_work(struct work_struct *work)
19706b16ae5SLuis Carlos Cobo {
19806b16ae5SLuis Carlos Cobo 	struct lbtf_private *priv = container_of(work, struct lbtf_private,
19906b16ae5SLuis Carlos Cobo 					 tx_work);
20006b16ae5SLuis Carlos Cobo 	unsigned int len;
20106b16ae5SLuis Carlos Cobo 	struct ieee80211_tx_info *info;
20206b16ae5SLuis Carlos Cobo 	struct txpd *txpd;
20306b16ae5SLuis Carlos Cobo 	struct sk_buff *skb = NULL;
20406b16ae5SLuis Carlos Cobo 	int err;
20506b16ae5SLuis Carlos Cobo 
206e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_MACOPS | LBTF_DEB_TX);
207e9bd5bcdSSteve deRosier 
20805c914feSJohannes Berg 	if ((priv->vif->type == NL80211_IFTYPE_AP) &&
20906b16ae5SLuis Carlos Cobo 	    (!skb_queue_empty(&priv->bc_ps_buf)))
21006b16ae5SLuis Carlos Cobo 		skb = skb_dequeue(&priv->bc_ps_buf);
21106b16ae5SLuis Carlos Cobo 	else if (priv->skb_to_tx) {
21206b16ae5SLuis Carlos Cobo 		skb = priv->skb_to_tx;
21306b16ae5SLuis Carlos Cobo 		priv->skb_to_tx = NULL;
214e9bd5bcdSSteve deRosier 	} else {
215e9bd5bcdSSteve deRosier 		lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);
21606b16ae5SLuis Carlos Cobo 		return;
217e9bd5bcdSSteve deRosier 	}
21806b16ae5SLuis Carlos Cobo 
21906b16ae5SLuis Carlos Cobo 	len = skb->len;
22006b16ae5SLuis Carlos Cobo 	info  = IEEE80211_SKB_CB(skb);
221d58ff351SJohannes Berg 	txpd = skb_push(skb, sizeof(struct txpd));
22206b16ae5SLuis Carlos Cobo 
22306b16ae5SLuis Carlos Cobo 	if (priv->surpriseremoved) {
22406b16ae5SLuis Carlos Cobo 		dev_kfree_skb_any(skb);
225e9bd5bcdSSteve deRosier 		lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);
22606b16ae5SLuis Carlos Cobo 		return;
22706b16ae5SLuis Carlos Cobo 	}
22806b16ae5SLuis Carlos Cobo 
22906b16ae5SLuis Carlos Cobo 	memset(txpd, 0, sizeof(struct txpd));
23006b16ae5SLuis Carlos Cobo 	/* Activate per-packet rate selection */
23106b16ae5SLuis Carlos Cobo 	txpd->tx_control |= cpu_to_le32(MRVL_PER_PACKET_RATE |
23206b16ae5SLuis Carlos Cobo 			     ieee80211_get_tx_rate(priv->hw, info)->hw_value);
23306b16ae5SLuis Carlos Cobo 
23406b16ae5SLuis Carlos Cobo 	/* copy destination address from 802.11 header */
235642a5747SKees Cook 	BUILD_BUG_ON(sizeof(txpd->tx_dest_addr) != ETH_ALEN);
236642a5747SKees Cook 	memcpy(&txpd->tx_dest_addr, skb->data + sizeof(struct txpd) + 4,
23706b16ae5SLuis Carlos Cobo 		ETH_ALEN);
23806b16ae5SLuis Carlos Cobo 	txpd->tx_packet_length = cpu_to_le16(len);
23906b16ae5SLuis Carlos Cobo 	txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd));
240e9bd5bcdSSteve deRosier 	lbtf_deb_hex(LBTF_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100));
24106b16ae5SLuis Carlos Cobo 	BUG_ON(priv->tx_skb);
24206b16ae5SLuis Carlos Cobo 	spin_lock_irq(&priv->driver_lock);
24306b16ae5SLuis Carlos Cobo 	priv->tx_skb = skb;
244be9d0d3fSLubomir Rintel 	err = priv->ops->hw_host_to_card(priv, MVMS_DAT, skb->data, skb->len);
24506b16ae5SLuis Carlos Cobo 	spin_unlock_irq(&priv->driver_lock);
24606b16ae5SLuis Carlos Cobo 	if (err) {
24706b16ae5SLuis Carlos Cobo 		dev_kfree_skb_any(skb);
24806b16ae5SLuis Carlos Cobo 		priv->tx_skb = NULL;
249e9bd5bcdSSteve deRosier 		pr_err("TX error: %d", err);
25006b16ae5SLuis Carlos Cobo 	}
251e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);
25206b16ae5SLuis Carlos Cobo }
25306b16ae5SLuis Carlos Cobo 
lbtf_op_start(struct ieee80211_hw * hw)25406b16ae5SLuis Carlos Cobo static int lbtf_op_start(struct ieee80211_hw *hw)
25506b16ae5SLuis Carlos Cobo {
25606b16ae5SLuis Carlos Cobo 	struct lbtf_private *priv = hw->priv;
25706b16ae5SLuis Carlos Cobo 
258e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_MACOPS);
259e9bd5bcdSSteve deRosier 
26006b16ae5SLuis Carlos Cobo 	priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE;
26106b16ae5SLuis Carlos Cobo 	priv->radioon = RADIO_ON;
26206b16ae5SLuis Carlos Cobo 	priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON;
2635d04b22bSLubomir Rintel 	lbtf_set_mac_control(priv);
2645d04b22bSLubomir Rintel 	lbtf_set_radio_control(priv);
26506b16ae5SLuis Carlos Cobo 
266e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_MACOPS);
26706b16ae5SLuis Carlos Cobo 	return 0;
26806b16ae5SLuis Carlos Cobo }
26906b16ae5SLuis Carlos Cobo 
lbtf_op_stop(struct ieee80211_hw * hw,bool suspend)27006b16ae5SLuis Carlos Cobo static void lbtf_op_stop(struct ieee80211_hw *hw, bool suspend)
27106b16ae5SLuis Carlos Cobo {
27206b16ae5SLuis Carlos Cobo 	struct lbtf_private *priv = hw->priv;
27306b16ae5SLuis Carlos Cobo 	unsigned long flags;
27406b16ae5SLuis Carlos Cobo 	struct sk_buff *skb;
27506b16ae5SLuis Carlos Cobo 
27606b16ae5SLuis Carlos Cobo 	struct cmd_ctrl_node *cmdnode;
277e9bd5bcdSSteve deRosier 
278e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_MACOPS);
279e9bd5bcdSSteve deRosier 
28006b16ae5SLuis Carlos Cobo 	/* Flush pending command nodes */
28106b16ae5SLuis Carlos Cobo 	spin_lock_irqsave(&priv->driver_lock, flags);
28206b16ae5SLuis Carlos Cobo 	list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
28306b16ae5SLuis Carlos Cobo 		cmdnode->result = -ENOENT;
28406b16ae5SLuis Carlos Cobo 		cmdnode->cmdwaitqwoken = 1;
28506b16ae5SLuis Carlos Cobo 		wake_up_interruptible(&cmdnode->cmdwait_q);
28606b16ae5SLuis Carlos Cobo 	}
28706b16ae5SLuis Carlos Cobo 
28806b16ae5SLuis Carlos Cobo 	spin_unlock_irqrestore(&priv->driver_lock, flags);
28906b16ae5SLuis Carlos Cobo 	cancel_work_sync(&priv->cmd_work);
29006b16ae5SLuis Carlos Cobo 	cancel_work_sync(&priv->tx_work);
29106b16ae5SLuis Carlos Cobo 	while ((skb = skb_dequeue(&priv->bc_ps_buf)))
29206b16ae5SLuis Carlos Cobo 		dev_kfree_skb_any(skb);
29306b16ae5SLuis Carlos Cobo 	priv->radioon = RADIO_OFF;
29406b16ae5SLuis Carlos Cobo 	lbtf_set_radio_control(priv);
29506b16ae5SLuis Carlos Cobo 
296e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_MACOPS);
29706b16ae5SLuis Carlos Cobo }
29806b16ae5SLuis Carlos Cobo 
lbtf_op_add_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)29906b16ae5SLuis Carlos Cobo static int lbtf_op_add_interface(struct ieee80211_hw *hw,
3001ed32e4fSJohannes Berg 			struct ieee80211_vif *vif)
30106b16ae5SLuis Carlos Cobo {
30206b16ae5SLuis Carlos Cobo 	struct lbtf_private *priv = hw->priv;
303e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_MACOPS);
30406b16ae5SLuis Carlos Cobo 	if (priv->vif != NULL)
30506b16ae5SLuis Carlos Cobo 		return -EOPNOTSUPP;
30606b16ae5SLuis Carlos Cobo 
3071ed32e4fSJohannes Berg 	priv->vif = vif;
3081ed32e4fSJohannes Berg 	switch (vif->type) {
30905c914feSJohannes Berg 	case NL80211_IFTYPE_MESH_POINT:
31005c914feSJohannes Berg 	case NL80211_IFTYPE_AP:
31106b16ae5SLuis Carlos Cobo 		lbtf_set_mode(priv, LBTF_AP_MODE);
31206b16ae5SLuis Carlos Cobo 		break;
31305c914feSJohannes Berg 	case NL80211_IFTYPE_STATION:
31406b16ae5SLuis Carlos Cobo 		lbtf_set_mode(priv, LBTF_STA_MODE);
31506b16ae5SLuis Carlos Cobo 		break;
31606b16ae5SLuis Carlos Cobo 	default:
31706b16ae5SLuis Carlos Cobo 		priv->vif = NULL;
31806b16ae5SLuis Carlos Cobo 		return -EOPNOTSUPP;
31906b16ae5SLuis Carlos Cobo 	}
3201ed32e4fSJohannes Berg 	lbtf_set_mac_address(priv, (u8 *) vif->addr);
321e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_MACOPS);
32206b16ae5SLuis Carlos Cobo 	return 0;
32306b16ae5SLuis Carlos Cobo }
32406b16ae5SLuis Carlos Cobo 
lbtf_op_remove_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)32506b16ae5SLuis Carlos Cobo static void lbtf_op_remove_interface(struct ieee80211_hw *hw,
3261ed32e4fSJohannes Berg 			struct ieee80211_vif *vif)
32706b16ae5SLuis Carlos Cobo {
32806b16ae5SLuis Carlos Cobo 	struct lbtf_private *priv = hw->priv;
329e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_MACOPS);
33006b16ae5SLuis Carlos Cobo 
33105c914feSJohannes Berg 	if (priv->vif->type == NL80211_IFTYPE_AP ||
33205c914feSJohannes Berg 	    priv->vif->type == NL80211_IFTYPE_MESH_POINT)
33306b16ae5SLuis Carlos Cobo 		lbtf_beacon_ctrl(priv, 0, 0);
33406b16ae5SLuis Carlos Cobo 	lbtf_set_mode(priv, LBTF_PASSIVE_MODE);
33506b16ae5SLuis Carlos Cobo 	lbtf_set_bssid(priv, 0, NULL);
33606b16ae5SLuis Carlos Cobo 	priv->vif = NULL;
337e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_MACOPS);
33806b16ae5SLuis Carlos Cobo }
33906b16ae5SLuis Carlos Cobo 
lbtf_op_config(struct ieee80211_hw * hw,int radio_idx,u32 changed)340e8975581SJohannes Berg static int lbtf_op_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
34106b16ae5SLuis Carlos Cobo {
34206b16ae5SLuis Carlos Cobo 	struct lbtf_private *priv = hw->priv;
343e8975581SJohannes Berg 	struct ieee80211_conf *conf = &hw->conf;
344e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_MACOPS);
345e8975581SJohannes Berg 
346675a0b04SKarl Beldan 	if (conf->chandef.chan->center_freq != priv->cur_freq) {
347675a0b04SKarl Beldan 		priv->cur_freq = conf->chandef.chan->center_freq;
348675a0b04SKarl Beldan 		lbtf_set_channel(priv, conf->chandef.chan->hw_value);
34906b16ae5SLuis Carlos Cobo 	}
350e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_MACOPS);
35106b16ae5SLuis Carlos Cobo 	return 0;
35206b16ae5SLuis Carlos Cobo }
35306b16ae5SLuis Carlos Cobo 
lbtf_op_prepare_multicast(struct ieee80211_hw * hw,struct netdev_hw_addr_list * mc_list)3543ac64beeSJohannes Berg static u64 lbtf_op_prepare_multicast(struct ieee80211_hw *hw,
35522bedad3SJiri Pirko 				     struct netdev_hw_addr_list *mc_list)
3563ac64beeSJohannes Berg {
3573ac64beeSJohannes Berg 	struct lbtf_private *priv = hw->priv;
3583ac64beeSJohannes Berg 	int i;
35922bedad3SJiri Pirko 	struct netdev_hw_addr *ha;
36022bedad3SJiri Pirko 	int mc_count = netdev_hw_addr_list_count(mc_list);
3613ac64beeSJohannes Berg 
3623ac64beeSJohannes Berg 	if (!mc_count || mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE)
3633ac64beeSJohannes Berg 		return mc_count;
3643ac64beeSJohannes Berg 
3653ac64beeSJohannes Berg 	priv->nr_of_multicastmacaddr = mc_count;
36622bedad3SJiri Pirko 	i = 0;
36722bedad3SJiri Pirko 	netdev_hw_addr_list_for_each(ha, mc_list)
36822bedad3SJiri Pirko 		memcpy(&priv->multicastlist[i++], ha->addr, ETH_ALEN);
3693ac64beeSJohannes Berg 
3703ac64beeSJohannes Berg 	return mc_count;
3713ac64beeSJohannes Berg }
3723ac64beeSJohannes Berg 
373df140465SJohannes Berg #define SUPPORTED_FIF_FLAGS  FIF_ALLMULTI
lbtf_op_configure_filter(struct ieee80211_hw * hw,unsigned int changed_flags,unsigned int * new_flags,u64 multicast)37406b16ae5SLuis Carlos Cobo static void lbtf_op_configure_filter(struct ieee80211_hw *hw,
37506b16ae5SLuis Carlos Cobo 			unsigned int changed_flags,
37606b16ae5SLuis Carlos Cobo 			unsigned int *new_flags,
3773ac64beeSJohannes Berg 			u64 multicast)
37806b16ae5SLuis Carlos Cobo {
37906b16ae5SLuis Carlos Cobo 	struct lbtf_private *priv = hw->priv;
38006b16ae5SLuis Carlos Cobo 	int old_mac_control = priv->mac_control;
381e9bd5bcdSSteve deRosier 
382e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_MACOPS);
383e9bd5bcdSSteve deRosier 
38406b16ae5SLuis Carlos Cobo 	changed_flags &= SUPPORTED_FIF_FLAGS;
38506b16ae5SLuis Carlos Cobo 	*new_flags &= SUPPORTED_FIF_FLAGS;
38606b16ae5SLuis Carlos Cobo 
387e9bd5bcdSSteve deRosier 	if (!changed_flags) {
388e9bd5bcdSSteve deRosier 		lbtf_deb_leave(LBTF_DEB_MACOPS);
38906b16ae5SLuis Carlos Cobo 		return;
390e9bd5bcdSSteve deRosier 	}
39106b16ae5SLuis Carlos Cobo 
39206b16ae5SLuis Carlos Cobo 	priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE;
39306b16ae5SLuis Carlos Cobo 	if (*new_flags & (FIF_ALLMULTI) ||
3943ac64beeSJohannes Berg 	    multicast > MRVDRV_MAX_MULTICAST_LIST_SIZE) {
39506b16ae5SLuis Carlos Cobo 		priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
39606b16ae5SLuis Carlos Cobo 		priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE;
3973ac64beeSJohannes Berg 	} else if (multicast) {
39806b16ae5SLuis Carlos Cobo 		priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE;
39906b16ae5SLuis Carlos Cobo 		priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
40006b16ae5SLuis Carlos Cobo 		lbtf_cmd_set_mac_multicast_addr(priv);
40106b16ae5SLuis Carlos Cobo 	} else {
40206b16ae5SLuis Carlos Cobo 		priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE |
40306b16ae5SLuis Carlos Cobo 				       CMD_ACT_MAC_ALL_MULTICAST_ENABLE);
40406b16ae5SLuis Carlos Cobo 		if (priv->nr_of_multicastmacaddr) {
40506b16ae5SLuis Carlos Cobo 			priv->nr_of_multicastmacaddr = 0;
40606b16ae5SLuis Carlos Cobo 			lbtf_cmd_set_mac_multicast_addr(priv);
40706b16ae5SLuis Carlos Cobo 		}
40806b16ae5SLuis Carlos Cobo 	}
40906b16ae5SLuis Carlos Cobo 
41006b16ae5SLuis Carlos Cobo 
41106b16ae5SLuis Carlos Cobo 	if (priv->mac_control != old_mac_control)
41206b16ae5SLuis Carlos Cobo 		lbtf_set_mac_control(priv);
413e9bd5bcdSSteve deRosier 
414e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_MACOPS);
41506b16ae5SLuis Carlos Cobo }
41606b16ae5SLuis Carlos Cobo 
lbtf_op_bss_info_changed(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * bss_conf,u64 changes)41706b16ae5SLuis Carlos Cobo static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw,
41806b16ae5SLuis Carlos Cobo 			struct ieee80211_vif *vif,
41906b16ae5SLuis Carlos Cobo 			struct ieee80211_bss_conf *bss_conf,
4207b7090b4SJohannes Berg 			u64 changes)
42106b16ae5SLuis Carlos Cobo {
42206b16ae5SLuis Carlos Cobo 	struct lbtf_private *priv = hw->priv;
4232d0ddec5SJohannes Berg 	struct sk_buff *beacon;
424e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_MACOPS);
4252d0ddec5SJohannes Berg 
4262d0ddec5SJohannes Berg 	if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_INT)) {
4272d0ddec5SJohannes Berg 		switch (priv->vif->type) {
4282d0ddec5SJohannes Berg 		case NL80211_IFTYPE_AP:
4292d0ddec5SJohannes Berg 		case NL80211_IFTYPE_MESH_POINT:
4306e8912a5SShaul Triebitz 			beacon = ieee80211_beacon_get(hw, vif, 0);
4312d0ddec5SJohannes Berg 			if (beacon) {
4322d0ddec5SJohannes Berg 				lbtf_beacon_set(priv, beacon);
4332d0ddec5SJohannes Berg 				kfree_skb(beacon);
4342d0ddec5SJohannes Berg 				lbtf_beacon_ctrl(priv, 1,
4352d0ddec5SJohannes Berg 						 bss_conf->beacon_int);
4362d0ddec5SJohannes Berg 			}
4372d0ddec5SJohannes Berg 			break;
4382d0ddec5SJohannes Berg 		default:
4392d0ddec5SJohannes Berg 			break;
4402d0ddec5SJohannes Berg 		}
4412d0ddec5SJohannes Berg 	}
4422d0ddec5SJohannes Berg 
4432d0ddec5SJohannes Berg 	if (changes & BSS_CHANGED_BSSID) {
4442d0ddec5SJohannes Berg 		bool activate = !is_zero_ether_addr(bss_conf->bssid);
4452d0ddec5SJohannes Berg 		lbtf_set_bssid(priv, activate, bss_conf->bssid);
4462d0ddec5SJohannes Berg 	}
44706b16ae5SLuis Carlos Cobo 
44806b16ae5SLuis Carlos Cobo 	if (changes & BSS_CHANGED_ERP_PREAMBLE) {
44906b16ae5SLuis Carlos Cobo 		if (bss_conf->use_short_preamble)
45006b16ae5SLuis Carlos Cobo 			priv->preamble = CMD_TYPE_SHORT_PREAMBLE;
45106b16ae5SLuis Carlos Cobo 		else
45206b16ae5SLuis Carlos Cobo 			priv->preamble = CMD_TYPE_LONG_PREAMBLE;
45306b16ae5SLuis Carlos Cobo 		lbtf_set_radio_control(priv);
45406b16ae5SLuis Carlos Cobo 	}
455e9bd5bcdSSteve deRosier 
456e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_MACOPS);
45706b16ae5SLuis Carlos Cobo }
45806b16ae5SLuis Carlos Cobo 
lbtf_op_get_survey(struct ieee80211_hw * hw,int idx,struct survey_info * survey)459bef9cb58SJohn W. Linville static int lbtf_op_get_survey(struct ieee80211_hw *hw, int idx,
460bef9cb58SJohn W. Linville 				struct survey_info *survey)
461bef9cb58SJohn W. Linville {
462bef9cb58SJohn W. Linville 	struct lbtf_private *priv = hw->priv;
463bef9cb58SJohn W. Linville 	struct ieee80211_conf *conf = &hw->conf;
464bef9cb58SJohn W. Linville 
465bef9cb58SJohn W. Linville 	if (idx != 0)
466bef9cb58SJohn W. Linville 		return -ENOENT;
467bef9cb58SJohn W. Linville 
468675a0b04SKarl Beldan 	survey->channel = conf->chandef.chan;
469bef9cb58SJohn W. Linville 	survey->filled = SURVEY_INFO_NOISE_DBM;
470bef9cb58SJohn W. Linville 	survey->noise = priv->noise;
471bef9cb58SJohn W. Linville 
472bef9cb58SJohn W. Linville 	return 0;
473bef9cb58SJohn W. Linville }
474bef9cb58SJohn W. Linville 
47506b16ae5SLuis Carlos Cobo static const struct ieee80211_ops lbtf_ops = {
476*0a44dfc0SJohannes Berg 	.add_chanctx = ieee80211_emulate_add_chanctx,
477*0a44dfc0SJohannes Berg 	.remove_chanctx = ieee80211_emulate_remove_chanctx,
478*0a44dfc0SJohannes Berg 	.change_chanctx = ieee80211_emulate_change_chanctx,
479*0a44dfc0SJohannes Berg 	.switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx,
48006b16ae5SLuis Carlos Cobo 	.tx			= lbtf_op_tx,
481a790cc3aSAlexander Wetzel 	.wake_tx_queue		= ieee80211_handle_wake_tx_queue,
48206b16ae5SLuis Carlos Cobo 	.start			= lbtf_op_start,
48306b16ae5SLuis Carlos Cobo 	.stop			= lbtf_op_stop,
48406b16ae5SLuis Carlos Cobo 	.add_interface		= lbtf_op_add_interface,
48506b16ae5SLuis Carlos Cobo 	.remove_interface	= lbtf_op_remove_interface,
48606b16ae5SLuis Carlos Cobo 	.config			= lbtf_op_config,
4873ac64beeSJohannes Berg 	.prepare_multicast	= lbtf_op_prepare_multicast,
48806b16ae5SLuis Carlos Cobo 	.configure_filter	= lbtf_op_configure_filter,
48906b16ae5SLuis Carlos Cobo 	.bss_info_changed	= lbtf_op_bss_info_changed,
490bef9cb58SJohn W. Linville 	.get_survey		= lbtf_op_get_survey,
49106b16ae5SLuis Carlos Cobo };
49206b16ae5SLuis Carlos Cobo 
lbtf_rx(struct lbtf_private * priv,struct sk_buff * skb)49306b16ae5SLuis Carlos Cobo int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb)
49406b16ae5SLuis Carlos Cobo {
49506b16ae5SLuis Carlos Cobo 	struct ieee80211_rx_status stats;
49606b16ae5SLuis Carlos Cobo 	struct rxpd *prxpd;
497de9cc7a4SHarvey Harrison 	int need_padding;
498de9cc7a4SHarvey Harrison 	struct ieee80211_hdr *hdr;
49906b16ae5SLuis Carlos Cobo 
500e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_RX);
501e9bd5bcdSSteve deRosier 
502baa0280fSLubomir Rintel 	if (priv->radioon != RADIO_ON) {
503baa0280fSLubomir Rintel 		lbtf_deb_rx("rx before we turned on the radio");
504baa0280fSLubomir Rintel 		goto done;
505baa0280fSLubomir Rintel 	}
506baa0280fSLubomir Rintel 
50706b16ae5SLuis Carlos Cobo 	prxpd = (struct rxpd *) skb->data;
50806b16ae5SLuis Carlos Cobo 
50913deb23aSPrarit Bhargava 	memset(&stats, 0, sizeof(stats));
51006b16ae5SLuis Carlos Cobo 	if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK)))
51106b16ae5SLuis Carlos Cobo 		stats.flag |= RX_FLAG_FAILED_FCS_CRC;
51206b16ae5SLuis Carlos Cobo 	stats.freq = priv->cur_freq;
51357fbcce3SJohannes Berg 	stats.band = NL80211_BAND_2GHZ;
514b723dbb3SLubomir Rintel 	stats.signal = prxpd->snr - prxpd->nf;
515bef9cb58SJohn W. Linville 	priv->noise = prxpd->nf;
51606b16ae5SLuis Carlos Cobo 	/* Marvell rate index has a hole at value 4 */
51706b16ae5SLuis Carlos Cobo 	if (prxpd->rx_rate > 4)
51806b16ae5SLuis Carlos Cobo 		--prxpd->rx_rate;
51906b16ae5SLuis Carlos Cobo 	stats.rate_idx = prxpd->rx_rate;
52006b16ae5SLuis Carlos Cobo 	skb_pull(skb, sizeof(struct rxpd));
52106b16ae5SLuis Carlos Cobo 
522de9cc7a4SHarvey Harrison 	hdr = (struct ieee80211_hdr *)skb->data;
52306b16ae5SLuis Carlos Cobo 
524de9cc7a4SHarvey Harrison 	need_padding = ieee80211_is_data_qos(hdr->frame_control);
525de9cc7a4SHarvey Harrison 	need_padding ^= ieee80211_has_a4(hdr->frame_control);
526de9cc7a4SHarvey Harrison 	need_padding ^= ieee80211_is_data_qos(hdr->frame_control) &&
527de9cc7a4SHarvey Harrison 			(*ieee80211_get_qos_ctl(hdr) &
52804b7dcf9SJohannes Berg 			 IEEE80211_QOS_CTL_A_MSDU_PRESENT);
52906b16ae5SLuis Carlos Cobo 
53006b16ae5SLuis Carlos Cobo 	if (need_padding) {
53106b16ae5SLuis Carlos Cobo 		memmove(skb->data + 2, skb->data, skb->len);
53206b16ae5SLuis Carlos Cobo 		skb_reserve(skb, 2);
53306b16ae5SLuis Carlos Cobo 	}
53406b16ae5SLuis Carlos Cobo 
535f1d58c25SJohannes Berg 	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
536e9bd5bcdSSteve deRosier 
537e9bd5bcdSSteve deRosier 	lbtf_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n",
538e9bd5bcdSSteve deRosier 	       skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd));
539e9bd5bcdSSteve deRosier 	lbtf_deb_hex(LBTF_DEB_RX, "RX Data", skb->data,
540e9bd5bcdSSteve deRosier 	             min_t(unsigned int, skb->len, 100));
541e9bd5bcdSSteve deRosier 
542f1d58c25SJohannes Berg 	ieee80211_rx_irqsafe(priv->hw, skb);
543e9bd5bcdSSteve deRosier 
544baa0280fSLubomir Rintel done:
545e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_RX);
54606b16ae5SLuis Carlos Cobo 	return 0;
54706b16ae5SLuis Carlos Cobo }
54806b16ae5SLuis Carlos Cobo EXPORT_SYMBOL_GPL(lbtf_rx);
54906b16ae5SLuis Carlos Cobo 
5509833f503SLee Jones /*
551baa0280fSLubomir Rintel  * lbtf_add_card: Add and initialize the card.
55206b16ae5SLuis Carlos Cobo  *
55306b16ae5SLuis Carlos Cobo  *  Returns: pointer to struct lbtf_priv.
55406b16ae5SLuis Carlos Cobo  */
lbtf_add_card(void * card,struct device * dmdev,const struct lbtf_ops * ops)555be9d0d3fSLubomir Rintel struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev,
556be9d0d3fSLubomir Rintel 				   const struct lbtf_ops *ops)
55706b16ae5SLuis Carlos Cobo {
55806b16ae5SLuis Carlos Cobo 	struct ieee80211_hw *hw;
55906b16ae5SLuis Carlos Cobo 	struct lbtf_private *priv = NULL;
56006b16ae5SLuis Carlos Cobo 
561e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_MAIN);
562e9bd5bcdSSteve deRosier 
56306b16ae5SLuis Carlos Cobo 	hw = ieee80211_alloc_hw(sizeof(struct lbtf_private), &lbtf_ops);
56406b16ae5SLuis Carlos Cobo 	if (!hw)
56506b16ae5SLuis Carlos Cobo 		goto done;
56606b16ae5SLuis Carlos Cobo 
56706b16ae5SLuis Carlos Cobo 	priv = hw->priv;
56806b16ae5SLuis Carlos Cobo 	if (lbtf_init_adapter(priv))
56906b16ae5SLuis Carlos Cobo 		goto err_init_adapter;
57006b16ae5SLuis Carlos Cobo 
57106b16ae5SLuis Carlos Cobo 	priv->hw = hw;
57206b16ae5SLuis Carlos Cobo 	priv->card = card;
573be9d0d3fSLubomir Rintel 	priv->ops = ops;
57406b16ae5SLuis Carlos Cobo 	priv->tx_skb = NULL;
575baa0280fSLubomir Rintel 	priv->radioon = RADIO_OFF;
57606b16ae5SLuis Carlos Cobo 
57706b16ae5SLuis Carlos Cobo 	hw->queues = 1;
57830686bf7SJohannes Berg 	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
579b723dbb3SLubomir Rintel 	ieee80211_hw_set(hw, SIGNAL_DBM);
58006b16ae5SLuis Carlos Cobo 	hw->extra_tx_headroom = sizeof(struct txpd);
58106b16ae5SLuis Carlos Cobo 	memcpy(priv->channels, lbtf_channels, sizeof(lbtf_channels));
58206b16ae5SLuis Carlos Cobo 	memcpy(priv->rates, lbtf_rates, sizeof(lbtf_rates));
58306b16ae5SLuis Carlos Cobo 	priv->band.n_bitrates = ARRAY_SIZE(lbtf_rates);
58406b16ae5SLuis Carlos Cobo 	priv->band.bitrates = priv->rates;
58506b16ae5SLuis Carlos Cobo 	priv->band.n_channels = ARRAY_SIZE(lbtf_channels);
58606b16ae5SLuis Carlos Cobo 	priv->band.channels = priv->channels;
58757fbcce3SJohannes Berg 	hw->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band;
588f3233831SDeepak Saxena 	hw->wiphy->interface_modes =
589f3233831SDeepak Saxena 		BIT(NL80211_IFTYPE_STATION) |
590f3233831SDeepak Saxena 		BIT(NL80211_IFTYPE_ADHOC);
59106b16ae5SLuis Carlos Cobo 	skb_queue_head_init(&priv->bc_ps_buf);
59206b16ae5SLuis Carlos Cobo 
593ae44b502SAndrew Zaborowski 	wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
594ae44b502SAndrew Zaborowski 
59506b16ae5SLuis Carlos Cobo 	SET_IEEE80211_DEV(hw, dmdev);
59606b16ae5SLuis Carlos Cobo 
59706b16ae5SLuis Carlos Cobo 	INIT_WORK(&priv->cmd_work, lbtf_cmd_work);
59806b16ae5SLuis Carlos Cobo 	INIT_WORK(&priv->tx_work, lbtf_tx_work);
599baa0280fSLubomir Rintel 
600baa0280fSLubomir Rintel 	if (priv->ops->hw_prog_firmware(priv)) {
601baa0280fSLubomir Rintel 		lbtf_deb_usbd(dmdev, "Error programming the firmware\n");
602baa0280fSLubomir Rintel 		priv->ops->hw_reset_device(priv);
603baa0280fSLubomir Rintel 		goto err_init_adapter;
604baa0280fSLubomir Rintel 	}
605baa0280fSLubomir Rintel 
6065d04b22bSLubomir Rintel 	eth_broadcast_addr(priv->current_addr);
6075d04b22bSLubomir Rintel 	if (lbtf_update_hw_spec(priv))
6085d04b22bSLubomir Rintel 		goto err_init_adapter;
6095d04b22bSLubomir Rintel 
6105d04b22bSLubomir Rintel 	if (priv->fwrelease < LBTF_FW_VER_MIN ||
6115d04b22bSLubomir Rintel 	    priv->fwrelease > LBTF_FW_VER_MAX) {
6125d04b22bSLubomir Rintel 		goto err_init_adapter;
6135d04b22bSLubomir Rintel 	}
6145d04b22bSLubomir Rintel 
615baa0280fSLubomir Rintel 	/* The firmware seems to start with the radio enabled. Turn it
616baa0280fSLubomir Rintel 	 * off before an actual mac80211 start callback is invoked.
617baa0280fSLubomir Rintel 	 */
618baa0280fSLubomir Rintel 	lbtf_set_radio_control(priv);
619baa0280fSLubomir Rintel 
62006b16ae5SLuis Carlos Cobo 	if (ieee80211_register_hw(hw))
62106b16ae5SLuis Carlos Cobo 		goto err_init_adapter;
62206b16ae5SLuis Carlos Cobo 
623c7a5682dSLubomir Rintel 	dev_info(dmdev, "libertastf: Marvell WLAN 802.11 thinfirm adapter\n");
62406b16ae5SLuis Carlos Cobo 	goto done;
62506b16ae5SLuis Carlos Cobo 
62606b16ae5SLuis Carlos Cobo err_init_adapter:
62706b16ae5SLuis Carlos Cobo 	lbtf_free_adapter(priv);
62806b16ae5SLuis Carlos Cobo 	ieee80211_free_hw(hw);
62906b16ae5SLuis Carlos Cobo 	priv = NULL;
63006b16ae5SLuis Carlos Cobo 
63106b16ae5SLuis Carlos Cobo done:
632e9bd5bcdSSteve deRosier 	lbtf_deb_leave_args(LBTF_DEB_MAIN, "priv %p", priv);
63306b16ae5SLuis Carlos Cobo 	return priv;
63406b16ae5SLuis Carlos Cobo }
63506b16ae5SLuis Carlos Cobo EXPORT_SYMBOL_GPL(lbtf_add_card);
63606b16ae5SLuis Carlos Cobo 
63706b16ae5SLuis Carlos Cobo 
lbtf_remove_card(struct lbtf_private * priv)63806b16ae5SLuis Carlos Cobo int lbtf_remove_card(struct lbtf_private *priv)
63906b16ae5SLuis Carlos Cobo {
64006b16ae5SLuis Carlos Cobo 	struct ieee80211_hw *hw = priv->hw;
64106b16ae5SLuis Carlos Cobo 
642e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_MAIN);
643e9bd5bcdSSteve deRosier 
64406b16ae5SLuis Carlos Cobo 	priv->surpriseremoved = 1;
64506b16ae5SLuis Carlos Cobo 	timer_delete(&priv->command_timer);
64606b16ae5SLuis Carlos Cobo 	lbtf_free_adapter(priv);
64706b16ae5SLuis Carlos Cobo 	priv->hw = NULL;
64806b16ae5SLuis Carlos Cobo 	ieee80211_unregister_hw(hw);
64906b16ae5SLuis Carlos Cobo 	ieee80211_free_hw(hw);
65006b16ae5SLuis Carlos Cobo 
651e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_MAIN);
65206b16ae5SLuis Carlos Cobo 	return 0;
65306b16ae5SLuis Carlos Cobo }
65406b16ae5SLuis Carlos Cobo EXPORT_SYMBOL_GPL(lbtf_remove_card);
65506b16ae5SLuis Carlos Cobo 
lbtf_send_tx_feedback(struct lbtf_private * priv,u8 retrycnt,u8 fail)65606b16ae5SLuis Carlos Cobo void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail)
65706b16ae5SLuis Carlos Cobo {
65806b16ae5SLuis Carlos Cobo 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb);
659e6a9854bSJohannes Berg 
660e6a9854bSJohannes Berg 	ieee80211_tx_info_clear_status(info);
66106b16ae5SLuis Carlos Cobo 	/*
66206b16ae5SLuis Carlos Cobo 	 * Commented out, otherwise we never go beyond 1Mbit/s using mac80211
66306b16ae5SLuis Carlos Cobo 	 * default pid rc algorithm.
66406b16ae5SLuis Carlos Cobo 	 *
66506b16ae5SLuis Carlos Cobo 	 * info->status.retry_count = MRVL_DEFAULT_RETRIES - retrycnt;
66606b16ae5SLuis Carlos Cobo 	 */
66706b16ae5SLuis Carlos Cobo 	if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !fail)
66806b16ae5SLuis Carlos Cobo 		info->flags |= IEEE80211_TX_STAT_ACK;
66906b16ae5SLuis Carlos Cobo 	skb_pull(priv->tx_skb, sizeof(struct txpd));
67006b16ae5SLuis Carlos Cobo 	ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb);
67106b16ae5SLuis Carlos Cobo 	priv->tx_skb = NULL;
67206b16ae5SLuis Carlos Cobo 	if (!priv->skb_to_tx && skb_queue_empty(&priv->bc_ps_buf))
67306b16ae5SLuis Carlos Cobo 		ieee80211_wake_queues(priv->hw);
67406b16ae5SLuis Carlos Cobo 	else
67506b16ae5SLuis Carlos Cobo 		queue_work(lbtf_wq, &priv->tx_work);
67606b16ae5SLuis Carlos Cobo }
67706b16ae5SLuis Carlos Cobo EXPORT_SYMBOL_GPL(lbtf_send_tx_feedback);
67806b16ae5SLuis Carlos Cobo 
lbtf_bcn_sent(struct lbtf_private * priv)67906b16ae5SLuis Carlos Cobo void lbtf_bcn_sent(struct lbtf_private *priv)
68006b16ae5SLuis Carlos Cobo {
68106b16ae5SLuis Carlos Cobo 	struct sk_buff *skb = NULL;
68206b16ae5SLuis Carlos Cobo 
68305c914feSJohannes Berg 	if (priv->vif->type != NL80211_IFTYPE_AP)
68406b16ae5SLuis Carlos Cobo 		return;
68506b16ae5SLuis Carlos Cobo 
68606b16ae5SLuis Carlos Cobo 	if (skb_queue_empty(&priv->bc_ps_buf)) {
6873db1cd5cSRusty Russell 		bool tx_buff_bc = false;
68806b16ae5SLuis Carlos Cobo 
68906b16ae5SLuis Carlos Cobo 		while ((skb = ieee80211_get_buffered_bc(priv->hw, priv->vif))) {
69006b16ae5SLuis Carlos Cobo 			skb_queue_tail(&priv->bc_ps_buf, skb);
6913db1cd5cSRusty Russell 			tx_buff_bc = true;
69206b16ae5SLuis Carlos Cobo 		}
69306b16ae5SLuis Carlos Cobo 		if (tx_buff_bc) {
69406b16ae5SLuis Carlos Cobo 			ieee80211_stop_queues(priv->hw);
69506b16ae5SLuis Carlos Cobo 			queue_work(lbtf_wq, &priv->tx_work);
69606b16ae5SLuis Carlos Cobo 		}
69706b16ae5SLuis Carlos Cobo 	}
69806b16ae5SLuis Carlos Cobo 
6996e8912a5SShaul Triebitz 	skb = ieee80211_beacon_get(priv->hw, priv->vif, 0);
70006b16ae5SLuis Carlos Cobo 
70106b16ae5SLuis Carlos Cobo 	if (skb) {
70206b16ae5SLuis Carlos Cobo 		lbtf_beacon_set(priv, skb);
70306b16ae5SLuis Carlos Cobo 		kfree_skb(skb);
70406b16ae5SLuis Carlos Cobo 	}
70506b16ae5SLuis Carlos Cobo }
70606b16ae5SLuis Carlos Cobo EXPORT_SYMBOL_GPL(lbtf_bcn_sent);
70706b16ae5SLuis Carlos Cobo 
lbtf_init_module(void)70806b16ae5SLuis Carlos Cobo static int __init lbtf_init_module(void)
70906b16ae5SLuis Carlos Cobo {
710e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_MAIN);
711452fa86eSBhaktipriya Shridhar 	lbtf_wq = alloc_workqueue("libertastf", WQ_MEM_RECLAIM | WQ_UNBOUND, 0);
71206b16ae5SLuis Carlos Cobo 	if (lbtf_wq == NULL) {
71306b16ae5SLuis Carlos Cobo 		printk(KERN_ERR "libertastf: couldn't create workqueue\n");
71406b16ae5SLuis Carlos Cobo 		return -ENOMEM;
71506b16ae5SLuis Carlos Cobo 	}
716e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_MAIN);
71706b16ae5SLuis Carlos Cobo 	return 0;
71806b16ae5SLuis Carlos Cobo }
71906b16ae5SLuis Carlos Cobo 
lbtf_exit_module(void)72006b16ae5SLuis Carlos Cobo static void __exit lbtf_exit_module(void)
72106b16ae5SLuis Carlos Cobo {
722e9bd5bcdSSteve deRosier 	lbtf_deb_enter(LBTF_DEB_MAIN);
72306b16ae5SLuis Carlos Cobo 	destroy_workqueue(lbtf_wq);
724e9bd5bcdSSteve deRosier 	lbtf_deb_leave(LBTF_DEB_MAIN);
72506b16ae5SLuis Carlos Cobo }
72606b16ae5SLuis Carlos Cobo 
72706b16ae5SLuis Carlos Cobo module_init(lbtf_init_module);
72806b16ae5SLuis Carlos Cobo module_exit(lbtf_exit_module);
72906b16ae5SLuis Carlos Cobo 
73006b16ae5SLuis Carlos Cobo MODULE_DESCRIPTION("Libertas WLAN Thinfirm Driver Library");
73106b16ae5SLuis Carlos Cobo MODULE_AUTHOR("Cozybit Inc.");
73206b16ae5SLuis Carlos Cobo MODULE_LICENSE("GPL");
733