18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This file contains the major functions in WLAN 48c2ecf20Sopenharmony_ci * driver. It includes init, exit, open, close and main 58c2ecf20Sopenharmony_ci * thread etc.. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 138c2ecf20Sopenharmony_ci#include <linux/hardirq.h> 148c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 158c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 168c2ecf20Sopenharmony_ci#include <linux/kthread.h> 178c2ecf20Sopenharmony_ci#include <linux/kfifo.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <net/cfg80211.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "host.h" 228c2ecf20Sopenharmony_ci#include "decl.h" 238c2ecf20Sopenharmony_ci#include "dev.h" 248c2ecf20Sopenharmony_ci#include "cfg.h" 258c2ecf20Sopenharmony_ci#include "debugfs.h" 268c2ecf20Sopenharmony_ci#include "cmd.h" 278c2ecf20Sopenharmony_ci#include "mesh.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define DRIVER_RELEASE_VERSION "323.p0" 308c2ecf20Sopenharmony_ciconst char lbs_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION 318c2ecf20Sopenharmony_ci#ifdef DEBUG 328c2ecf20Sopenharmony_ci "-dbg" 338c2ecf20Sopenharmony_ci#endif 348c2ecf20Sopenharmony_ci ""; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Module parameters */ 388c2ecf20Sopenharmony_ciunsigned int lbs_debug; 398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_debug); 408c2ecf20Sopenharmony_cimodule_param_named(libertas_debug, lbs_debug, int, 0644); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ciunsigned int lbs_disablemesh; 438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_disablemesh); 448c2ecf20Sopenharmony_cimodule_param_named(libertas_disablemesh, lbs_disablemesh, int, 0644); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * This global structure is used to send the confirm_sleep command as 498c2ecf20Sopenharmony_ci * fast as possible down to the firmware. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_cistruct cmd_confirm_sleep confirm_sleep; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 558c2ecf20Sopenharmony_ci * the table to keep region code 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ciu16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE] = 588c2ecf20Sopenharmony_ci { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 }; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * FW rate table. FW refers to rates by their index in this table, not by the 628c2ecf20Sopenharmony_ci * rate value itself. Values of 0x00 are 638c2ecf20Sopenharmony_ci * reserved positions. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_cistatic u8 fw_data_rates[MAX_RATES] = 668c2ecf20Sopenharmony_ci { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, 678c2ecf20Sopenharmony_ci 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x00 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/** 718c2ecf20Sopenharmony_ci * lbs_fw_index_to_data_rate - use index to get the data rate 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * @idx: The index of data rate 748c2ecf20Sopenharmony_ci * returns: data rate or 0 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ciu32 lbs_fw_index_to_data_rate(u8 idx) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci if (idx >= sizeof(fw_data_rates)) 798c2ecf20Sopenharmony_ci idx = 0; 808c2ecf20Sopenharmony_ci return fw_data_rates[idx]; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/** 848c2ecf20Sopenharmony_ci * lbs_data_rate_to_fw_index - use rate to get the index 858c2ecf20Sopenharmony_ci * 868c2ecf20Sopenharmony_ci * @rate: data rate 878c2ecf20Sopenharmony_ci * returns: index or 0 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ciu8 lbs_data_rate_to_fw_index(u32 rate) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci u8 i; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (!rate) 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(fw_data_rates); i++) { 978c2ecf20Sopenharmony_ci if (rate == fw_data_rates[i]) 988c2ecf20Sopenharmony_ci return i; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ciint lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci int ret = 0; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci switch (type) { 1088c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 1098c2ecf20Sopenharmony_ci ret = lbs_set_monitor_mode(priv, 1); 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 1128c2ecf20Sopenharmony_ci if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) 1138c2ecf20Sopenharmony_ci ret = lbs_set_monitor_mode(priv, 0); 1148c2ecf20Sopenharmony_ci if (!ret) 1158c2ecf20Sopenharmony_ci ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 1); 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 1188c2ecf20Sopenharmony_ci if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) 1198c2ecf20Sopenharmony_ci ret = lbs_set_monitor_mode(priv, 0); 1208c2ecf20Sopenharmony_ci if (!ret) 1218c2ecf20Sopenharmony_ci ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 2); 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci default: 1248c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci return ret; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ciint lbs_start_iface(struct lbs_private *priv) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct cmd_ds_802_11_mac_address cmd; 1328c2ecf20Sopenharmony_ci int ret; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (priv->power_restore) { 1358c2ecf20Sopenharmony_ci ret = priv->power_restore(priv); 1368c2ecf20Sopenharmony_ci if (ret) 1378c2ecf20Sopenharmony_ci return ret; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 1418c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_SET); 1428c2ecf20Sopenharmony_ci memcpy(cmd.macadd, priv->current_addr, ETH_ALEN); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd); 1458c2ecf20Sopenharmony_ci if (ret) { 1468c2ecf20Sopenharmony_ci lbs_deb_net("set MAC address failed\n"); 1478c2ecf20Sopenharmony_ci goto err; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci ret = lbs_set_iface_type(priv, priv->wdev->iftype); 1518c2ecf20Sopenharmony_ci if (ret) { 1528c2ecf20Sopenharmony_ci lbs_deb_net("set iface type failed\n"); 1538c2ecf20Sopenharmony_ci goto err; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = lbs_set_11d_domain_info(priv); 1578c2ecf20Sopenharmony_ci if (ret) { 1588c2ecf20Sopenharmony_ci lbs_deb_net("set 11d domain info failed\n"); 1598c2ecf20Sopenharmony_ci goto err; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci lbs_update_channel(priv); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci priv->iface_running = true; 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cierr: 1688c2ecf20Sopenharmony_ci if (priv->power_save) 1698c2ecf20Sopenharmony_ci priv->power_save(priv); 1708c2ecf20Sopenharmony_ci return ret; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/** 1748c2ecf20Sopenharmony_ci * lbs_dev_open - open the ethX interface 1758c2ecf20Sopenharmony_ci * 1768c2ecf20Sopenharmony_ci * @dev: A pointer to &net_device structure 1778c2ecf20Sopenharmony_ci * returns: 0 or -EBUSY if monitor mode active 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_cistatic int lbs_dev_open(struct net_device *dev) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct lbs_private *priv = dev->ml_priv; 1828c2ecf20Sopenharmony_ci int ret = 0; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (!priv->iface_running) { 1858c2ecf20Sopenharmony_ci ret = lbs_start_iface(priv); 1868c2ecf20Sopenharmony_ci if (ret) 1878c2ecf20Sopenharmony_ci goto out; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci netif_carrier_off(dev); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (!priv->tx_pending_len) 1958c2ecf20Sopenharmony_ci netif_wake_queue(dev); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ciout: 2008c2ecf20Sopenharmony_ci return ret; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic bool lbs_command_queue_empty(struct lbs_private *priv) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci unsigned long flags; 2068c2ecf20Sopenharmony_ci bool ret; 2078c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 2088c2ecf20Sopenharmony_ci ret = priv->cur_cmd == NULL && list_empty(&priv->cmdpendingq); 2098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ciint lbs_stop_iface(struct lbs_private *priv) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci unsigned long flags; 2168c2ecf20Sopenharmony_ci int ret = 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 2198c2ecf20Sopenharmony_ci priv->iface_running = false; 2208c2ecf20Sopenharmony_ci dev_kfree_skb_irq(priv->currenttxskb); 2218c2ecf20Sopenharmony_ci priv->currenttxskb = NULL; 2228c2ecf20Sopenharmony_ci priv->tx_pending_len = 0; 2238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci cancel_work_sync(&priv->mcast_work); 2268c2ecf20Sopenharmony_ci del_timer_sync(&priv->tx_lockup_timer); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* Disable command processing, and wait for all commands to complete */ 2298c2ecf20Sopenharmony_ci lbs_deb_main("waiting for commands to complete\n"); 2308c2ecf20Sopenharmony_ci wait_event(priv->waitq, lbs_command_queue_empty(priv)); 2318c2ecf20Sopenharmony_ci lbs_deb_main("all commands completed\n"); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (priv->power_save) 2348c2ecf20Sopenharmony_ci ret = priv->power_save(priv); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return ret; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/** 2408c2ecf20Sopenharmony_ci * lbs_eth_stop - close the ethX interface 2418c2ecf20Sopenharmony_ci * 2428c2ecf20Sopenharmony_ci * @dev: A pointer to &net_device structure 2438c2ecf20Sopenharmony_ci * returns: 0 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_cistatic int lbs_eth_stop(struct net_device *dev) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct lbs_private *priv = dev->ml_priv; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (priv->connect_status == LBS_CONNECTED) 2508c2ecf20Sopenharmony_ci lbs_disconnect(priv, WLAN_REASON_DEAUTH_LEAVING); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 2538c2ecf20Sopenharmony_ci netif_stop_queue(dev); 2548c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci lbs_update_mcast(priv); 2578c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->scan_work); 2588c2ecf20Sopenharmony_ci if (priv->scan_req) 2598c2ecf20Sopenharmony_ci lbs_scan_done(priv); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci netif_carrier_off(priv->dev); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (!lbs_iface_active(priv)) 2648c2ecf20Sopenharmony_ci lbs_stop_iface(priv); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_civoid lbs_host_to_card_done(struct lbs_private *priv) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci unsigned long flags; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 2748c2ecf20Sopenharmony_ci del_timer(&priv->tx_lockup_timer); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci priv->dnld_sent = DNLD_RES_RECEIVED; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Wake main thread if commands are pending */ 2798c2ecf20Sopenharmony_ci if (!priv->cur_cmd || priv->tx_pending_len > 0) { 2808c2ecf20Sopenharmony_ci if (!priv->wakeup_dev_required) 2818c2ecf20Sopenharmony_ci wake_up(&priv->waitq); 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_host_to_card_done); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ciint lbs_set_mac_address(struct net_device *dev, void *addr) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci int ret = 0; 2918c2ecf20Sopenharmony_ci struct lbs_private *priv = dev->ml_priv; 2928c2ecf20Sopenharmony_ci struct sockaddr *phwaddr = addr; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* 2958c2ecf20Sopenharmony_ci * Can only set MAC address when all interfaces are down, to be written 2968c2ecf20Sopenharmony_ci * to the hardware when one of them is brought up. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci if (lbs_iface_active(priv)) 2998c2ecf20Sopenharmony_ci return -EBUSY; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* In case it was called from the mesh device */ 3028c2ecf20Sopenharmony_ci dev = priv->dev; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci memcpy(priv->current_addr, phwaddr->sa_data, ETH_ALEN); 3058c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, phwaddr->sa_data, ETH_ALEN); 3068c2ecf20Sopenharmony_ci if (priv->mesh_dev) 3078c2ecf20Sopenharmony_ci memcpy(priv->mesh_dev->dev_addr, phwaddr->sa_data, ETH_ALEN); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci return ret; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic inline int mac_in_list(unsigned char *list, int list_len, 3148c2ecf20Sopenharmony_ci unsigned char *mac) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci while (list_len) { 3178c2ecf20Sopenharmony_ci if (!memcmp(list, mac, ETH_ALEN)) 3188c2ecf20Sopenharmony_ci return 1; 3198c2ecf20Sopenharmony_ci list += ETH_ALEN; 3208c2ecf20Sopenharmony_ci list_len--; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd, 3278c2ecf20Sopenharmony_ci struct net_device *dev, int nr_addrs) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci int i = nr_addrs; 3308c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 3318c2ecf20Sopenharmony_ci int cnt; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if ((dev->flags & (IFF_UP|IFF_MULTICAST)) != (IFF_UP|IFF_MULTICAST)) 3348c2ecf20Sopenharmony_ci return nr_addrs; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci netif_addr_lock_bh(dev); 3378c2ecf20Sopenharmony_ci cnt = netdev_mc_count(dev); 3388c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 3398c2ecf20Sopenharmony_ci if (mac_in_list(cmd->maclist, nr_addrs, ha->addr)) { 3408c2ecf20Sopenharmony_ci lbs_deb_net("mcast address %s:%pM skipped\n", dev->name, 3418c2ecf20Sopenharmony_ci ha->addr); 3428c2ecf20Sopenharmony_ci cnt--; 3438c2ecf20Sopenharmony_ci continue; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (i == MRVDRV_MAX_MULTICAST_LIST_SIZE) 3478c2ecf20Sopenharmony_ci break; 3488c2ecf20Sopenharmony_ci memcpy(&cmd->maclist[6*i], ha->addr, ETH_ALEN); 3498c2ecf20Sopenharmony_ci lbs_deb_net("mcast address %s:%pM added to filter\n", dev->name, 3508c2ecf20Sopenharmony_ci ha->addr); 3518c2ecf20Sopenharmony_ci i++; 3528c2ecf20Sopenharmony_ci cnt--; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci netif_addr_unlock_bh(dev); 3558c2ecf20Sopenharmony_ci if (cnt) 3568c2ecf20Sopenharmony_ci return -EOVERFLOW; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return i; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_civoid lbs_update_mcast(struct lbs_private *priv) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct cmd_ds_mac_multicast_adr mcast_cmd; 3648c2ecf20Sopenharmony_ci int dev_flags = 0; 3658c2ecf20Sopenharmony_ci int nr_addrs; 3668c2ecf20Sopenharmony_ci int old_mac_control = priv->mac_control; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (netif_running(priv->dev)) 3698c2ecf20Sopenharmony_ci dev_flags |= priv->dev->flags; 3708c2ecf20Sopenharmony_ci if (priv->mesh_dev && netif_running(priv->mesh_dev)) 3718c2ecf20Sopenharmony_ci dev_flags |= priv->mesh_dev->flags; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (dev_flags & IFF_PROMISC) { 3748c2ecf20Sopenharmony_ci priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE; 3758c2ecf20Sopenharmony_ci priv->mac_control &= ~(CMD_ACT_MAC_ALL_MULTICAST_ENABLE | 3768c2ecf20Sopenharmony_ci CMD_ACT_MAC_MULTICAST_ENABLE); 3778c2ecf20Sopenharmony_ci goto out_set_mac_control; 3788c2ecf20Sopenharmony_ci } else if (dev_flags & IFF_ALLMULTI) { 3798c2ecf20Sopenharmony_ci do_allmulti: 3808c2ecf20Sopenharmony_ci priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; 3818c2ecf20Sopenharmony_ci priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE | 3828c2ecf20Sopenharmony_ci CMD_ACT_MAC_MULTICAST_ENABLE); 3838c2ecf20Sopenharmony_ci goto out_set_mac_control; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* Once for priv->dev, again for priv->mesh_dev if it exists */ 3878c2ecf20Sopenharmony_ci nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->dev, 0); 3888c2ecf20Sopenharmony_ci if (nr_addrs >= 0 && priv->mesh_dev) 3898c2ecf20Sopenharmony_ci nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->mesh_dev, nr_addrs); 3908c2ecf20Sopenharmony_ci if (nr_addrs < 0) 3918c2ecf20Sopenharmony_ci goto do_allmulti; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (nr_addrs) { 3948c2ecf20Sopenharmony_ci int size = offsetof(struct cmd_ds_mac_multicast_adr, 3958c2ecf20Sopenharmony_ci maclist[6*nr_addrs]); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci mcast_cmd.action = cpu_to_le16(CMD_ACT_SET); 3988c2ecf20Sopenharmony_ci mcast_cmd.hdr.size = cpu_to_le16(size); 3998c2ecf20Sopenharmony_ci mcast_cmd.nr_of_adrs = cpu_to_le16(nr_addrs); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci lbs_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &mcast_cmd.hdr, size); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; 4048c2ecf20Sopenharmony_ci } else 4058c2ecf20Sopenharmony_ci priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE | 4088c2ecf20Sopenharmony_ci CMD_ACT_MAC_ALL_MULTICAST_ENABLE); 4098c2ecf20Sopenharmony_ci out_set_mac_control: 4108c2ecf20Sopenharmony_ci if (priv->mac_control != old_mac_control) 4118c2ecf20Sopenharmony_ci lbs_set_mac_control(priv); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic void lbs_set_mcast_worker(struct work_struct *work) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work); 4178c2ecf20Sopenharmony_ci lbs_update_mcast(priv); 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_civoid lbs_set_multicast_list(struct net_device *dev) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct lbs_private *priv = dev->ml_priv; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci schedule_work(&priv->mcast_work); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci/** 4288c2ecf20Sopenharmony_ci * lbs_thread - handles the major jobs in the LBS driver. 4298c2ecf20Sopenharmony_ci * It handles all events generated by firmware, RX data received 4308c2ecf20Sopenharmony_ci * from firmware and TX data sent from kernel. 4318c2ecf20Sopenharmony_ci * 4328c2ecf20Sopenharmony_ci * @data: A pointer to &lbs_thread structure 4338c2ecf20Sopenharmony_ci * returns: 0 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_cistatic int lbs_thread(void *data) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct net_device *dev = data; 4388c2ecf20Sopenharmony_ci struct lbs_private *priv = dev->ml_priv; 4398c2ecf20Sopenharmony_ci wait_queue_entry_t wait; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci init_waitqueue_entry(&wait, current); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci for (;;) { 4448c2ecf20Sopenharmony_ci int shouldsleep; 4458c2ecf20Sopenharmony_ci u8 resp_idx; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci lbs_deb_thread("1: currenttxskb %p, dnld_sent %d\n", 4488c2ecf20Sopenharmony_ci priv->currenttxskb, priv->dnld_sent); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci add_wait_queue(&priv->waitq, &wait); 4518c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 4528c2ecf20Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (kthread_should_stop()) 4558c2ecf20Sopenharmony_ci shouldsleep = 0; /* Bye */ 4568c2ecf20Sopenharmony_ci else if (priv->surpriseremoved) 4578c2ecf20Sopenharmony_ci shouldsleep = 1; /* We need to wait until we're _told_ to die */ 4588c2ecf20Sopenharmony_ci else if (priv->psstate == PS_STATE_SLEEP) 4598c2ecf20Sopenharmony_ci shouldsleep = 1; /* Sleep mode. Nothing we can do till it wakes */ 4608c2ecf20Sopenharmony_ci else if (priv->cmd_timed_out) 4618c2ecf20Sopenharmony_ci shouldsleep = 0; /* Command timed out. Recover */ 4628c2ecf20Sopenharmony_ci else if (!priv->fw_ready) 4638c2ecf20Sopenharmony_ci shouldsleep = 1; /* Firmware not ready. We're waiting for it */ 4648c2ecf20Sopenharmony_ci else if (priv->dnld_sent) 4658c2ecf20Sopenharmony_ci shouldsleep = 1; /* Something is en route to the device already */ 4668c2ecf20Sopenharmony_ci else if (priv->tx_pending_len > 0) 4678c2ecf20Sopenharmony_ci shouldsleep = 0; /* We've a packet to send */ 4688c2ecf20Sopenharmony_ci else if (priv->resp_len[priv->resp_idx]) 4698c2ecf20Sopenharmony_ci shouldsleep = 0; /* We have a command response */ 4708c2ecf20Sopenharmony_ci else if (priv->cur_cmd) 4718c2ecf20Sopenharmony_ci shouldsleep = 1; /* Can't send a command; one already running */ 4728c2ecf20Sopenharmony_ci else if (!list_empty(&priv->cmdpendingq) && 4738c2ecf20Sopenharmony_ci !(priv->wakeup_dev_required)) 4748c2ecf20Sopenharmony_ci shouldsleep = 0; /* We have a command to send */ 4758c2ecf20Sopenharmony_ci else if (kfifo_len(&priv->event_fifo)) 4768c2ecf20Sopenharmony_ci shouldsleep = 0; /* We have an event to process */ 4778c2ecf20Sopenharmony_ci else 4788c2ecf20Sopenharmony_ci shouldsleep = 1; /* No command */ 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (shouldsleep) { 4818c2ecf20Sopenharmony_ci lbs_deb_thread("sleeping, connect_status %d, " 4828c2ecf20Sopenharmony_ci "psmode %d, psstate %d\n", 4838c2ecf20Sopenharmony_ci priv->connect_status, 4848c2ecf20Sopenharmony_ci priv->psmode, priv->psstate); 4858c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 4868c2ecf20Sopenharmony_ci schedule(); 4878c2ecf20Sopenharmony_ci } else 4888c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci lbs_deb_thread("2: currenttxskb %p, dnld_send %d\n", 4918c2ecf20Sopenharmony_ci priv->currenttxskb, priv->dnld_sent); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 4948c2ecf20Sopenharmony_ci remove_wait_queue(&priv->waitq, &wait); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci lbs_deb_thread("3: currenttxskb %p, dnld_sent %d\n", 4978c2ecf20Sopenharmony_ci priv->currenttxskb, priv->dnld_sent); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (kthread_should_stop()) { 5008c2ecf20Sopenharmony_ci lbs_deb_thread("break from main thread\n"); 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (priv->surpriseremoved) { 5058c2ecf20Sopenharmony_ci lbs_deb_thread("adapter removed; waiting to die...\n"); 5068c2ecf20Sopenharmony_ci continue; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci lbs_deb_thread("4: currenttxskb %p, dnld_sent %d\n", 5108c2ecf20Sopenharmony_ci priv->currenttxskb, priv->dnld_sent); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* Process any pending command response */ 5138c2ecf20Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 5148c2ecf20Sopenharmony_ci resp_idx = priv->resp_idx; 5158c2ecf20Sopenharmony_ci if (priv->resp_len[resp_idx]) { 5168c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 5178c2ecf20Sopenharmony_ci lbs_process_command_response(priv, 5188c2ecf20Sopenharmony_ci priv->resp_buf[resp_idx], 5198c2ecf20Sopenharmony_ci priv->resp_len[resp_idx]); 5208c2ecf20Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 5218c2ecf20Sopenharmony_ci priv->resp_len[resp_idx] = 0; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* Process hardware events, e.g. card removed, link lost */ 5268c2ecf20Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 5278c2ecf20Sopenharmony_ci while (kfifo_len(&priv->event_fifo)) { 5288c2ecf20Sopenharmony_ci u32 event; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (kfifo_out(&priv->event_fifo, 5318c2ecf20Sopenharmony_ci (unsigned char *) &event, sizeof(event)) != 5328c2ecf20Sopenharmony_ci sizeof(event)) 5338c2ecf20Sopenharmony_ci break; 5348c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 5358c2ecf20Sopenharmony_ci lbs_process_event(priv, event); 5368c2ecf20Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (priv->wakeup_dev_required) { 5418c2ecf20Sopenharmony_ci lbs_deb_thread("Waking up device...\n"); 5428c2ecf20Sopenharmony_ci /* Wake up device */ 5438c2ecf20Sopenharmony_ci if (priv->exit_deep_sleep(priv)) 5448c2ecf20Sopenharmony_ci lbs_deb_thread("Wakeup device failed\n"); 5458c2ecf20Sopenharmony_ci continue; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* command timeout stuff */ 5498c2ecf20Sopenharmony_ci if (priv->cmd_timed_out && priv->cur_cmd) { 5508c2ecf20Sopenharmony_ci struct cmd_ctrl_node *cmdnode = priv->cur_cmd; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci netdev_info(dev, "Timeout submitting command 0x%04x\n", 5538c2ecf20Sopenharmony_ci le16_to_cpu(cmdnode->cmdbuf->command)); 5548c2ecf20Sopenharmony_ci lbs_complete_command(priv, cmdnode, -ETIMEDOUT); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* Reset card, but only when it isn't in the process 5578c2ecf20Sopenharmony_ci * of being shutdown anyway. */ 5588c2ecf20Sopenharmony_ci if (!dev->dismantle && priv->reset_card) 5598c2ecf20Sopenharmony_ci priv->reset_card(priv); 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci priv->cmd_timed_out = 0; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (!priv->fw_ready) 5648c2ecf20Sopenharmony_ci continue; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* Check if we need to confirm Sleep Request received previously */ 5678c2ecf20Sopenharmony_ci if (priv->psstate == PS_STATE_PRE_SLEEP && 5688c2ecf20Sopenharmony_ci !priv->dnld_sent && !priv->cur_cmd) { 5698c2ecf20Sopenharmony_ci if (priv->connect_status == LBS_CONNECTED) { 5708c2ecf20Sopenharmony_ci lbs_deb_thread("pre-sleep, currenttxskb %p, " 5718c2ecf20Sopenharmony_ci "dnld_sent %d, cur_cmd %p\n", 5728c2ecf20Sopenharmony_ci priv->currenttxskb, priv->dnld_sent, 5738c2ecf20Sopenharmony_ci priv->cur_cmd); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci lbs_ps_confirm_sleep(priv); 5768c2ecf20Sopenharmony_ci } else { 5778c2ecf20Sopenharmony_ci /* workaround for firmware sending 5788c2ecf20Sopenharmony_ci * deauth/linkloss event immediately 5798c2ecf20Sopenharmony_ci * after sleep request; remove this 5808c2ecf20Sopenharmony_ci * after firmware fixes it 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_ci priv->psstate = PS_STATE_AWAKE; 5838c2ecf20Sopenharmony_ci netdev_alert(dev, 5848c2ecf20Sopenharmony_ci "ignore PS_SleepConfirm in non-connected state\n"); 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* The PS state is changed during processing of Sleep Request 5898c2ecf20Sopenharmony_ci * event above 5908c2ecf20Sopenharmony_ci */ 5918c2ecf20Sopenharmony_ci if ((priv->psstate == PS_STATE_SLEEP) || 5928c2ecf20Sopenharmony_ci (priv->psstate == PS_STATE_PRE_SLEEP)) 5938c2ecf20Sopenharmony_ci continue; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (priv->is_deep_sleep) 5968c2ecf20Sopenharmony_ci continue; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci /* Execute the next command */ 5998c2ecf20Sopenharmony_ci if (!priv->dnld_sent && !priv->cur_cmd) 6008c2ecf20Sopenharmony_ci lbs_execute_next_command(priv); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 6038c2ecf20Sopenharmony_ci if (!priv->dnld_sent && priv->tx_pending_len > 0) { 6048c2ecf20Sopenharmony_ci int ret = priv->hw_host_to_card(priv, MVMS_DAT, 6058c2ecf20Sopenharmony_ci priv->tx_pending_buf, 6068c2ecf20Sopenharmony_ci priv->tx_pending_len); 6078c2ecf20Sopenharmony_ci if (ret) { 6088c2ecf20Sopenharmony_ci lbs_deb_tx("host_to_card failed %d\n", ret); 6098c2ecf20Sopenharmony_ci priv->dnld_sent = DNLD_RES_RECEIVED; 6108c2ecf20Sopenharmony_ci } else { 6118c2ecf20Sopenharmony_ci mod_timer(&priv->tx_lockup_timer, 6128c2ecf20Sopenharmony_ci jiffies + (HZ * 5)); 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci priv->tx_pending_len = 0; 6158c2ecf20Sopenharmony_ci if (!priv->currenttxskb) { 6168c2ecf20Sopenharmony_ci /* We can wake the queues immediately if we aren't 6178c2ecf20Sopenharmony_ci waiting for TX feedback */ 6188c2ecf20Sopenharmony_ci if (priv->connect_status == LBS_CONNECTED) 6198c2ecf20Sopenharmony_ci netif_wake_queue(priv->dev); 6208c2ecf20Sopenharmony_ci if (priv->mesh_dev && 6218c2ecf20Sopenharmony_ci netif_running(priv->mesh_dev)) 6228c2ecf20Sopenharmony_ci netif_wake_queue(priv->mesh_dev); 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci del_timer(&priv->command_timer); 6298c2ecf20Sopenharmony_ci del_timer(&priv->tx_lockup_timer); 6308c2ecf20Sopenharmony_ci del_timer(&priv->auto_deepsleep_timer); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return 0; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci/** 6368c2ecf20Sopenharmony_ci * lbs_setup_firmware - gets the HW spec from the firmware and sets 6378c2ecf20Sopenharmony_ci * some basic parameters 6388c2ecf20Sopenharmony_ci * 6398c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 6408c2ecf20Sopenharmony_ci * returns: 0 or -1 6418c2ecf20Sopenharmony_ci */ 6428c2ecf20Sopenharmony_cistatic int lbs_setup_firmware(struct lbs_private *priv) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci int ret = -1; 6458c2ecf20Sopenharmony_ci s16 curlevel = 0, minlevel = 0, maxlevel = 0; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* Read MAC address from firmware */ 6488c2ecf20Sopenharmony_ci eth_broadcast_addr(priv->current_addr); 6498c2ecf20Sopenharmony_ci ret = lbs_update_hw_spec(priv); 6508c2ecf20Sopenharmony_ci if (ret) 6518c2ecf20Sopenharmony_ci goto done; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci /* Read power levels if available */ 6548c2ecf20Sopenharmony_ci ret = lbs_get_tx_power(priv, &curlevel, &minlevel, &maxlevel); 6558c2ecf20Sopenharmony_ci if (ret == 0) { 6568c2ecf20Sopenharmony_ci priv->txpower_cur = curlevel; 6578c2ecf20Sopenharmony_ci priv->txpower_min = minlevel; 6588c2ecf20Sopenharmony_ci priv->txpower_max = maxlevel; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci /* Send cmd to FW to enable 11D function */ 6628c2ecf20Sopenharmony_ci ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_11D_ENABLE, 1); 6638c2ecf20Sopenharmony_ci if (ret) 6648c2ecf20Sopenharmony_ci goto done; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci ret = lbs_set_mac_control_sync(priv); 6678c2ecf20Sopenharmony_cidone: 6688c2ecf20Sopenharmony_ci return ret; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ciint lbs_suspend(struct lbs_private *priv) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci int ret; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci if (priv->is_deep_sleep) { 6768c2ecf20Sopenharmony_ci ret = lbs_set_deep_sleep(priv, 0); 6778c2ecf20Sopenharmony_ci if (ret) { 6788c2ecf20Sopenharmony_ci netdev_err(priv->dev, 6798c2ecf20Sopenharmony_ci "deep sleep cancellation failed: %d\n", ret); 6808c2ecf20Sopenharmony_ci return ret; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci priv->deep_sleep_required = 1; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci ret = lbs_set_host_sleep(priv, 1); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci netif_device_detach(priv->dev); 6888c2ecf20Sopenharmony_ci if (priv->mesh_dev) 6898c2ecf20Sopenharmony_ci netif_device_detach(priv->mesh_dev); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci return ret; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_suspend); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ciint lbs_resume(struct lbs_private *priv) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci int ret; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci ret = lbs_set_host_sleep(priv, 0); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci netif_device_attach(priv->dev); 7028c2ecf20Sopenharmony_ci if (priv->mesh_dev) 7038c2ecf20Sopenharmony_ci netif_device_attach(priv->mesh_dev); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (priv->deep_sleep_required) { 7068c2ecf20Sopenharmony_ci priv->deep_sleep_required = 0; 7078c2ecf20Sopenharmony_ci ret = lbs_set_deep_sleep(priv, 1); 7088c2ecf20Sopenharmony_ci if (ret) 7098c2ecf20Sopenharmony_ci netdev_err(priv->dev, 7108c2ecf20Sopenharmony_ci "deep sleep activation failed: %d\n", ret); 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (priv->setup_fw_on_resume) 7148c2ecf20Sopenharmony_ci ret = lbs_setup_firmware(priv); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci return ret; 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_resume); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci/** 7218c2ecf20Sopenharmony_ci * lbs_cmd_timeout_handler - handles the timeout of command sending. 7228c2ecf20Sopenharmony_ci * It will re-send the same command again. 7238c2ecf20Sopenharmony_ci * 7248c2ecf20Sopenharmony_ci * @t: Context from which to retrieve a &struct lbs_private pointer 7258c2ecf20Sopenharmony_ci */ 7268c2ecf20Sopenharmony_cistatic void lbs_cmd_timeout_handler(struct timer_list *t) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci struct lbs_private *priv = from_timer(priv, t, command_timer); 7298c2ecf20Sopenharmony_ci unsigned long flags; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (!priv->cur_cmd) 7348c2ecf20Sopenharmony_ci goto out; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci netdev_info(priv->dev, "command 0x%04x timed out\n", 7378c2ecf20Sopenharmony_ci le16_to_cpu(priv->cur_cmd->cmdbuf->command)); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci priv->cmd_timed_out = 1; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci /* 7428c2ecf20Sopenharmony_ci * If the device didn't even acknowledge the command, reset the state 7438c2ecf20Sopenharmony_ci * so that we don't block all future commands due to this one timeout. 7448c2ecf20Sopenharmony_ci */ 7458c2ecf20Sopenharmony_ci if (priv->dnld_sent == DNLD_CMD_SENT) 7468c2ecf20Sopenharmony_ci priv->dnld_sent = DNLD_RES_RECEIVED; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci wake_up(&priv->waitq); 7498c2ecf20Sopenharmony_ciout: 7508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci/** 7548c2ecf20Sopenharmony_ci * lbs_tx_lockup_handler - handles the timeout of the passing of TX frames 7558c2ecf20Sopenharmony_ci * to the hardware. This is known to frequently happen with SD8686 when 7568c2ecf20Sopenharmony_ci * waking up after a Wake-on-WLAN-triggered resume. 7578c2ecf20Sopenharmony_ci * 7588c2ecf20Sopenharmony_ci * @t: Context from which to retrieve a &struct lbs_private pointer 7598c2ecf20Sopenharmony_ci */ 7608c2ecf20Sopenharmony_cistatic void lbs_tx_lockup_handler(struct timer_list *t) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci struct lbs_private *priv = from_timer(priv, t, tx_lockup_timer); 7638c2ecf20Sopenharmony_ci unsigned long flags; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci netdev_info(priv->dev, "TX lockup detected\n"); 7688c2ecf20Sopenharmony_ci if (priv->reset_card) 7698c2ecf20Sopenharmony_ci priv->reset_card(priv); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci priv->dnld_sent = DNLD_RES_RECEIVED; 7728c2ecf20Sopenharmony_ci wake_up_interruptible(&priv->waitq); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci/** 7788c2ecf20Sopenharmony_ci * auto_deepsleep_timer_fn - put the device back to deep sleep mode when 7798c2ecf20Sopenharmony_ci * timer expires and no activity (command, event, data etc.) is detected. 7808c2ecf20Sopenharmony_ci * @t: Context from which to retrieve a &struct lbs_private pointer 7818c2ecf20Sopenharmony_ci * returns: N/A 7828c2ecf20Sopenharmony_ci */ 7838c2ecf20Sopenharmony_cistatic void auto_deepsleep_timer_fn(struct timer_list *t) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci struct lbs_private *priv = from_timer(priv, t, auto_deepsleep_timer); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci if (priv->is_activity_detected) { 7888c2ecf20Sopenharmony_ci priv->is_activity_detected = 0; 7898c2ecf20Sopenharmony_ci } else { 7908c2ecf20Sopenharmony_ci if (priv->is_auto_deep_sleep_enabled && 7918c2ecf20Sopenharmony_ci (!priv->wakeup_dev_required) && 7928c2ecf20Sopenharmony_ci (priv->connect_status != LBS_CONNECTED)) { 7938c2ecf20Sopenharmony_ci struct cmd_header cmd; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci lbs_deb_main("Entering auto deep sleep mode...\n"); 7968c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 7978c2ecf20Sopenharmony_ci cmd.size = cpu_to_le16(sizeof(cmd)); 7988c2ecf20Sopenharmony_ci lbs_cmd_async(priv, CMD_802_11_DEEP_SLEEP, &cmd, 7998c2ecf20Sopenharmony_ci sizeof(cmd)); 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci mod_timer(&priv->auto_deepsleep_timer , jiffies + 8038c2ecf20Sopenharmony_ci (priv->auto_deep_sleep_timeout * HZ)/1000); 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ciint lbs_enter_auto_deep_sleep(struct lbs_private *priv) 8078c2ecf20Sopenharmony_ci{ 8088c2ecf20Sopenharmony_ci priv->is_auto_deep_sleep_enabled = 1; 8098c2ecf20Sopenharmony_ci if (priv->is_deep_sleep) 8108c2ecf20Sopenharmony_ci priv->wakeup_dev_required = 1; 8118c2ecf20Sopenharmony_ci mod_timer(&priv->auto_deepsleep_timer , 8128c2ecf20Sopenharmony_ci jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci return 0; 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ciint lbs_exit_auto_deep_sleep(struct lbs_private *priv) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci priv->is_auto_deep_sleep_enabled = 0; 8208c2ecf20Sopenharmony_ci priv->auto_deep_sleep_timeout = 0; 8218c2ecf20Sopenharmony_ci del_timer(&priv->auto_deepsleep_timer); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci return 0; 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic int lbs_init_adapter(struct lbs_private *priv) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci int ret; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci eth_broadcast_addr(priv->current_addr); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci priv->connect_status = LBS_DISCONNECTED; 8338c2ecf20Sopenharmony_ci priv->channel = DEFAULT_AD_HOC_CHANNEL; 8348c2ecf20Sopenharmony_ci priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; 8358c2ecf20Sopenharmony_ci priv->radio_on = 1; 8368c2ecf20Sopenharmony_ci priv->psmode = LBS802_11POWERMODECAM; 8378c2ecf20Sopenharmony_ci priv->psstate = PS_STATE_FULL_POWER; 8388c2ecf20Sopenharmony_ci priv->is_deep_sleep = 0; 8398c2ecf20Sopenharmony_ci priv->is_auto_deep_sleep_enabled = 0; 8408c2ecf20Sopenharmony_ci priv->deep_sleep_required = 0; 8418c2ecf20Sopenharmony_ci priv->wakeup_dev_required = 0; 8428c2ecf20Sopenharmony_ci init_waitqueue_head(&priv->ds_awake_q); 8438c2ecf20Sopenharmony_ci init_waitqueue_head(&priv->scan_q); 8448c2ecf20Sopenharmony_ci priv->authtype_auto = 1; 8458c2ecf20Sopenharmony_ci priv->is_host_sleep_configured = 0; 8468c2ecf20Sopenharmony_ci priv->is_host_sleep_activated = 0; 8478c2ecf20Sopenharmony_ci init_waitqueue_head(&priv->host_sleep_q); 8488c2ecf20Sopenharmony_ci init_waitqueue_head(&priv->fw_waitq); 8498c2ecf20Sopenharmony_ci mutex_init(&priv->lock); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci timer_setup(&priv->command_timer, lbs_cmd_timeout_handler, 0); 8528c2ecf20Sopenharmony_ci timer_setup(&priv->tx_lockup_timer, lbs_tx_lockup_handler, 0); 8538c2ecf20Sopenharmony_ci timer_setup(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn, 0); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&priv->cmdfreeq); 8568c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&priv->cmdpendingq); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci spin_lock_init(&priv->driver_lock); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci /* Allocate the command buffers */ 8618c2ecf20Sopenharmony_ci if (lbs_allocate_cmd_buffer(priv)) { 8628c2ecf20Sopenharmony_ci pr_err("Out of memory allocating command buffers\n"); 8638c2ecf20Sopenharmony_ci ret = -ENOMEM; 8648c2ecf20Sopenharmony_ci goto out; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci priv->resp_idx = 0; 8678c2ecf20Sopenharmony_ci priv->resp_len[0] = priv->resp_len[1] = 0; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci /* Create the event FIFO */ 8708c2ecf20Sopenharmony_ci ret = kfifo_alloc(&priv->event_fifo, sizeof(u32) * 16, GFP_KERNEL); 8718c2ecf20Sopenharmony_ci if (ret) { 8728c2ecf20Sopenharmony_ci pr_err("Out of memory allocating event FIFO buffer\n"); 8738c2ecf20Sopenharmony_ci lbs_free_cmd_buffer(priv); 8748c2ecf20Sopenharmony_ci goto out; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ciout: 8788c2ecf20Sopenharmony_ci return ret; 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cistatic void lbs_free_adapter(struct lbs_private *priv) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci lbs_free_cmd_buffer(priv); 8848c2ecf20Sopenharmony_ci kfifo_free(&priv->event_fifo); 8858c2ecf20Sopenharmony_ci del_timer(&priv->command_timer); 8868c2ecf20Sopenharmony_ci del_timer(&priv->tx_lockup_timer); 8878c2ecf20Sopenharmony_ci del_timer(&priv->auto_deepsleep_timer); 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic const struct net_device_ops lbs_netdev_ops = { 8918c2ecf20Sopenharmony_ci .ndo_open = lbs_dev_open, 8928c2ecf20Sopenharmony_ci .ndo_stop = lbs_eth_stop, 8938c2ecf20Sopenharmony_ci .ndo_start_xmit = lbs_hard_start_xmit, 8948c2ecf20Sopenharmony_ci .ndo_set_mac_address = lbs_set_mac_address, 8958c2ecf20Sopenharmony_ci .ndo_set_rx_mode = lbs_set_multicast_list, 8968c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 8978c2ecf20Sopenharmony_ci}; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci/** 9008c2ecf20Sopenharmony_ci * lbs_add_card - adds the card. It will probe the 9018c2ecf20Sopenharmony_ci * card, allocate the lbs_priv and initialize the device. 9028c2ecf20Sopenharmony_ci * 9038c2ecf20Sopenharmony_ci * @card: A pointer to card 9048c2ecf20Sopenharmony_ci * @dmdev: A pointer to &struct device 9058c2ecf20Sopenharmony_ci * returns: A pointer to &struct lbs_private structure 9068c2ecf20Sopenharmony_ci */ 9078c2ecf20Sopenharmony_cistruct lbs_private *lbs_add_card(void *card, struct device *dmdev) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci struct net_device *dev; 9108c2ecf20Sopenharmony_ci struct wireless_dev *wdev; 9118c2ecf20Sopenharmony_ci struct lbs_private *priv = NULL; 9128c2ecf20Sopenharmony_ci int err; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci /* Allocate an Ethernet device and register it */ 9158c2ecf20Sopenharmony_ci wdev = lbs_cfg_alloc(dmdev); 9168c2ecf20Sopenharmony_ci if (IS_ERR(wdev)) { 9178c2ecf20Sopenharmony_ci err = PTR_ERR(wdev); 9188c2ecf20Sopenharmony_ci pr_err("cfg80211 init failed\n"); 9198c2ecf20Sopenharmony_ci goto err_cfg; 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci wdev->iftype = NL80211_IFTYPE_STATION; 9238c2ecf20Sopenharmony_ci priv = wdev_priv(wdev); 9248c2ecf20Sopenharmony_ci priv->wdev = wdev; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci err = lbs_init_adapter(priv); 9278c2ecf20Sopenharmony_ci if (err) { 9288c2ecf20Sopenharmony_ci pr_err("failed to initialize adapter structure\n"); 9298c2ecf20Sopenharmony_ci goto err_wdev; 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci dev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, ether_setup); 9338c2ecf20Sopenharmony_ci if (!dev) { 9348c2ecf20Sopenharmony_ci err = -ENOMEM; 9358c2ecf20Sopenharmony_ci dev_err(dmdev, "no memory for network device instance\n"); 9368c2ecf20Sopenharmony_ci goto err_adapter; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci dev->ieee80211_ptr = wdev; 9408c2ecf20Sopenharmony_ci dev->ml_priv = priv; 9418c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, dmdev); 9428c2ecf20Sopenharmony_ci wdev->netdev = dev; 9438c2ecf20Sopenharmony_ci priv->dev = dev; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci dev->netdev_ops = &lbs_netdev_ops; 9468c2ecf20Sopenharmony_ci dev->watchdog_timeo = 5 * HZ; 9478c2ecf20Sopenharmony_ci dev->ethtool_ops = &lbs_ethtool_ops; 9488c2ecf20Sopenharmony_ci dev->flags |= IFF_BROADCAST | IFF_MULTICAST; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci priv->card = card; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci strcpy(dev->name, "wlan%d"); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci lbs_deb_thread("Starting main thread...\n"); 9558c2ecf20Sopenharmony_ci init_waitqueue_head(&priv->waitq); 9568c2ecf20Sopenharmony_ci priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main"); 9578c2ecf20Sopenharmony_ci if (IS_ERR(priv->main_thread)) { 9588c2ecf20Sopenharmony_ci err = PTR_ERR(priv->main_thread); 9598c2ecf20Sopenharmony_ci lbs_deb_thread("Error creating main thread.\n"); 9608c2ecf20Sopenharmony_ci goto err_ndev; 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci priv->work_thread = create_singlethread_workqueue("lbs_worker"); 9648c2ecf20Sopenharmony_ci INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci priv->wol_criteria = EHS_REMOVE_WAKEUP; 9678c2ecf20Sopenharmony_ci priv->wol_gpio = 0xff; 9688c2ecf20Sopenharmony_ci priv->wol_gap = 20; 9698c2ecf20Sopenharmony_ci priv->ehs_remove_supported = true; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci return priv; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci err_ndev: 9748c2ecf20Sopenharmony_ci free_netdev(dev); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci err_adapter: 9778c2ecf20Sopenharmony_ci lbs_free_adapter(priv); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci err_wdev: 9808c2ecf20Sopenharmony_ci lbs_cfg_free(priv); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci err_cfg: 9838c2ecf20Sopenharmony_ci return ERR_PTR(err); 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_add_card); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_civoid lbs_remove_card(struct lbs_private *priv) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci struct net_device *dev = priv->dev; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci lbs_remove_mesh(priv); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci if (priv->wiphy_registered) 9958c2ecf20Sopenharmony_ci lbs_scan_deinit(priv); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci lbs_wait_for_firmware_load(priv); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci /* worker thread destruction blocks on the in-flight command which 10008c2ecf20Sopenharmony_ci * should have been cleared already in lbs_stop_card(). 10018c2ecf20Sopenharmony_ci */ 10028c2ecf20Sopenharmony_ci lbs_deb_main("destroying worker thread\n"); 10038c2ecf20Sopenharmony_ci destroy_workqueue(priv->work_thread); 10048c2ecf20Sopenharmony_ci lbs_deb_main("done destroying worker thread\n"); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci if (priv->psmode == LBS802_11POWERMODEMAX_PSP) { 10078c2ecf20Sopenharmony_ci priv->psmode = LBS802_11POWERMODECAM; 10088c2ecf20Sopenharmony_ci /* no need to wakeup if already woken up, 10098c2ecf20Sopenharmony_ci * on suspend, this exit ps command is not processed 10108c2ecf20Sopenharmony_ci * the driver hangs 10118c2ecf20Sopenharmony_ci */ 10128c2ecf20Sopenharmony_ci if (priv->psstate != PS_STATE_FULL_POWER) 10138c2ecf20Sopenharmony_ci lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, true); 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci if (priv->is_deep_sleep) { 10178c2ecf20Sopenharmony_ci priv->is_deep_sleep = 0; 10188c2ecf20Sopenharmony_ci wake_up_interruptible(&priv->ds_awake_q); 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci priv->is_host_sleep_configured = 0; 10228c2ecf20Sopenharmony_ci priv->is_host_sleep_activated = 0; 10238c2ecf20Sopenharmony_ci wake_up_interruptible(&priv->host_sleep_q); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci /* Stop the thread servicing the interrupts */ 10268c2ecf20Sopenharmony_ci priv->surpriseremoved = 1; 10278c2ecf20Sopenharmony_ci kthread_stop(priv->main_thread); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci lbs_free_adapter(priv); 10308c2ecf20Sopenharmony_ci lbs_cfg_free(priv); 10318c2ecf20Sopenharmony_ci free_netdev(dev); 10328c2ecf20Sopenharmony_ci} 10338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_remove_card); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ciint lbs_rtap_supported(struct lbs_private *priv) 10378c2ecf20Sopenharmony_ci{ 10388c2ecf20Sopenharmony_ci if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) 10398c2ecf20Sopenharmony_ci return 1; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci /* newer firmware use a capability mask */ 10428c2ecf20Sopenharmony_ci return ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && 10438c2ecf20Sopenharmony_ci (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)); 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ciint lbs_start_card(struct lbs_private *priv) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci struct net_device *dev = priv->dev; 10508c2ecf20Sopenharmony_ci int ret; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci /* poke the firmware */ 10538c2ecf20Sopenharmony_ci ret = lbs_setup_firmware(priv); 10548c2ecf20Sopenharmony_ci if (ret) 10558c2ecf20Sopenharmony_ci goto done; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci if (!lbs_disablemesh) 10588c2ecf20Sopenharmony_ci lbs_init_mesh(priv); 10598c2ecf20Sopenharmony_ci else 10608c2ecf20Sopenharmony_ci pr_info("%s: mesh disabled\n", dev->name); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci ret = lbs_cfg_register(priv); 10638c2ecf20Sopenharmony_ci if (ret) { 10648c2ecf20Sopenharmony_ci pr_err("cannot register device\n"); 10658c2ecf20Sopenharmony_ci goto done; 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci if (lbs_mesh_activated(priv)) 10698c2ecf20Sopenharmony_ci lbs_start_mesh(priv); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci lbs_debugfs_init_one(priv, dev); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci netdev_info(dev, "Marvell WLAN 802.11 adapter\n"); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci ret = 0; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cidone: 10788c2ecf20Sopenharmony_ci return ret; 10798c2ecf20Sopenharmony_ci} 10808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_start_card); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_civoid lbs_stop_card(struct lbs_private *priv) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci struct net_device *dev; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci if (!priv) 10888c2ecf20Sopenharmony_ci return; 10898c2ecf20Sopenharmony_ci dev = priv->dev; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci /* If the netdev isn't registered, it means that lbs_start_card() was 10928c2ecf20Sopenharmony_ci * never called so we have nothing to do here. */ 10938c2ecf20Sopenharmony_ci if (dev->reg_state != NETREG_REGISTERED) 10948c2ecf20Sopenharmony_ci return; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci netif_stop_queue(dev); 10978c2ecf20Sopenharmony_ci netif_carrier_off(dev); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci lbs_debugfs_remove_one(priv); 11008c2ecf20Sopenharmony_ci lbs_deinit_mesh(priv); 11018c2ecf20Sopenharmony_ci unregister_netdev(dev); 11028c2ecf20Sopenharmony_ci} 11038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_stop_card); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_civoid lbs_queue_event(struct lbs_private *priv, u32 event) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci unsigned long flags; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci if (priv->psstate == PS_STATE_SLEEP) 11138c2ecf20Sopenharmony_ci priv->psstate = PS_STATE_AWAKE; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci kfifo_in(&priv->event_fifo, (unsigned char *) &event, sizeof(u32)); 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci wake_up(&priv->waitq); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 11208c2ecf20Sopenharmony_ci} 11218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_queue_event); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_civoid lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx) 11248c2ecf20Sopenharmony_ci{ 11258c2ecf20Sopenharmony_ci if (priv->psstate == PS_STATE_SLEEP) 11268c2ecf20Sopenharmony_ci priv->psstate = PS_STATE_AWAKE; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci /* Swap buffers by flipping the response index */ 11298c2ecf20Sopenharmony_ci BUG_ON(resp_idx > 1); 11308c2ecf20Sopenharmony_ci priv->resp_idx = resp_idx; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci wake_up(&priv->waitq); 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_notify_command_response); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_cistatic int __init lbs_init_module(void) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci memset(&confirm_sleep, 0, sizeof(confirm_sleep)); 11398c2ecf20Sopenharmony_ci confirm_sleep.hdr.command = cpu_to_le16(CMD_802_11_PS_MODE); 11408c2ecf20Sopenharmony_ci confirm_sleep.hdr.size = cpu_to_le16(sizeof(confirm_sleep)); 11418c2ecf20Sopenharmony_ci confirm_sleep.action = cpu_to_le16(PS_MODE_ACTION_SLEEP_CONFIRMED); 11428c2ecf20Sopenharmony_ci lbs_debugfs_init(); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci return 0; 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic void __exit lbs_exit_module(void) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci lbs_debugfs_remove(); 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_cimodule_init(lbs_init_module); 11538c2ecf20Sopenharmony_cimodule_exit(lbs_exit_module); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Libertas WLAN Driver Library"); 11568c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd."); 11578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1158