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