162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2008, cozybit Inc. 462306a36Sopenharmony_ci * Copyright (C) 2003-2006, Marvell International Ltd. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/hardirq.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/etherdevice.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include "libertas_tf.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* thinfirm version: 5.132.X.pX */ 1662306a36Sopenharmony_ci#define LBTF_FW_VER_MIN 0x05840300 1762306a36Sopenharmony_ci#define LBTF_FW_VER_MAX 0x0584ffff 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* Module parameters */ 2062306a36Sopenharmony_ciunsigned int lbtf_debug; 2162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbtf_debug); 2262306a36Sopenharmony_cimodule_param_named(libertas_tf_debug, lbtf_debug, int, 0644); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct workqueue_struct *lbtf_wq; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic const struct ieee80211_channel lbtf_channels[] = { 2762306a36Sopenharmony_ci { .center_freq = 2412, .hw_value = 1 }, 2862306a36Sopenharmony_ci { .center_freq = 2417, .hw_value = 2 }, 2962306a36Sopenharmony_ci { .center_freq = 2422, .hw_value = 3 }, 3062306a36Sopenharmony_ci { .center_freq = 2427, .hw_value = 4 }, 3162306a36Sopenharmony_ci { .center_freq = 2432, .hw_value = 5 }, 3262306a36Sopenharmony_ci { .center_freq = 2437, .hw_value = 6 }, 3362306a36Sopenharmony_ci { .center_freq = 2442, .hw_value = 7 }, 3462306a36Sopenharmony_ci { .center_freq = 2447, .hw_value = 8 }, 3562306a36Sopenharmony_ci { .center_freq = 2452, .hw_value = 9 }, 3662306a36Sopenharmony_ci { .center_freq = 2457, .hw_value = 10 }, 3762306a36Sopenharmony_ci { .center_freq = 2462, .hw_value = 11 }, 3862306a36Sopenharmony_ci { .center_freq = 2467, .hw_value = 12 }, 3962306a36Sopenharmony_ci { .center_freq = 2472, .hw_value = 13 }, 4062306a36Sopenharmony_ci { .center_freq = 2484, .hw_value = 14 }, 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* This table contains the hardware specific values for the modulation rates. */ 4462306a36Sopenharmony_cistatic const struct ieee80211_rate lbtf_rates[] = { 4562306a36Sopenharmony_ci { .bitrate = 10, 4662306a36Sopenharmony_ci .hw_value = 0, }, 4762306a36Sopenharmony_ci { .bitrate = 20, 4862306a36Sopenharmony_ci .hw_value = 1, 4962306a36Sopenharmony_ci .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 5062306a36Sopenharmony_ci { .bitrate = 55, 5162306a36Sopenharmony_ci .hw_value = 2, 5262306a36Sopenharmony_ci .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 5362306a36Sopenharmony_ci { .bitrate = 110, 5462306a36Sopenharmony_ci .hw_value = 3, 5562306a36Sopenharmony_ci .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 5662306a36Sopenharmony_ci { .bitrate = 60, 5762306a36Sopenharmony_ci .hw_value = 5, 5862306a36Sopenharmony_ci .flags = 0 }, 5962306a36Sopenharmony_ci { .bitrate = 90, 6062306a36Sopenharmony_ci .hw_value = 6, 6162306a36Sopenharmony_ci .flags = 0 }, 6262306a36Sopenharmony_ci { .bitrate = 120, 6362306a36Sopenharmony_ci .hw_value = 7, 6462306a36Sopenharmony_ci .flags = 0 }, 6562306a36Sopenharmony_ci { .bitrate = 180, 6662306a36Sopenharmony_ci .hw_value = 8, 6762306a36Sopenharmony_ci .flags = 0 }, 6862306a36Sopenharmony_ci { .bitrate = 240, 6962306a36Sopenharmony_ci .hw_value = 9, 7062306a36Sopenharmony_ci .flags = 0 }, 7162306a36Sopenharmony_ci { .bitrate = 360, 7262306a36Sopenharmony_ci .hw_value = 10, 7362306a36Sopenharmony_ci .flags = 0 }, 7462306a36Sopenharmony_ci { .bitrate = 480, 7562306a36Sopenharmony_ci .hw_value = 11, 7662306a36Sopenharmony_ci .flags = 0 }, 7762306a36Sopenharmony_ci { .bitrate = 540, 7862306a36Sopenharmony_ci .hw_value = 12, 7962306a36Sopenharmony_ci .flags = 0 }, 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void lbtf_cmd_work(struct work_struct *work) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct lbtf_private *priv = container_of(work, struct lbtf_private, 8562306a36Sopenharmony_ci cmd_work); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_CMD); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 9062306a36Sopenharmony_ci /* command response? */ 9162306a36Sopenharmony_ci if (priv->cmd_response_rxed) { 9262306a36Sopenharmony_ci priv->cmd_response_rxed = 0; 9362306a36Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 9462306a36Sopenharmony_ci lbtf_process_rx_command(priv); 9562306a36Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (priv->cmd_timed_out && priv->cur_cmd) { 9962306a36Sopenharmony_ci struct cmd_ctrl_node *cmdnode = priv->cur_cmd; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (++priv->nr_retries > 10) { 10262306a36Sopenharmony_ci lbtf_complete_command(priv, cmdnode, 10362306a36Sopenharmony_ci -ETIMEDOUT); 10462306a36Sopenharmony_ci priv->nr_retries = 0; 10562306a36Sopenharmony_ci } else { 10662306a36Sopenharmony_ci priv->cur_cmd = NULL; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Stick it back at the _top_ of the pending 10962306a36Sopenharmony_ci * queue for immediate resubmission */ 11062306a36Sopenharmony_ci list_add(&cmdnode->list, &priv->cmdpendingq); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci priv->cmd_timed_out = 0; 11462306a36Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* Execute the next command */ 11762306a36Sopenharmony_ci if (!priv->cur_cmd) 11862306a36Sopenharmony_ci lbtf_execute_next_command(priv); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_CMD); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* 12462306a36Sopenharmony_ci * This function handles the timeout of command sending. 12562306a36Sopenharmony_ci * It will re-send the same command again. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistatic void command_timer_fn(struct timer_list *t) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct lbtf_private *priv = from_timer(priv, t, command_timer); 13062306a36Sopenharmony_ci unsigned long flags; 13162306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_CMD); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (!priv->cur_cmd) { 13662306a36Sopenharmony_ci printk(KERN_DEBUG "libertastf: command timer expired; " 13762306a36Sopenharmony_ci "no pending command\n"); 13862306a36Sopenharmony_ci goto out; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci printk(KERN_DEBUG "libertas: command %x timed out\n", 14262306a36Sopenharmony_ci le16_to_cpu(priv->cur_cmd->cmdbuf->command)); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci priv->cmd_timed_out = 1; 14562306a36Sopenharmony_ci queue_work(lbtf_wq, &priv->cmd_work); 14662306a36Sopenharmony_ciout: 14762306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 14862306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_CMD); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic int lbtf_init_adapter(struct lbtf_private *priv) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MAIN); 15462306a36Sopenharmony_ci eth_broadcast_addr(priv->current_addr); 15562306a36Sopenharmony_ci mutex_init(&priv->lock); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci priv->vif = NULL; 15862306a36Sopenharmony_ci timer_setup(&priv->command_timer, command_timer_fn, 0); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci INIT_LIST_HEAD(&priv->cmdfreeq); 16162306a36Sopenharmony_ci INIT_LIST_HEAD(&priv->cmdpendingq); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci spin_lock_init(&priv->driver_lock); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* Allocate the command buffers */ 16662306a36Sopenharmony_ci if (lbtf_allocate_cmd_buffer(priv)) 16762306a36Sopenharmony_ci return -1; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MAIN); 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic void lbtf_free_adapter(struct lbtf_private *priv) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MAIN); 17662306a36Sopenharmony_ci lbtf_free_cmd_buffer(priv); 17762306a36Sopenharmony_ci del_timer(&priv->command_timer); 17862306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MAIN); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic void lbtf_op_tx(struct ieee80211_hw *hw, 18262306a36Sopenharmony_ci struct ieee80211_tx_control *control, 18362306a36Sopenharmony_ci struct sk_buff *skb) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct lbtf_private *priv = hw->priv; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci priv->skb_to_tx = skb; 18862306a36Sopenharmony_ci queue_work(lbtf_wq, &priv->tx_work); 18962306a36Sopenharmony_ci /* 19062306a36Sopenharmony_ci * queue will be restarted when we receive transmission feedback if 19162306a36Sopenharmony_ci * there are no buffered multicast frames to send 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci ieee80211_stop_queues(priv->hw); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic void lbtf_tx_work(struct work_struct *work) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct lbtf_private *priv = container_of(work, struct lbtf_private, 19962306a36Sopenharmony_ci tx_work); 20062306a36Sopenharmony_ci unsigned int len; 20162306a36Sopenharmony_ci struct ieee80211_tx_info *info; 20262306a36Sopenharmony_ci struct txpd *txpd; 20362306a36Sopenharmony_ci struct sk_buff *skb = NULL; 20462306a36Sopenharmony_ci int err; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS | LBTF_DEB_TX); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if ((priv->vif->type == NL80211_IFTYPE_AP) && 20962306a36Sopenharmony_ci (!skb_queue_empty(&priv->bc_ps_buf))) 21062306a36Sopenharmony_ci skb = skb_dequeue(&priv->bc_ps_buf); 21162306a36Sopenharmony_ci else if (priv->skb_to_tx) { 21262306a36Sopenharmony_ci skb = priv->skb_to_tx; 21362306a36Sopenharmony_ci priv->skb_to_tx = NULL; 21462306a36Sopenharmony_ci } else { 21562306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX); 21662306a36Sopenharmony_ci return; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci len = skb->len; 22062306a36Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 22162306a36Sopenharmony_ci txpd = skb_push(skb, sizeof(struct txpd)); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (priv->surpriseremoved) { 22462306a36Sopenharmony_ci dev_kfree_skb_any(skb); 22562306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX); 22662306a36Sopenharmony_ci return; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci memset(txpd, 0, sizeof(struct txpd)); 23062306a36Sopenharmony_ci /* Activate per-packet rate selection */ 23162306a36Sopenharmony_ci txpd->tx_control |= cpu_to_le32(MRVL_PER_PACKET_RATE | 23262306a36Sopenharmony_ci ieee80211_get_tx_rate(priv->hw, info)->hw_value); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* copy destination address from 802.11 header */ 23562306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(txpd->tx_dest_addr) != ETH_ALEN); 23662306a36Sopenharmony_ci memcpy(&txpd->tx_dest_addr, skb->data + sizeof(struct txpd) + 4, 23762306a36Sopenharmony_ci ETH_ALEN); 23862306a36Sopenharmony_ci txpd->tx_packet_length = cpu_to_le16(len); 23962306a36Sopenharmony_ci txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd)); 24062306a36Sopenharmony_ci lbtf_deb_hex(LBTF_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100)); 24162306a36Sopenharmony_ci BUG_ON(priv->tx_skb); 24262306a36Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 24362306a36Sopenharmony_ci priv->tx_skb = skb; 24462306a36Sopenharmony_ci err = priv->ops->hw_host_to_card(priv, MVMS_DAT, skb->data, skb->len); 24562306a36Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 24662306a36Sopenharmony_ci if (err) { 24762306a36Sopenharmony_ci dev_kfree_skb_any(skb); 24862306a36Sopenharmony_ci priv->tx_skb = NULL; 24962306a36Sopenharmony_ci pr_err("TX error: %d", err); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int lbtf_op_start(struct ieee80211_hw *hw) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct lbtf_private *priv = hw->priv; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE; 26162306a36Sopenharmony_ci priv->radioon = RADIO_ON; 26262306a36Sopenharmony_ci priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; 26362306a36Sopenharmony_ci lbtf_set_mac_control(priv); 26462306a36Sopenharmony_ci lbtf_set_radio_control(priv); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void lbtf_op_stop(struct ieee80211_hw *hw) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct lbtf_private *priv = hw->priv; 27362306a36Sopenharmony_ci unsigned long flags; 27462306a36Sopenharmony_ci struct sk_buff *skb; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci struct cmd_ctrl_node *cmdnode; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Flush pending command nodes */ 28162306a36Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 28262306a36Sopenharmony_ci list_for_each_entry(cmdnode, &priv->cmdpendingq, list) { 28362306a36Sopenharmony_ci cmdnode->result = -ENOENT; 28462306a36Sopenharmony_ci cmdnode->cmdwaitqwoken = 1; 28562306a36Sopenharmony_ci wake_up_interruptible(&cmdnode->cmdwait_q); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 28962306a36Sopenharmony_ci cancel_work_sync(&priv->cmd_work); 29062306a36Sopenharmony_ci cancel_work_sync(&priv->tx_work); 29162306a36Sopenharmony_ci while ((skb = skb_dequeue(&priv->bc_ps_buf))) 29262306a36Sopenharmony_ci dev_kfree_skb_any(skb); 29362306a36Sopenharmony_ci priv->radioon = RADIO_OFF; 29462306a36Sopenharmony_ci lbtf_set_radio_control(priv); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int lbtf_op_add_interface(struct ieee80211_hw *hw, 30062306a36Sopenharmony_ci struct ieee80211_vif *vif) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct lbtf_private *priv = hw->priv; 30362306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS); 30462306a36Sopenharmony_ci if (priv->vif != NULL) 30562306a36Sopenharmony_ci return -EOPNOTSUPP; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci priv->vif = vif; 30862306a36Sopenharmony_ci switch (vif->type) { 30962306a36Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 31062306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 31162306a36Sopenharmony_ci lbtf_set_mode(priv, LBTF_AP_MODE); 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 31462306a36Sopenharmony_ci lbtf_set_mode(priv, LBTF_STA_MODE); 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci default: 31762306a36Sopenharmony_ci priv->vif = NULL; 31862306a36Sopenharmony_ci return -EOPNOTSUPP; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci lbtf_set_mac_address(priv, (u8 *) vif->addr); 32162306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic void lbtf_op_remove_interface(struct ieee80211_hw *hw, 32662306a36Sopenharmony_ci struct ieee80211_vif *vif) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct lbtf_private *priv = hw->priv; 32962306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (priv->vif->type == NL80211_IFTYPE_AP || 33262306a36Sopenharmony_ci priv->vif->type == NL80211_IFTYPE_MESH_POINT) 33362306a36Sopenharmony_ci lbtf_beacon_ctrl(priv, 0, 0); 33462306a36Sopenharmony_ci lbtf_set_mode(priv, LBTF_PASSIVE_MODE); 33562306a36Sopenharmony_ci lbtf_set_bssid(priv, 0, NULL); 33662306a36Sopenharmony_ci priv->vif = NULL; 33762306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic int lbtf_op_config(struct ieee80211_hw *hw, u32 changed) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct lbtf_private *priv = hw->priv; 34362306a36Sopenharmony_ci struct ieee80211_conf *conf = &hw->conf; 34462306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (conf->chandef.chan->center_freq != priv->cur_freq) { 34762306a36Sopenharmony_ci priv->cur_freq = conf->chandef.chan->center_freq; 34862306a36Sopenharmony_ci lbtf_set_channel(priv, conf->chandef.chan->hw_value); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic u64 lbtf_op_prepare_multicast(struct ieee80211_hw *hw, 35562306a36Sopenharmony_ci struct netdev_hw_addr_list *mc_list) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct lbtf_private *priv = hw->priv; 35862306a36Sopenharmony_ci int i; 35962306a36Sopenharmony_ci struct netdev_hw_addr *ha; 36062306a36Sopenharmony_ci int mc_count = netdev_hw_addr_list_count(mc_list); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (!mc_count || mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) 36362306a36Sopenharmony_ci return mc_count; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci priv->nr_of_multicastmacaddr = mc_count; 36662306a36Sopenharmony_ci i = 0; 36762306a36Sopenharmony_ci netdev_hw_addr_list_for_each(ha, mc_list) 36862306a36Sopenharmony_ci memcpy(&priv->multicastlist[i++], ha->addr, ETH_ALEN); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return mc_count; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci#define SUPPORTED_FIF_FLAGS FIF_ALLMULTI 37462306a36Sopenharmony_cistatic void lbtf_op_configure_filter(struct ieee80211_hw *hw, 37562306a36Sopenharmony_ci unsigned int changed_flags, 37662306a36Sopenharmony_ci unsigned int *new_flags, 37762306a36Sopenharmony_ci u64 multicast) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct lbtf_private *priv = hw->priv; 38062306a36Sopenharmony_ci int old_mac_control = priv->mac_control; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci changed_flags &= SUPPORTED_FIF_FLAGS; 38562306a36Sopenharmony_ci *new_flags &= SUPPORTED_FIF_FLAGS; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (!changed_flags) { 38862306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 38962306a36Sopenharmony_ci return; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE; 39362306a36Sopenharmony_ci if (*new_flags & (FIF_ALLMULTI) || 39462306a36Sopenharmony_ci multicast > MRVDRV_MAX_MULTICAST_LIST_SIZE) { 39562306a36Sopenharmony_ci priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; 39662306a36Sopenharmony_ci priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; 39762306a36Sopenharmony_ci } else if (multicast) { 39862306a36Sopenharmony_ci priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; 39962306a36Sopenharmony_ci priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE; 40062306a36Sopenharmony_ci lbtf_cmd_set_mac_multicast_addr(priv); 40162306a36Sopenharmony_ci } else { 40262306a36Sopenharmony_ci priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE | 40362306a36Sopenharmony_ci CMD_ACT_MAC_ALL_MULTICAST_ENABLE); 40462306a36Sopenharmony_ci if (priv->nr_of_multicastmacaddr) { 40562306a36Sopenharmony_ci priv->nr_of_multicastmacaddr = 0; 40662306a36Sopenharmony_ci lbtf_cmd_set_mac_multicast_addr(priv); 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (priv->mac_control != old_mac_control) 41262306a36Sopenharmony_ci lbtf_set_mac_control(priv); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic void lbtf_op_bss_info_changed(struct ieee80211_hw *hw, 41862306a36Sopenharmony_ci struct ieee80211_vif *vif, 41962306a36Sopenharmony_ci struct ieee80211_bss_conf *bss_conf, 42062306a36Sopenharmony_ci u64 changes) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct lbtf_private *priv = hw->priv; 42362306a36Sopenharmony_ci struct sk_buff *beacon; 42462306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_INT)) { 42762306a36Sopenharmony_ci switch (priv->vif->type) { 42862306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 42962306a36Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 43062306a36Sopenharmony_ci beacon = ieee80211_beacon_get(hw, vif, 0); 43162306a36Sopenharmony_ci if (beacon) { 43262306a36Sopenharmony_ci lbtf_beacon_set(priv, beacon); 43362306a36Sopenharmony_ci kfree_skb(beacon); 43462306a36Sopenharmony_ci lbtf_beacon_ctrl(priv, 1, 43562306a36Sopenharmony_ci bss_conf->beacon_int); 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci break; 43862306a36Sopenharmony_ci default: 43962306a36Sopenharmony_ci break; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (changes & BSS_CHANGED_BSSID) { 44462306a36Sopenharmony_ci bool activate = !is_zero_ether_addr(bss_conf->bssid); 44562306a36Sopenharmony_ci lbtf_set_bssid(priv, activate, bss_conf->bssid); 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (changes & BSS_CHANGED_ERP_PREAMBLE) { 44962306a36Sopenharmony_ci if (bss_conf->use_short_preamble) 45062306a36Sopenharmony_ci priv->preamble = CMD_TYPE_SHORT_PREAMBLE; 45162306a36Sopenharmony_ci else 45262306a36Sopenharmony_ci priv->preamble = CMD_TYPE_LONG_PREAMBLE; 45362306a36Sopenharmony_ci lbtf_set_radio_control(priv); 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic int lbtf_op_get_survey(struct ieee80211_hw *hw, int idx, 46062306a36Sopenharmony_ci struct survey_info *survey) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct lbtf_private *priv = hw->priv; 46362306a36Sopenharmony_ci struct ieee80211_conf *conf = &hw->conf; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (idx != 0) 46662306a36Sopenharmony_ci return -ENOENT; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci survey->channel = conf->chandef.chan; 46962306a36Sopenharmony_ci survey->filled = SURVEY_INFO_NOISE_DBM; 47062306a36Sopenharmony_ci survey->noise = priv->noise; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic const struct ieee80211_ops lbtf_ops = { 47662306a36Sopenharmony_ci .tx = lbtf_op_tx, 47762306a36Sopenharmony_ci .wake_tx_queue = ieee80211_handle_wake_tx_queue, 47862306a36Sopenharmony_ci .start = lbtf_op_start, 47962306a36Sopenharmony_ci .stop = lbtf_op_stop, 48062306a36Sopenharmony_ci .add_interface = lbtf_op_add_interface, 48162306a36Sopenharmony_ci .remove_interface = lbtf_op_remove_interface, 48262306a36Sopenharmony_ci .config = lbtf_op_config, 48362306a36Sopenharmony_ci .prepare_multicast = lbtf_op_prepare_multicast, 48462306a36Sopenharmony_ci .configure_filter = lbtf_op_configure_filter, 48562306a36Sopenharmony_ci .bss_info_changed = lbtf_op_bss_info_changed, 48662306a36Sopenharmony_ci .get_survey = lbtf_op_get_survey, 48762306a36Sopenharmony_ci}; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ciint lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct ieee80211_rx_status stats; 49262306a36Sopenharmony_ci struct rxpd *prxpd; 49362306a36Sopenharmony_ci int need_padding; 49462306a36Sopenharmony_ci struct ieee80211_hdr *hdr; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_RX); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (priv->radioon != RADIO_ON) { 49962306a36Sopenharmony_ci lbtf_deb_rx("rx before we turned on the radio"); 50062306a36Sopenharmony_ci goto done; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci prxpd = (struct rxpd *) skb->data; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci memset(&stats, 0, sizeof(stats)); 50662306a36Sopenharmony_ci if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK))) 50762306a36Sopenharmony_ci stats.flag |= RX_FLAG_FAILED_FCS_CRC; 50862306a36Sopenharmony_ci stats.freq = priv->cur_freq; 50962306a36Sopenharmony_ci stats.band = NL80211_BAND_2GHZ; 51062306a36Sopenharmony_ci stats.signal = prxpd->snr - prxpd->nf; 51162306a36Sopenharmony_ci priv->noise = prxpd->nf; 51262306a36Sopenharmony_ci /* Marvell rate index has a hole at value 4 */ 51362306a36Sopenharmony_ci if (prxpd->rx_rate > 4) 51462306a36Sopenharmony_ci --prxpd->rx_rate; 51562306a36Sopenharmony_ci stats.rate_idx = prxpd->rx_rate; 51662306a36Sopenharmony_ci skb_pull(skb, sizeof(struct rxpd)); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci hdr = (struct ieee80211_hdr *)skb->data; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci need_padding = ieee80211_is_data_qos(hdr->frame_control); 52162306a36Sopenharmony_ci need_padding ^= ieee80211_has_a4(hdr->frame_control); 52262306a36Sopenharmony_ci need_padding ^= ieee80211_is_data_qos(hdr->frame_control) && 52362306a36Sopenharmony_ci (*ieee80211_get_qos_ctl(hdr) & 52462306a36Sopenharmony_ci IEEE80211_QOS_CTL_A_MSDU_PRESENT); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (need_padding) { 52762306a36Sopenharmony_ci memmove(skb->data + 2, skb->data, skb->len); 52862306a36Sopenharmony_ci skb_reserve(skb, 2); 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats)); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci lbtf_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n", 53462306a36Sopenharmony_ci skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); 53562306a36Sopenharmony_ci lbtf_deb_hex(LBTF_DEB_RX, "RX Data", skb->data, 53662306a36Sopenharmony_ci min_t(unsigned int, skb->len, 100)); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci ieee80211_rx_irqsafe(priv->hw, skb); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cidone: 54162306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_RX); 54262306a36Sopenharmony_ci return 0; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbtf_rx); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci/* 54762306a36Sopenharmony_ci * lbtf_add_card: Add and initialize the card. 54862306a36Sopenharmony_ci * 54962306a36Sopenharmony_ci * Returns: pointer to struct lbtf_priv. 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_cistruct lbtf_private *lbtf_add_card(void *card, struct device *dmdev, 55262306a36Sopenharmony_ci const struct lbtf_ops *ops) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct ieee80211_hw *hw; 55562306a36Sopenharmony_ci struct lbtf_private *priv = NULL; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MAIN); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci hw = ieee80211_alloc_hw(sizeof(struct lbtf_private), &lbtf_ops); 56062306a36Sopenharmony_ci if (!hw) 56162306a36Sopenharmony_ci goto done; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci priv = hw->priv; 56462306a36Sopenharmony_ci if (lbtf_init_adapter(priv)) 56562306a36Sopenharmony_ci goto err_init_adapter; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci priv->hw = hw; 56862306a36Sopenharmony_ci priv->card = card; 56962306a36Sopenharmony_ci priv->ops = ops; 57062306a36Sopenharmony_ci priv->tx_skb = NULL; 57162306a36Sopenharmony_ci priv->radioon = RADIO_OFF; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci hw->queues = 1; 57462306a36Sopenharmony_ci ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); 57562306a36Sopenharmony_ci ieee80211_hw_set(hw, SIGNAL_DBM); 57662306a36Sopenharmony_ci hw->extra_tx_headroom = sizeof(struct txpd); 57762306a36Sopenharmony_ci memcpy(priv->channels, lbtf_channels, sizeof(lbtf_channels)); 57862306a36Sopenharmony_ci memcpy(priv->rates, lbtf_rates, sizeof(lbtf_rates)); 57962306a36Sopenharmony_ci priv->band.n_bitrates = ARRAY_SIZE(lbtf_rates); 58062306a36Sopenharmony_ci priv->band.bitrates = priv->rates; 58162306a36Sopenharmony_ci priv->band.n_channels = ARRAY_SIZE(lbtf_channels); 58262306a36Sopenharmony_ci priv->band.channels = priv->channels; 58362306a36Sopenharmony_ci hw->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band; 58462306a36Sopenharmony_ci hw->wiphy->interface_modes = 58562306a36Sopenharmony_ci BIT(NL80211_IFTYPE_STATION) | 58662306a36Sopenharmony_ci BIT(NL80211_IFTYPE_ADHOC); 58762306a36Sopenharmony_ci skb_queue_head_init(&priv->bc_ps_buf); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci SET_IEEE80211_DEV(hw, dmdev); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci INIT_WORK(&priv->cmd_work, lbtf_cmd_work); 59462306a36Sopenharmony_ci INIT_WORK(&priv->tx_work, lbtf_tx_work); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (priv->ops->hw_prog_firmware(priv)) { 59762306a36Sopenharmony_ci lbtf_deb_usbd(dmdev, "Error programming the firmware\n"); 59862306a36Sopenharmony_ci priv->ops->hw_reset_device(priv); 59962306a36Sopenharmony_ci goto err_init_adapter; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci eth_broadcast_addr(priv->current_addr); 60362306a36Sopenharmony_ci if (lbtf_update_hw_spec(priv)) 60462306a36Sopenharmony_ci goto err_init_adapter; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (priv->fwrelease < LBTF_FW_VER_MIN || 60762306a36Sopenharmony_ci priv->fwrelease > LBTF_FW_VER_MAX) { 60862306a36Sopenharmony_ci goto err_init_adapter; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* The firmware seems to start with the radio enabled. Turn it 61262306a36Sopenharmony_ci * off before an actual mac80211 start callback is invoked. 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_ci lbtf_set_radio_control(priv); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (ieee80211_register_hw(hw)) 61762306a36Sopenharmony_ci goto err_init_adapter; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci dev_info(dmdev, "libertastf: Marvell WLAN 802.11 thinfirm adapter\n"); 62062306a36Sopenharmony_ci goto done; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cierr_init_adapter: 62362306a36Sopenharmony_ci lbtf_free_adapter(priv); 62462306a36Sopenharmony_ci ieee80211_free_hw(hw); 62562306a36Sopenharmony_ci priv = NULL; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cidone: 62862306a36Sopenharmony_ci lbtf_deb_leave_args(LBTF_DEB_MAIN, "priv %p", priv); 62962306a36Sopenharmony_ci return priv; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbtf_add_card); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ciint lbtf_remove_card(struct lbtf_private *priv) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci struct ieee80211_hw *hw = priv->hw; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MAIN); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci priv->surpriseremoved = 1; 64162306a36Sopenharmony_ci del_timer(&priv->command_timer); 64262306a36Sopenharmony_ci lbtf_free_adapter(priv); 64362306a36Sopenharmony_ci priv->hw = NULL; 64462306a36Sopenharmony_ci ieee80211_unregister_hw(hw); 64562306a36Sopenharmony_ci ieee80211_free_hw(hw); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MAIN); 64862306a36Sopenharmony_ci return 0; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbtf_remove_card); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_civoid lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci ieee80211_tx_info_clear_status(info); 65762306a36Sopenharmony_ci /* 65862306a36Sopenharmony_ci * Commented out, otherwise we never go beyond 1Mbit/s using mac80211 65962306a36Sopenharmony_ci * default pid rc algorithm. 66062306a36Sopenharmony_ci * 66162306a36Sopenharmony_ci * info->status.retry_count = MRVL_DEFAULT_RETRIES - retrycnt; 66262306a36Sopenharmony_ci */ 66362306a36Sopenharmony_ci if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !fail) 66462306a36Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_ACK; 66562306a36Sopenharmony_ci skb_pull(priv->tx_skb, sizeof(struct txpd)); 66662306a36Sopenharmony_ci ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb); 66762306a36Sopenharmony_ci priv->tx_skb = NULL; 66862306a36Sopenharmony_ci if (!priv->skb_to_tx && skb_queue_empty(&priv->bc_ps_buf)) 66962306a36Sopenharmony_ci ieee80211_wake_queues(priv->hw); 67062306a36Sopenharmony_ci else 67162306a36Sopenharmony_ci queue_work(lbtf_wq, &priv->tx_work); 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbtf_send_tx_feedback); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_civoid lbtf_bcn_sent(struct lbtf_private *priv) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci struct sk_buff *skb = NULL; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (priv->vif->type != NL80211_IFTYPE_AP) 68062306a36Sopenharmony_ci return; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (skb_queue_empty(&priv->bc_ps_buf)) { 68362306a36Sopenharmony_ci bool tx_buff_bc = false; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci while ((skb = ieee80211_get_buffered_bc(priv->hw, priv->vif))) { 68662306a36Sopenharmony_ci skb_queue_tail(&priv->bc_ps_buf, skb); 68762306a36Sopenharmony_ci tx_buff_bc = true; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci if (tx_buff_bc) { 69062306a36Sopenharmony_ci ieee80211_stop_queues(priv->hw); 69162306a36Sopenharmony_ci queue_work(lbtf_wq, &priv->tx_work); 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci skb = ieee80211_beacon_get(priv->hw, priv->vif, 0); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (skb) { 69862306a36Sopenharmony_ci lbtf_beacon_set(priv, skb); 69962306a36Sopenharmony_ci kfree_skb(skb); 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbtf_bcn_sent); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic int __init lbtf_init_module(void) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MAIN); 70762306a36Sopenharmony_ci lbtf_wq = alloc_workqueue("libertastf", WQ_MEM_RECLAIM, 0); 70862306a36Sopenharmony_ci if (lbtf_wq == NULL) { 70962306a36Sopenharmony_ci printk(KERN_ERR "libertastf: couldn't create workqueue\n"); 71062306a36Sopenharmony_ci return -ENOMEM; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MAIN); 71362306a36Sopenharmony_ci return 0; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic void __exit lbtf_exit_module(void) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MAIN); 71962306a36Sopenharmony_ci destroy_workqueue(lbtf_wq); 72062306a36Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MAIN); 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cimodule_init(lbtf_init_module); 72462306a36Sopenharmony_cimodule_exit(lbtf_exit_module); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ciMODULE_DESCRIPTION("Libertas WLAN Thinfirm Driver Library"); 72762306a36Sopenharmony_ciMODULE_AUTHOR("Cozybit Inc."); 72862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 729