18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2008, cozybit Inc. 48c2ecf20Sopenharmony_ci * Copyright (C) 2003-2006, Marvell International Ltd. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/hardirq.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include "libertas_tf.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* thinfirm version: 5.132.X.pX */ 168c2ecf20Sopenharmony_ci#define LBTF_FW_VER_MIN 0x05840300 178c2ecf20Sopenharmony_ci#define LBTF_FW_VER_MAX 0x0584ffff 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* Module parameters */ 208c2ecf20Sopenharmony_ciunsigned int lbtf_debug; 218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbtf_debug); 228c2ecf20Sopenharmony_cimodule_param_named(libertas_tf_debug, lbtf_debug, int, 0644); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct workqueue_struct *lbtf_wq; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic const struct ieee80211_channel lbtf_channels[] = { 278c2ecf20Sopenharmony_ci { .center_freq = 2412, .hw_value = 1 }, 288c2ecf20Sopenharmony_ci { .center_freq = 2417, .hw_value = 2 }, 298c2ecf20Sopenharmony_ci { .center_freq = 2422, .hw_value = 3 }, 308c2ecf20Sopenharmony_ci { .center_freq = 2427, .hw_value = 4 }, 318c2ecf20Sopenharmony_ci { .center_freq = 2432, .hw_value = 5 }, 328c2ecf20Sopenharmony_ci { .center_freq = 2437, .hw_value = 6 }, 338c2ecf20Sopenharmony_ci { .center_freq = 2442, .hw_value = 7 }, 348c2ecf20Sopenharmony_ci { .center_freq = 2447, .hw_value = 8 }, 358c2ecf20Sopenharmony_ci { .center_freq = 2452, .hw_value = 9 }, 368c2ecf20Sopenharmony_ci { .center_freq = 2457, .hw_value = 10 }, 378c2ecf20Sopenharmony_ci { .center_freq = 2462, .hw_value = 11 }, 388c2ecf20Sopenharmony_ci { .center_freq = 2467, .hw_value = 12 }, 398c2ecf20Sopenharmony_ci { .center_freq = 2472, .hw_value = 13 }, 408c2ecf20Sopenharmony_ci { .center_freq = 2484, .hw_value = 14 }, 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* This table contains the hardware specific values for the modulation rates. */ 448c2ecf20Sopenharmony_cistatic const struct ieee80211_rate lbtf_rates[] = { 458c2ecf20Sopenharmony_ci { .bitrate = 10, 468c2ecf20Sopenharmony_ci .hw_value = 0, }, 478c2ecf20Sopenharmony_ci { .bitrate = 20, 488c2ecf20Sopenharmony_ci .hw_value = 1, 498c2ecf20Sopenharmony_ci .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 508c2ecf20Sopenharmony_ci { .bitrate = 55, 518c2ecf20Sopenharmony_ci .hw_value = 2, 528c2ecf20Sopenharmony_ci .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 538c2ecf20Sopenharmony_ci { .bitrate = 110, 548c2ecf20Sopenharmony_ci .hw_value = 3, 558c2ecf20Sopenharmony_ci .flags = IEEE80211_RATE_SHORT_PREAMBLE }, 568c2ecf20Sopenharmony_ci { .bitrate = 60, 578c2ecf20Sopenharmony_ci .hw_value = 5, 588c2ecf20Sopenharmony_ci .flags = 0 }, 598c2ecf20Sopenharmony_ci { .bitrate = 90, 608c2ecf20Sopenharmony_ci .hw_value = 6, 618c2ecf20Sopenharmony_ci .flags = 0 }, 628c2ecf20Sopenharmony_ci { .bitrate = 120, 638c2ecf20Sopenharmony_ci .hw_value = 7, 648c2ecf20Sopenharmony_ci .flags = 0 }, 658c2ecf20Sopenharmony_ci { .bitrate = 180, 668c2ecf20Sopenharmony_ci .hw_value = 8, 678c2ecf20Sopenharmony_ci .flags = 0 }, 688c2ecf20Sopenharmony_ci { .bitrate = 240, 698c2ecf20Sopenharmony_ci .hw_value = 9, 708c2ecf20Sopenharmony_ci .flags = 0 }, 718c2ecf20Sopenharmony_ci { .bitrate = 360, 728c2ecf20Sopenharmony_ci .hw_value = 10, 738c2ecf20Sopenharmony_ci .flags = 0 }, 748c2ecf20Sopenharmony_ci { .bitrate = 480, 758c2ecf20Sopenharmony_ci .hw_value = 11, 768c2ecf20Sopenharmony_ci .flags = 0 }, 778c2ecf20Sopenharmony_ci { .bitrate = 540, 788c2ecf20Sopenharmony_ci .hw_value = 12, 798c2ecf20Sopenharmony_ci .flags = 0 }, 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void lbtf_cmd_work(struct work_struct *work) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct lbtf_private *priv = container_of(work, struct lbtf_private, 858c2ecf20Sopenharmony_ci cmd_work); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_CMD); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 908c2ecf20Sopenharmony_ci /* command response? */ 918c2ecf20Sopenharmony_ci if (priv->cmd_response_rxed) { 928c2ecf20Sopenharmony_ci priv->cmd_response_rxed = 0; 938c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 948c2ecf20Sopenharmony_ci lbtf_process_rx_command(priv); 958c2ecf20Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (priv->cmd_timed_out && priv->cur_cmd) { 998c2ecf20Sopenharmony_ci struct cmd_ctrl_node *cmdnode = priv->cur_cmd; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (++priv->nr_retries > 10) { 1028c2ecf20Sopenharmony_ci lbtf_complete_command(priv, cmdnode, 1038c2ecf20Sopenharmony_ci -ETIMEDOUT); 1048c2ecf20Sopenharmony_ci priv->nr_retries = 0; 1058c2ecf20Sopenharmony_ci } else { 1068c2ecf20Sopenharmony_ci priv->cur_cmd = NULL; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* Stick it back at the _top_ of the pending 1098c2ecf20Sopenharmony_ci * queue for immediate resubmission */ 1108c2ecf20Sopenharmony_ci list_add(&cmdnode->list, &priv->cmdpendingq); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci priv->cmd_timed_out = 0; 1148c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* Execute the next command */ 1178c2ecf20Sopenharmony_ci if (!priv->cur_cmd) 1188c2ecf20Sopenharmony_ci lbtf_execute_next_command(priv); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_CMD); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* 1248c2ecf20Sopenharmony_ci * This function handles the timeout of command sending. 1258c2ecf20Sopenharmony_ci * It will re-send the same command again. 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_cistatic void command_timer_fn(struct timer_list *t) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct lbtf_private *priv = from_timer(priv, t, command_timer); 1308c2ecf20Sopenharmony_ci unsigned long flags; 1318c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_CMD); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (!priv->cur_cmd) { 1368c2ecf20Sopenharmony_ci printk(KERN_DEBUG "libertastf: command timer expired; " 1378c2ecf20Sopenharmony_ci "no pending command\n"); 1388c2ecf20Sopenharmony_ci goto out; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci printk(KERN_DEBUG "libertas: command %x timed out\n", 1428c2ecf20Sopenharmony_ci le16_to_cpu(priv->cur_cmd->cmdbuf->command)); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci priv->cmd_timed_out = 1; 1458c2ecf20Sopenharmony_ci queue_work(lbtf_wq, &priv->cmd_work); 1468c2ecf20Sopenharmony_ciout: 1478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 1488c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_CMD); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int lbtf_init_adapter(struct lbtf_private *priv) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MAIN); 1548c2ecf20Sopenharmony_ci eth_broadcast_addr(priv->current_addr); 1558c2ecf20Sopenharmony_ci mutex_init(&priv->lock); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci priv->vif = NULL; 1588c2ecf20Sopenharmony_ci timer_setup(&priv->command_timer, command_timer_fn, 0); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&priv->cmdfreeq); 1618c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&priv->cmdpendingq); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci spin_lock_init(&priv->driver_lock); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Allocate the command buffers */ 1668c2ecf20Sopenharmony_ci if (lbtf_allocate_cmd_buffer(priv)) 1678c2ecf20Sopenharmony_ci return -1; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MAIN); 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic void lbtf_free_adapter(struct lbtf_private *priv) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MAIN); 1768c2ecf20Sopenharmony_ci lbtf_free_cmd_buffer(priv); 1778c2ecf20Sopenharmony_ci del_timer(&priv->command_timer); 1788c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MAIN); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void lbtf_op_tx(struct ieee80211_hw *hw, 1828c2ecf20Sopenharmony_ci struct ieee80211_tx_control *control, 1838c2ecf20Sopenharmony_ci struct sk_buff *skb) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct lbtf_private *priv = hw->priv; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci priv->skb_to_tx = skb; 1888c2ecf20Sopenharmony_ci queue_work(lbtf_wq, &priv->tx_work); 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * queue will be restarted when we receive transmission feedback if 1918c2ecf20Sopenharmony_ci * there are no buffered multicast frames to send 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci ieee80211_stop_queues(priv->hw); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void lbtf_tx_work(struct work_struct *work) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct lbtf_private *priv = container_of(work, struct lbtf_private, 1998c2ecf20Sopenharmony_ci tx_work); 2008c2ecf20Sopenharmony_ci unsigned int len; 2018c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info; 2028c2ecf20Sopenharmony_ci struct txpd *txpd; 2038c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 2048c2ecf20Sopenharmony_ci int err; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS | LBTF_DEB_TX); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if ((priv->vif->type == NL80211_IFTYPE_AP) && 2098c2ecf20Sopenharmony_ci (!skb_queue_empty(&priv->bc_ps_buf))) 2108c2ecf20Sopenharmony_ci skb = skb_dequeue(&priv->bc_ps_buf); 2118c2ecf20Sopenharmony_ci else if (priv->skb_to_tx) { 2128c2ecf20Sopenharmony_ci skb = priv->skb_to_tx; 2138c2ecf20Sopenharmony_ci priv->skb_to_tx = NULL; 2148c2ecf20Sopenharmony_ci } else { 2158c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX); 2168c2ecf20Sopenharmony_ci return; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci len = skb->len; 2208c2ecf20Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 2218c2ecf20Sopenharmony_ci txpd = skb_push(skb, sizeof(struct txpd)); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (priv->surpriseremoved) { 2248c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 2258c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX); 2268c2ecf20Sopenharmony_ci return; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci memset(txpd, 0, sizeof(struct txpd)); 2308c2ecf20Sopenharmony_ci /* Activate per-packet rate selection */ 2318c2ecf20Sopenharmony_ci txpd->tx_control |= cpu_to_le32(MRVL_PER_PACKET_RATE | 2328c2ecf20Sopenharmony_ci ieee80211_get_tx_rate(priv->hw, info)->hw_value); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* copy destination address from 802.11 header */ 2358c2ecf20Sopenharmony_ci memcpy(txpd->tx_dest_addr_high, skb->data + sizeof(struct txpd) + 4, 2368c2ecf20Sopenharmony_ci ETH_ALEN); 2378c2ecf20Sopenharmony_ci txpd->tx_packet_length = cpu_to_le16(len); 2388c2ecf20Sopenharmony_ci txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd)); 2398c2ecf20Sopenharmony_ci lbtf_deb_hex(LBTF_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100)); 2408c2ecf20Sopenharmony_ci BUG_ON(priv->tx_skb); 2418c2ecf20Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 2428c2ecf20Sopenharmony_ci priv->tx_skb = skb; 2438c2ecf20Sopenharmony_ci err = priv->ops->hw_host_to_card(priv, MVMS_DAT, skb->data, skb->len); 2448c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 2458c2ecf20Sopenharmony_ci if (err) { 2468c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 2478c2ecf20Sopenharmony_ci priv->tx_skb = NULL; 2488c2ecf20Sopenharmony_ci pr_err("TX error: %d", err); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX); 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int lbtf_op_start(struct ieee80211_hw *hw) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct lbtf_private *priv = hw->priv; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE; 2608c2ecf20Sopenharmony_ci priv->radioon = RADIO_ON; 2618c2ecf20Sopenharmony_ci priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; 2628c2ecf20Sopenharmony_ci lbtf_set_mac_control(priv); 2638c2ecf20Sopenharmony_ci lbtf_set_radio_control(priv); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic void lbtf_op_stop(struct ieee80211_hw *hw) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct lbtf_private *priv = hw->priv; 2728c2ecf20Sopenharmony_ci unsigned long flags; 2738c2ecf20Sopenharmony_ci struct sk_buff *skb; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci struct cmd_ctrl_node *cmdnode; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* Flush pending command nodes */ 2808c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 2818c2ecf20Sopenharmony_ci list_for_each_entry(cmdnode, &priv->cmdpendingq, list) { 2828c2ecf20Sopenharmony_ci cmdnode->result = -ENOENT; 2838c2ecf20Sopenharmony_ci cmdnode->cmdwaitqwoken = 1; 2848c2ecf20Sopenharmony_ci wake_up_interruptible(&cmdnode->cmdwait_q); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 2888c2ecf20Sopenharmony_ci cancel_work_sync(&priv->cmd_work); 2898c2ecf20Sopenharmony_ci cancel_work_sync(&priv->tx_work); 2908c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&priv->bc_ps_buf))) 2918c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 2928c2ecf20Sopenharmony_ci priv->radioon = RADIO_OFF; 2938c2ecf20Sopenharmony_ci lbtf_set_radio_control(priv); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int lbtf_op_add_interface(struct ieee80211_hw *hw, 2998c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct lbtf_private *priv = hw->priv; 3028c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS); 3038c2ecf20Sopenharmony_ci if (priv->vif != NULL) 3048c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci priv->vif = vif; 3078c2ecf20Sopenharmony_ci switch (vif->type) { 3088c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 3098c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 3108c2ecf20Sopenharmony_ci lbtf_set_mode(priv, LBTF_AP_MODE); 3118c2ecf20Sopenharmony_ci break; 3128c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 3138c2ecf20Sopenharmony_ci lbtf_set_mode(priv, LBTF_STA_MODE); 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci default: 3168c2ecf20Sopenharmony_ci priv->vif = NULL; 3178c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci lbtf_set_mac_address(priv, (u8 *) vif->addr); 3208c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic void lbtf_op_remove_interface(struct ieee80211_hw *hw, 3258c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct lbtf_private *priv = hw->priv; 3288c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (priv->vif->type == NL80211_IFTYPE_AP || 3318c2ecf20Sopenharmony_ci priv->vif->type == NL80211_IFTYPE_MESH_POINT) 3328c2ecf20Sopenharmony_ci lbtf_beacon_ctrl(priv, 0, 0); 3338c2ecf20Sopenharmony_ci lbtf_set_mode(priv, LBTF_PASSIVE_MODE); 3348c2ecf20Sopenharmony_ci lbtf_set_bssid(priv, 0, NULL); 3358c2ecf20Sopenharmony_ci priv->vif = NULL; 3368c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int lbtf_op_config(struct ieee80211_hw *hw, u32 changed) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct lbtf_private *priv = hw->priv; 3428c2ecf20Sopenharmony_ci struct ieee80211_conf *conf = &hw->conf; 3438c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (conf->chandef.chan->center_freq != priv->cur_freq) { 3468c2ecf20Sopenharmony_ci priv->cur_freq = conf->chandef.chan->center_freq; 3478c2ecf20Sopenharmony_ci lbtf_set_channel(priv, conf->chandef.chan->hw_value); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 3508c2ecf20Sopenharmony_ci return 0; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic u64 lbtf_op_prepare_multicast(struct ieee80211_hw *hw, 3548c2ecf20Sopenharmony_ci struct netdev_hw_addr_list *mc_list) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct lbtf_private *priv = hw->priv; 3578c2ecf20Sopenharmony_ci int i; 3588c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 3598c2ecf20Sopenharmony_ci int mc_count = netdev_hw_addr_list_count(mc_list); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (!mc_count || mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) 3628c2ecf20Sopenharmony_ci return mc_count; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci priv->nr_of_multicastmacaddr = mc_count; 3658c2ecf20Sopenharmony_ci i = 0; 3668c2ecf20Sopenharmony_ci netdev_hw_addr_list_for_each(ha, mc_list) 3678c2ecf20Sopenharmony_ci memcpy(&priv->multicastlist[i++], ha->addr, ETH_ALEN); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return mc_count; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci#define SUPPORTED_FIF_FLAGS FIF_ALLMULTI 3738c2ecf20Sopenharmony_cistatic void lbtf_op_configure_filter(struct ieee80211_hw *hw, 3748c2ecf20Sopenharmony_ci unsigned int changed_flags, 3758c2ecf20Sopenharmony_ci unsigned int *new_flags, 3768c2ecf20Sopenharmony_ci u64 multicast) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct lbtf_private *priv = hw->priv; 3798c2ecf20Sopenharmony_ci int old_mac_control = priv->mac_control; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci changed_flags &= SUPPORTED_FIF_FLAGS; 3848c2ecf20Sopenharmony_ci *new_flags &= SUPPORTED_FIF_FLAGS; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (!changed_flags) { 3878c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 3888c2ecf20Sopenharmony_ci return; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE; 3928c2ecf20Sopenharmony_ci if (*new_flags & (FIF_ALLMULTI) || 3938c2ecf20Sopenharmony_ci multicast > MRVDRV_MAX_MULTICAST_LIST_SIZE) { 3948c2ecf20Sopenharmony_ci priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; 3958c2ecf20Sopenharmony_ci priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; 3968c2ecf20Sopenharmony_ci } else if (multicast) { 3978c2ecf20Sopenharmony_ci priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; 3988c2ecf20Sopenharmony_ci priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE; 3998c2ecf20Sopenharmony_ci lbtf_cmd_set_mac_multicast_addr(priv); 4008c2ecf20Sopenharmony_ci } else { 4018c2ecf20Sopenharmony_ci priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE | 4028c2ecf20Sopenharmony_ci CMD_ACT_MAC_ALL_MULTICAST_ENABLE); 4038c2ecf20Sopenharmony_ci if (priv->nr_of_multicastmacaddr) { 4048c2ecf20Sopenharmony_ci priv->nr_of_multicastmacaddr = 0; 4058c2ecf20Sopenharmony_ci lbtf_cmd_set_mac_multicast_addr(priv); 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (priv->mac_control != old_mac_control) 4118c2ecf20Sopenharmony_ci lbtf_set_mac_control(priv); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic void lbtf_op_bss_info_changed(struct ieee80211_hw *hw, 4178c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 4188c2ecf20Sopenharmony_ci struct ieee80211_bss_conf *bss_conf, 4198c2ecf20Sopenharmony_ci u32 changes) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci struct lbtf_private *priv = hw->priv; 4228c2ecf20Sopenharmony_ci struct sk_buff *beacon; 4238c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MACOPS); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_INT)) { 4268c2ecf20Sopenharmony_ci switch (priv->vif->type) { 4278c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 4288c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 4298c2ecf20Sopenharmony_ci beacon = ieee80211_beacon_get(hw, vif); 4308c2ecf20Sopenharmony_ci if (beacon) { 4318c2ecf20Sopenharmony_ci lbtf_beacon_set(priv, beacon); 4328c2ecf20Sopenharmony_ci kfree_skb(beacon); 4338c2ecf20Sopenharmony_ci lbtf_beacon_ctrl(priv, 1, 4348c2ecf20Sopenharmony_ci bss_conf->beacon_int); 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci default: 4388c2ecf20Sopenharmony_ci break; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (changes & BSS_CHANGED_BSSID) { 4438c2ecf20Sopenharmony_ci bool activate = !is_zero_ether_addr(bss_conf->bssid); 4448c2ecf20Sopenharmony_ci lbtf_set_bssid(priv, activate, bss_conf->bssid); 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (changes & BSS_CHANGED_ERP_PREAMBLE) { 4488c2ecf20Sopenharmony_ci if (bss_conf->use_short_preamble) 4498c2ecf20Sopenharmony_ci priv->preamble = CMD_TYPE_SHORT_PREAMBLE; 4508c2ecf20Sopenharmony_ci else 4518c2ecf20Sopenharmony_ci priv->preamble = CMD_TYPE_LONG_PREAMBLE; 4528c2ecf20Sopenharmony_ci lbtf_set_radio_control(priv); 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MACOPS); 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic int lbtf_op_get_survey(struct ieee80211_hw *hw, int idx, 4598c2ecf20Sopenharmony_ci struct survey_info *survey) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci struct lbtf_private *priv = hw->priv; 4628c2ecf20Sopenharmony_ci struct ieee80211_conf *conf = &hw->conf; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (idx != 0) 4658c2ecf20Sopenharmony_ci return -ENOENT; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci survey->channel = conf->chandef.chan; 4688c2ecf20Sopenharmony_ci survey->filled = SURVEY_INFO_NOISE_DBM; 4698c2ecf20Sopenharmony_ci survey->noise = priv->noise; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic const struct ieee80211_ops lbtf_ops = { 4758c2ecf20Sopenharmony_ci .tx = lbtf_op_tx, 4768c2ecf20Sopenharmony_ci .start = lbtf_op_start, 4778c2ecf20Sopenharmony_ci .stop = lbtf_op_stop, 4788c2ecf20Sopenharmony_ci .add_interface = lbtf_op_add_interface, 4798c2ecf20Sopenharmony_ci .remove_interface = lbtf_op_remove_interface, 4808c2ecf20Sopenharmony_ci .config = lbtf_op_config, 4818c2ecf20Sopenharmony_ci .prepare_multicast = lbtf_op_prepare_multicast, 4828c2ecf20Sopenharmony_ci .configure_filter = lbtf_op_configure_filter, 4838c2ecf20Sopenharmony_ci .bss_info_changed = lbtf_op_bss_info_changed, 4848c2ecf20Sopenharmony_ci .get_survey = lbtf_op_get_survey, 4858c2ecf20Sopenharmony_ci}; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ciint lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct ieee80211_rx_status stats; 4908c2ecf20Sopenharmony_ci struct rxpd *prxpd; 4918c2ecf20Sopenharmony_ci int need_padding; 4928c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_RX); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (priv->radioon != RADIO_ON) { 4978c2ecf20Sopenharmony_ci lbtf_deb_rx("rx before we turned on the radio"); 4988c2ecf20Sopenharmony_ci goto done; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci prxpd = (struct rxpd *) skb->data; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci memset(&stats, 0, sizeof(stats)); 5048c2ecf20Sopenharmony_ci if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK))) 5058c2ecf20Sopenharmony_ci stats.flag |= RX_FLAG_FAILED_FCS_CRC; 5068c2ecf20Sopenharmony_ci stats.freq = priv->cur_freq; 5078c2ecf20Sopenharmony_ci stats.band = NL80211_BAND_2GHZ; 5088c2ecf20Sopenharmony_ci stats.signal = prxpd->snr - prxpd->nf; 5098c2ecf20Sopenharmony_ci priv->noise = prxpd->nf; 5108c2ecf20Sopenharmony_ci /* Marvell rate index has a hole at value 4 */ 5118c2ecf20Sopenharmony_ci if (prxpd->rx_rate > 4) 5128c2ecf20Sopenharmony_ci --prxpd->rx_rate; 5138c2ecf20Sopenharmony_ci stats.rate_idx = prxpd->rx_rate; 5148c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(struct rxpd)); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci hdr = (struct ieee80211_hdr *)skb->data; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci need_padding = ieee80211_is_data_qos(hdr->frame_control); 5198c2ecf20Sopenharmony_ci need_padding ^= ieee80211_has_a4(hdr->frame_control); 5208c2ecf20Sopenharmony_ci need_padding ^= ieee80211_is_data_qos(hdr->frame_control) && 5218c2ecf20Sopenharmony_ci (*ieee80211_get_qos_ctl(hdr) & 5228c2ecf20Sopenharmony_ci IEEE80211_QOS_CTL_A_MSDU_PRESENT); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (need_padding) { 5258c2ecf20Sopenharmony_ci memmove(skb->data + 2, skb->data, skb->len); 5268c2ecf20Sopenharmony_ci skb_reserve(skb, 2); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats)); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci lbtf_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n", 5328c2ecf20Sopenharmony_ci skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); 5338c2ecf20Sopenharmony_ci lbtf_deb_hex(LBTF_DEB_RX, "RX Data", skb->data, 5348c2ecf20Sopenharmony_ci min_t(unsigned int, skb->len, 100)); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci ieee80211_rx_irqsafe(priv->hw, skb); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cidone: 5398c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_RX); 5408c2ecf20Sopenharmony_ci return 0; 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbtf_rx); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci/* 5458c2ecf20Sopenharmony_ci * lbtf_add_card: Add and initialize the card. 5468c2ecf20Sopenharmony_ci * 5478c2ecf20Sopenharmony_ci * Returns: pointer to struct lbtf_priv. 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_cistruct lbtf_private *lbtf_add_card(void *card, struct device *dmdev, 5508c2ecf20Sopenharmony_ci const struct lbtf_ops *ops) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct ieee80211_hw *hw; 5538c2ecf20Sopenharmony_ci struct lbtf_private *priv = NULL; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MAIN); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci hw = ieee80211_alloc_hw(sizeof(struct lbtf_private), &lbtf_ops); 5588c2ecf20Sopenharmony_ci if (!hw) 5598c2ecf20Sopenharmony_ci goto done; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci priv = hw->priv; 5628c2ecf20Sopenharmony_ci if (lbtf_init_adapter(priv)) 5638c2ecf20Sopenharmony_ci goto err_init_adapter; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci priv->hw = hw; 5668c2ecf20Sopenharmony_ci priv->card = card; 5678c2ecf20Sopenharmony_ci priv->ops = ops; 5688c2ecf20Sopenharmony_ci priv->tx_skb = NULL; 5698c2ecf20Sopenharmony_ci priv->radioon = RADIO_OFF; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci hw->queues = 1; 5728c2ecf20Sopenharmony_ci ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); 5738c2ecf20Sopenharmony_ci ieee80211_hw_set(hw, SIGNAL_DBM); 5748c2ecf20Sopenharmony_ci hw->extra_tx_headroom = sizeof(struct txpd); 5758c2ecf20Sopenharmony_ci memcpy(priv->channels, lbtf_channels, sizeof(lbtf_channels)); 5768c2ecf20Sopenharmony_ci memcpy(priv->rates, lbtf_rates, sizeof(lbtf_rates)); 5778c2ecf20Sopenharmony_ci priv->band.n_bitrates = ARRAY_SIZE(lbtf_rates); 5788c2ecf20Sopenharmony_ci priv->band.bitrates = priv->rates; 5798c2ecf20Sopenharmony_ci priv->band.n_channels = ARRAY_SIZE(lbtf_channels); 5808c2ecf20Sopenharmony_ci priv->band.channels = priv->channels; 5818c2ecf20Sopenharmony_ci hw->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band; 5828c2ecf20Sopenharmony_ci hw->wiphy->interface_modes = 5838c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_STATION) | 5848c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_ADHOC); 5858c2ecf20Sopenharmony_ci skb_queue_head_init(&priv->bc_ps_buf); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci SET_IEEE80211_DEV(hw, dmdev); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci INIT_WORK(&priv->cmd_work, lbtf_cmd_work); 5928c2ecf20Sopenharmony_ci INIT_WORK(&priv->tx_work, lbtf_tx_work); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (priv->ops->hw_prog_firmware(priv)) { 5958c2ecf20Sopenharmony_ci lbtf_deb_usbd(dmdev, "Error programming the firmware\n"); 5968c2ecf20Sopenharmony_ci priv->ops->hw_reset_device(priv); 5978c2ecf20Sopenharmony_ci goto err_init_adapter; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci eth_broadcast_addr(priv->current_addr); 6018c2ecf20Sopenharmony_ci if (lbtf_update_hw_spec(priv)) 6028c2ecf20Sopenharmony_ci goto err_init_adapter; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (priv->fwrelease < LBTF_FW_VER_MIN || 6058c2ecf20Sopenharmony_ci priv->fwrelease > LBTF_FW_VER_MAX) { 6068c2ecf20Sopenharmony_ci goto err_init_adapter; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* The firmware seems to start with the radio enabled. Turn it 6108c2ecf20Sopenharmony_ci * off before an actual mac80211 start callback is invoked. 6118c2ecf20Sopenharmony_ci */ 6128c2ecf20Sopenharmony_ci lbtf_set_radio_control(priv); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (ieee80211_register_hw(hw)) 6158c2ecf20Sopenharmony_ci goto err_init_adapter; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci dev_info(dmdev, "libertastf: Marvell WLAN 802.11 thinfirm adapter\n"); 6188c2ecf20Sopenharmony_ci goto done; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cierr_init_adapter: 6218c2ecf20Sopenharmony_ci lbtf_free_adapter(priv); 6228c2ecf20Sopenharmony_ci ieee80211_free_hw(hw); 6238c2ecf20Sopenharmony_ci priv = NULL; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cidone: 6268c2ecf20Sopenharmony_ci lbtf_deb_leave_args(LBTF_DEB_MAIN, "priv %p", priv); 6278c2ecf20Sopenharmony_ci return priv; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbtf_add_card); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ciint lbtf_remove_card(struct lbtf_private *priv) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = priv->hw; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MAIN); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci priv->surpriseremoved = 1; 6398c2ecf20Sopenharmony_ci del_timer(&priv->command_timer); 6408c2ecf20Sopenharmony_ci lbtf_free_adapter(priv); 6418c2ecf20Sopenharmony_ci priv->hw = NULL; 6428c2ecf20Sopenharmony_ci ieee80211_unregister_hw(hw); 6438c2ecf20Sopenharmony_ci ieee80211_free_hw(hw); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MAIN); 6468c2ecf20Sopenharmony_ci return 0; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbtf_remove_card); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_civoid lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci ieee80211_tx_info_clear_status(info); 6558c2ecf20Sopenharmony_ci /* 6568c2ecf20Sopenharmony_ci * Commented out, otherwise we never go beyond 1Mbit/s using mac80211 6578c2ecf20Sopenharmony_ci * default pid rc algorithm. 6588c2ecf20Sopenharmony_ci * 6598c2ecf20Sopenharmony_ci * info->status.retry_count = MRVL_DEFAULT_RETRIES - retrycnt; 6608c2ecf20Sopenharmony_ci */ 6618c2ecf20Sopenharmony_ci if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !fail) 6628c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_ACK; 6638c2ecf20Sopenharmony_ci skb_pull(priv->tx_skb, sizeof(struct txpd)); 6648c2ecf20Sopenharmony_ci ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb); 6658c2ecf20Sopenharmony_ci priv->tx_skb = NULL; 6668c2ecf20Sopenharmony_ci if (!priv->skb_to_tx && skb_queue_empty(&priv->bc_ps_buf)) 6678c2ecf20Sopenharmony_ci ieee80211_wake_queues(priv->hw); 6688c2ecf20Sopenharmony_ci else 6698c2ecf20Sopenharmony_ci queue_work(lbtf_wq, &priv->tx_work); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbtf_send_tx_feedback); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_civoid lbtf_bcn_sent(struct lbtf_private *priv) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (priv->vif->type != NL80211_IFTYPE_AP) 6788c2ecf20Sopenharmony_ci return; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (skb_queue_empty(&priv->bc_ps_buf)) { 6818c2ecf20Sopenharmony_ci bool tx_buff_bc = false; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci while ((skb = ieee80211_get_buffered_bc(priv->hw, priv->vif))) { 6848c2ecf20Sopenharmony_ci skb_queue_tail(&priv->bc_ps_buf, skb); 6858c2ecf20Sopenharmony_ci tx_buff_bc = true; 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci if (tx_buff_bc) { 6888c2ecf20Sopenharmony_ci ieee80211_stop_queues(priv->hw); 6898c2ecf20Sopenharmony_ci queue_work(lbtf_wq, &priv->tx_work); 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci skb = ieee80211_beacon_get(priv->hw, priv->vif); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci if (skb) { 6968c2ecf20Sopenharmony_ci lbtf_beacon_set(priv, skb); 6978c2ecf20Sopenharmony_ci kfree_skb(skb); 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbtf_bcn_sent); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic int __init lbtf_init_module(void) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MAIN); 7058c2ecf20Sopenharmony_ci lbtf_wq = alloc_workqueue("libertastf", WQ_MEM_RECLAIM, 0); 7068c2ecf20Sopenharmony_ci if (lbtf_wq == NULL) { 7078c2ecf20Sopenharmony_ci printk(KERN_ERR "libertastf: couldn't create workqueue\n"); 7088c2ecf20Sopenharmony_ci return -ENOMEM; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MAIN); 7118c2ecf20Sopenharmony_ci return 0; 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_cistatic void __exit lbtf_exit_module(void) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci lbtf_deb_enter(LBTF_DEB_MAIN); 7178c2ecf20Sopenharmony_ci destroy_workqueue(lbtf_wq); 7188c2ecf20Sopenharmony_ci lbtf_deb_leave(LBTF_DEB_MAIN); 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cimodule_init(lbtf_init_module); 7228c2ecf20Sopenharmony_cimodule_exit(lbtf_exit_module); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Libertas WLAN Thinfirm Driver Library"); 7258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Cozybit Inc."); 7268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 727