162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file contains the major functions in WLAN 462306a36Sopenharmony_ci * driver. It includes init, exit, open, close and main 562306a36Sopenharmony_ci * thread etc.. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/etherdevice.h> 1362306a36Sopenharmony_ci#include <linux/hardirq.h> 1462306a36Sopenharmony_ci#include <linux/netdevice.h> 1562306a36Sopenharmony_ci#include <linux/if_arp.h> 1662306a36Sopenharmony_ci#include <linux/kthread.h> 1762306a36Sopenharmony_ci#include <linux/kfifo.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <net/cfg80211.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "host.h" 2262306a36Sopenharmony_ci#include "decl.h" 2362306a36Sopenharmony_ci#include "dev.h" 2462306a36Sopenharmony_ci#include "cfg.h" 2562306a36Sopenharmony_ci#include "debugfs.h" 2662306a36Sopenharmony_ci#include "cmd.h" 2762306a36Sopenharmony_ci#include "mesh.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define DRIVER_RELEASE_VERSION "323.p0" 3062306a36Sopenharmony_ciconst char lbs_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION 3162306a36Sopenharmony_ci#ifdef DEBUG 3262306a36Sopenharmony_ci "-dbg" 3362306a36Sopenharmony_ci#endif 3462306a36Sopenharmony_ci ""; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* Module parameters */ 3862306a36Sopenharmony_ciunsigned int lbs_debug; 3962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_debug); 4062306a36Sopenharmony_cimodule_param_named(libertas_debug, lbs_debug, int, 0644); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic unsigned int lbs_disablemesh; 4362306a36Sopenharmony_cimodule_param_named(libertas_disablemesh, lbs_disablemesh, int, 0644); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * This global structure is used to send the confirm_sleep command as 4862306a36Sopenharmony_ci * fast as possible down to the firmware. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_cistruct cmd_confirm_sleep confirm_sleep; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* 5462306a36Sopenharmony_ci * the table to keep region code 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ciu16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE] = 5762306a36Sopenharmony_ci { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 }; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* 6062306a36Sopenharmony_ci * FW rate table. FW refers to rates by their index in this table, not by the 6162306a36Sopenharmony_ci * rate value itself. Values of 0x00 are 6262306a36Sopenharmony_ci * reserved positions. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistatic u8 fw_data_rates[MAX_RATES] = 6562306a36Sopenharmony_ci { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, 6662306a36Sopenharmony_ci 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x00 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/** 7062306a36Sopenharmony_ci * lbs_fw_index_to_data_rate - use index to get the data rate 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * @idx: The index of data rate 7362306a36Sopenharmony_ci * returns: data rate or 0 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ciu32 lbs_fw_index_to_data_rate(u8 idx) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci if (idx >= sizeof(fw_data_rates)) 7862306a36Sopenharmony_ci idx = 0; 7962306a36Sopenharmony_ci return fw_data_rates[idx]; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/** 8362306a36Sopenharmony_ci * lbs_data_rate_to_fw_index - use rate to get the index 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * @rate: data rate 8662306a36Sopenharmony_ci * returns: index or 0 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ciu8 lbs_data_rate_to_fw_index(u32 rate) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci u8 i; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (!rate) 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci for (i = 0; i < sizeof(fw_data_rates); i++) { 9662306a36Sopenharmony_ci if (rate == fw_data_rates[i]) 9762306a36Sopenharmony_ci return i; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ciint lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci int ret = 0; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci switch (type) { 10762306a36Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 10862306a36Sopenharmony_ci ret = lbs_set_monitor_mode(priv, 1); 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 11162306a36Sopenharmony_ci if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) 11262306a36Sopenharmony_ci ret = lbs_set_monitor_mode(priv, 0); 11362306a36Sopenharmony_ci if (!ret) 11462306a36Sopenharmony_ci ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 1); 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 11762306a36Sopenharmony_ci if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) 11862306a36Sopenharmony_ci ret = lbs_set_monitor_mode(priv, 0); 11962306a36Sopenharmony_ci if (!ret) 12062306a36Sopenharmony_ci ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 2); 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci default: 12362306a36Sopenharmony_ci ret = -ENOTSUPP; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci return ret; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ciint lbs_start_iface(struct lbs_private *priv) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct cmd_ds_802_11_mac_address cmd; 13162306a36Sopenharmony_ci int ret; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (priv->power_restore) { 13462306a36Sopenharmony_ci ret = priv->power_restore(priv); 13562306a36Sopenharmony_ci if (ret) 13662306a36Sopenharmony_ci return ret; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 14062306a36Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_SET); 14162306a36Sopenharmony_ci memcpy(cmd.macadd, priv->current_addr, ETH_ALEN); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd); 14462306a36Sopenharmony_ci if (ret) { 14562306a36Sopenharmony_ci lbs_deb_net("set MAC address failed\n"); 14662306a36Sopenharmony_ci goto err; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci ret = lbs_set_iface_type(priv, priv->wdev->iftype); 15062306a36Sopenharmony_ci if (ret) { 15162306a36Sopenharmony_ci lbs_deb_net("set iface type failed\n"); 15262306a36Sopenharmony_ci goto err; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ret = lbs_set_11d_domain_info(priv); 15662306a36Sopenharmony_ci if (ret) { 15762306a36Sopenharmony_ci lbs_deb_net("set 11d domain info failed\n"); 15862306a36Sopenharmony_ci goto err; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci lbs_update_channel(priv); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci priv->iface_running = true; 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cierr: 16762306a36Sopenharmony_ci if (priv->power_save) 16862306a36Sopenharmony_ci priv->power_save(priv); 16962306a36Sopenharmony_ci return ret; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/** 17362306a36Sopenharmony_ci * lbs_dev_open - open the ethX interface 17462306a36Sopenharmony_ci * 17562306a36Sopenharmony_ci * @dev: A pointer to &net_device structure 17662306a36Sopenharmony_ci * returns: 0 or -EBUSY if monitor mode active 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_cistatic int lbs_dev_open(struct net_device *dev) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct lbs_private *priv = dev->ml_priv; 18162306a36Sopenharmony_ci int ret = 0; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (!priv->iface_running) { 18462306a36Sopenharmony_ci ret = lbs_start_iface(priv); 18562306a36Sopenharmony_ci if (ret) 18662306a36Sopenharmony_ci goto out; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci netif_carrier_off(dev); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (!priv->tx_pending_len) 19462306a36Sopenharmony_ci netif_wake_queue(dev); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ciout: 19962306a36Sopenharmony_ci return ret; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic bool lbs_command_queue_empty(struct lbs_private *priv) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci unsigned long flags; 20562306a36Sopenharmony_ci bool ret; 20662306a36Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 20762306a36Sopenharmony_ci ret = priv->cur_cmd == NULL && list_empty(&priv->cmdpendingq); 20862306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 20962306a36Sopenharmony_ci return ret; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ciint lbs_stop_iface(struct lbs_private *priv) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci unsigned long flags; 21562306a36Sopenharmony_ci int ret = 0; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 21862306a36Sopenharmony_ci priv->iface_running = false; 21962306a36Sopenharmony_ci dev_kfree_skb_irq(priv->currenttxskb); 22062306a36Sopenharmony_ci priv->currenttxskb = NULL; 22162306a36Sopenharmony_ci priv->tx_pending_len = 0; 22262306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci cancel_work_sync(&priv->mcast_work); 22562306a36Sopenharmony_ci del_timer_sync(&priv->tx_lockup_timer); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Disable command processing, and wait for all commands to complete */ 22862306a36Sopenharmony_ci lbs_deb_main("waiting for commands to complete\n"); 22962306a36Sopenharmony_ci wait_event(priv->waitq, lbs_command_queue_empty(priv)); 23062306a36Sopenharmony_ci lbs_deb_main("all commands completed\n"); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (priv->power_save) 23362306a36Sopenharmony_ci ret = priv->power_save(priv); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return ret; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci/** 23962306a36Sopenharmony_ci * lbs_eth_stop - close the ethX interface 24062306a36Sopenharmony_ci * 24162306a36Sopenharmony_ci * @dev: A pointer to &net_device structure 24262306a36Sopenharmony_ci * returns: 0 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_cistatic int lbs_eth_stop(struct net_device *dev) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct lbs_private *priv = dev->ml_priv; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (priv->connect_status == LBS_CONNECTED) 24962306a36Sopenharmony_ci lbs_disconnect(priv, WLAN_REASON_DEAUTH_LEAVING); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 25262306a36Sopenharmony_ci netif_stop_queue(dev); 25362306a36Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci lbs_update_mcast(priv); 25662306a36Sopenharmony_ci cancel_delayed_work_sync(&priv->scan_work); 25762306a36Sopenharmony_ci if (priv->scan_req) 25862306a36Sopenharmony_ci lbs_scan_done(priv); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci netif_carrier_off(priv->dev); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (!lbs_iface_active(priv)) 26362306a36Sopenharmony_ci lbs_stop_iface(priv); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_civoid lbs_host_to_card_done(struct lbs_private *priv) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci unsigned long flags; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 27362306a36Sopenharmony_ci del_timer(&priv->tx_lockup_timer); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci priv->dnld_sent = DNLD_RES_RECEIVED; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* Wake main thread if commands are pending */ 27862306a36Sopenharmony_ci if (!priv->cur_cmd || priv->tx_pending_len > 0) { 27962306a36Sopenharmony_ci if (!priv->wakeup_dev_required) 28062306a36Sopenharmony_ci wake_up(&priv->waitq); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_host_to_card_done); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ciint lbs_set_mac_address(struct net_device *dev, void *addr) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci int ret = 0; 29062306a36Sopenharmony_ci struct lbs_private *priv = dev->ml_priv; 29162306a36Sopenharmony_ci struct sockaddr *phwaddr = addr; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* 29462306a36Sopenharmony_ci * Can only set MAC address when all interfaces are down, to be written 29562306a36Sopenharmony_ci * to the hardware when one of them is brought up. 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci if (lbs_iface_active(priv)) 29862306a36Sopenharmony_ci return -EBUSY; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* In case it was called from the mesh device */ 30162306a36Sopenharmony_ci dev = priv->dev; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci memcpy(priv->current_addr, phwaddr->sa_data, ETH_ALEN); 30462306a36Sopenharmony_ci eth_hw_addr_set(dev, phwaddr->sa_data); 30562306a36Sopenharmony_ci if (priv->mesh_dev) 30662306a36Sopenharmony_ci eth_hw_addr_set(priv->mesh_dev, phwaddr->sa_data); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return ret; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic inline int mac_in_list(unsigned char *list, int list_len, 31362306a36Sopenharmony_ci unsigned char *mac) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci while (list_len) { 31662306a36Sopenharmony_ci if (!memcmp(list, mac, ETH_ALEN)) 31762306a36Sopenharmony_ci return 1; 31862306a36Sopenharmony_ci list += ETH_ALEN; 31962306a36Sopenharmony_ci list_len--; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd, 32662306a36Sopenharmony_ci struct net_device *dev, int nr_addrs) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci int i = nr_addrs; 32962306a36Sopenharmony_ci struct netdev_hw_addr *ha; 33062306a36Sopenharmony_ci int cnt; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if ((dev->flags & (IFF_UP|IFF_MULTICAST)) != (IFF_UP|IFF_MULTICAST)) 33362306a36Sopenharmony_ci return nr_addrs; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci netif_addr_lock_bh(dev); 33662306a36Sopenharmony_ci cnt = netdev_mc_count(dev); 33762306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 33862306a36Sopenharmony_ci if (mac_in_list(cmd->maclist, nr_addrs, ha->addr)) { 33962306a36Sopenharmony_ci lbs_deb_net("mcast address %s:%pM skipped\n", dev->name, 34062306a36Sopenharmony_ci ha->addr); 34162306a36Sopenharmony_ci cnt--; 34262306a36Sopenharmony_ci continue; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (i == MRVDRV_MAX_MULTICAST_LIST_SIZE) 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci memcpy(&cmd->maclist[6*i], ha->addr, ETH_ALEN); 34862306a36Sopenharmony_ci lbs_deb_net("mcast address %s:%pM added to filter\n", dev->name, 34962306a36Sopenharmony_ci ha->addr); 35062306a36Sopenharmony_ci i++; 35162306a36Sopenharmony_ci cnt--; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci netif_addr_unlock_bh(dev); 35462306a36Sopenharmony_ci if (cnt) 35562306a36Sopenharmony_ci return -EOVERFLOW; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return i; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_civoid lbs_update_mcast(struct lbs_private *priv) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct cmd_ds_mac_multicast_adr mcast_cmd; 36362306a36Sopenharmony_ci int dev_flags = 0; 36462306a36Sopenharmony_ci int nr_addrs; 36562306a36Sopenharmony_ci int old_mac_control = priv->mac_control; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (netif_running(priv->dev)) 36862306a36Sopenharmony_ci dev_flags |= priv->dev->flags; 36962306a36Sopenharmony_ci if (priv->mesh_dev && netif_running(priv->mesh_dev)) 37062306a36Sopenharmony_ci dev_flags |= priv->mesh_dev->flags; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (dev_flags & IFF_PROMISC) { 37362306a36Sopenharmony_ci priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE; 37462306a36Sopenharmony_ci priv->mac_control &= ~(CMD_ACT_MAC_ALL_MULTICAST_ENABLE | 37562306a36Sopenharmony_ci CMD_ACT_MAC_MULTICAST_ENABLE); 37662306a36Sopenharmony_ci goto out_set_mac_control; 37762306a36Sopenharmony_ci } else if (dev_flags & IFF_ALLMULTI) { 37862306a36Sopenharmony_ci do_allmulti: 37962306a36Sopenharmony_ci priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; 38062306a36Sopenharmony_ci priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE | 38162306a36Sopenharmony_ci CMD_ACT_MAC_MULTICAST_ENABLE); 38262306a36Sopenharmony_ci goto out_set_mac_control; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* Once for priv->dev, again for priv->mesh_dev if it exists */ 38662306a36Sopenharmony_ci nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->dev, 0); 38762306a36Sopenharmony_ci if (nr_addrs >= 0 && priv->mesh_dev) 38862306a36Sopenharmony_ci nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->mesh_dev, nr_addrs); 38962306a36Sopenharmony_ci if (nr_addrs < 0) 39062306a36Sopenharmony_ci goto do_allmulti; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (nr_addrs) { 39362306a36Sopenharmony_ci int size = offsetof(struct cmd_ds_mac_multicast_adr, 39462306a36Sopenharmony_ci maclist[6*nr_addrs]); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci mcast_cmd.action = cpu_to_le16(CMD_ACT_SET); 39762306a36Sopenharmony_ci mcast_cmd.hdr.size = cpu_to_le16(size); 39862306a36Sopenharmony_ci mcast_cmd.nr_of_adrs = cpu_to_le16(nr_addrs); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci lbs_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &mcast_cmd.hdr, size); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; 40362306a36Sopenharmony_ci } else 40462306a36Sopenharmony_ci priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE | 40762306a36Sopenharmony_ci CMD_ACT_MAC_ALL_MULTICAST_ENABLE); 40862306a36Sopenharmony_ci out_set_mac_control: 40962306a36Sopenharmony_ci if (priv->mac_control != old_mac_control) 41062306a36Sopenharmony_ci lbs_set_mac_control(priv); 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic void lbs_set_mcast_worker(struct work_struct *work) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work); 41662306a36Sopenharmony_ci lbs_update_mcast(priv); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_civoid lbs_set_multicast_list(struct net_device *dev) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct lbs_private *priv = dev->ml_priv; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci schedule_work(&priv->mcast_work); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci/** 42762306a36Sopenharmony_ci * lbs_thread - handles the major jobs in the LBS driver. 42862306a36Sopenharmony_ci * It handles all events generated by firmware, RX data received 42962306a36Sopenharmony_ci * from firmware and TX data sent from kernel. 43062306a36Sopenharmony_ci * 43162306a36Sopenharmony_ci * @data: A pointer to &lbs_thread structure 43262306a36Sopenharmony_ci * returns: 0 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_cistatic int lbs_thread(void *data) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct net_device *dev = data; 43762306a36Sopenharmony_ci struct lbs_private *priv = dev->ml_priv; 43862306a36Sopenharmony_ci wait_queue_entry_t wait; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci init_waitqueue_entry(&wait, current); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci for (;;) { 44362306a36Sopenharmony_ci int shouldsleep; 44462306a36Sopenharmony_ci u8 resp_idx; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci lbs_deb_thread("1: currenttxskb %p, dnld_sent %d\n", 44762306a36Sopenharmony_ci priv->currenttxskb, priv->dnld_sent); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci add_wait_queue(&priv->waitq, &wait); 45062306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 45162306a36Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (kthread_should_stop()) 45462306a36Sopenharmony_ci shouldsleep = 0; /* Bye */ 45562306a36Sopenharmony_ci else if (priv->surpriseremoved) 45662306a36Sopenharmony_ci shouldsleep = 1; /* We need to wait until we're _told_ to die */ 45762306a36Sopenharmony_ci else if (priv->psstate == PS_STATE_SLEEP) 45862306a36Sopenharmony_ci shouldsleep = 1; /* Sleep mode. Nothing we can do till it wakes */ 45962306a36Sopenharmony_ci else if (priv->cmd_timed_out) 46062306a36Sopenharmony_ci shouldsleep = 0; /* Command timed out. Recover */ 46162306a36Sopenharmony_ci else if (!priv->fw_ready) 46262306a36Sopenharmony_ci shouldsleep = 1; /* Firmware not ready. We're waiting for it */ 46362306a36Sopenharmony_ci else if (priv->dnld_sent) 46462306a36Sopenharmony_ci shouldsleep = 1; /* Something is en route to the device already */ 46562306a36Sopenharmony_ci else if (priv->tx_pending_len > 0) 46662306a36Sopenharmony_ci shouldsleep = 0; /* We've a packet to send */ 46762306a36Sopenharmony_ci else if (priv->resp_len[priv->resp_idx]) 46862306a36Sopenharmony_ci shouldsleep = 0; /* We have a command response */ 46962306a36Sopenharmony_ci else if (priv->cur_cmd) 47062306a36Sopenharmony_ci shouldsleep = 1; /* Can't send a command; one already running */ 47162306a36Sopenharmony_ci else if (!list_empty(&priv->cmdpendingq) && 47262306a36Sopenharmony_ci !(priv->wakeup_dev_required)) 47362306a36Sopenharmony_ci shouldsleep = 0; /* We have a command to send */ 47462306a36Sopenharmony_ci else if (kfifo_len(&priv->event_fifo)) 47562306a36Sopenharmony_ci shouldsleep = 0; /* We have an event to process */ 47662306a36Sopenharmony_ci else 47762306a36Sopenharmony_ci shouldsleep = 1; /* No command */ 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (shouldsleep) { 48062306a36Sopenharmony_ci lbs_deb_thread("sleeping, connect_status %d, " 48162306a36Sopenharmony_ci "psmode %d, psstate %d\n", 48262306a36Sopenharmony_ci priv->connect_status, 48362306a36Sopenharmony_ci priv->psmode, priv->psstate); 48462306a36Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 48562306a36Sopenharmony_ci schedule(); 48662306a36Sopenharmony_ci } else 48762306a36Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci lbs_deb_thread("2: currenttxskb %p, dnld_send %d\n", 49062306a36Sopenharmony_ci priv->currenttxskb, priv->dnld_sent); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 49362306a36Sopenharmony_ci remove_wait_queue(&priv->waitq, &wait); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci lbs_deb_thread("3: currenttxskb %p, dnld_sent %d\n", 49662306a36Sopenharmony_ci priv->currenttxskb, priv->dnld_sent); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (kthread_should_stop()) { 49962306a36Sopenharmony_ci lbs_deb_thread("break from main thread\n"); 50062306a36Sopenharmony_ci break; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (priv->surpriseremoved) { 50462306a36Sopenharmony_ci lbs_deb_thread("adapter removed; waiting to die...\n"); 50562306a36Sopenharmony_ci continue; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci lbs_deb_thread("4: currenttxskb %p, dnld_sent %d\n", 50962306a36Sopenharmony_ci priv->currenttxskb, priv->dnld_sent); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* Process any pending command response */ 51262306a36Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 51362306a36Sopenharmony_ci resp_idx = priv->resp_idx; 51462306a36Sopenharmony_ci if (priv->resp_len[resp_idx]) { 51562306a36Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 51662306a36Sopenharmony_ci lbs_process_command_response(priv, 51762306a36Sopenharmony_ci priv->resp_buf[resp_idx], 51862306a36Sopenharmony_ci priv->resp_len[resp_idx]); 51962306a36Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 52062306a36Sopenharmony_ci priv->resp_len[resp_idx] = 0; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* Process hardware events, e.g. card removed, link lost */ 52562306a36Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 52662306a36Sopenharmony_ci while (kfifo_len(&priv->event_fifo)) { 52762306a36Sopenharmony_ci u32 event; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (kfifo_out(&priv->event_fifo, 53062306a36Sopenharmony_ci (unsigned char *) &event, sizeof(event)) != 53162306a36Sopenharmony_ci sizeof(event)) 53262306a36Sopenharmony_ci break; 53362306a36Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 53462306a36Sopenharmony_ci lbs_process_event(priv, event); 53562306a36Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (priv->wakeup_dev_required) { 54062306a36Sopenharmony_ci lbs_deb_thread("Waking up device...\n"); 54162306a36Sopenharmony_ci /* Wake up device */ 54262306a36Sopenharmony_ci if (priv->exit_deep_sleep(priv)) 54362306a36Sopenharmony_ci lbs_deb_thread("Wakeup device failed\n"); 54462306a36Sopenharmony_ci continue; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* command timeout stuff */ 54862306a36Sopenharmony_ci if (priv->cmd_timed_out && priv->cur_cmd) { 54962306a36Sopenharmony_ci struct cmd_ctrl_node *cmdnode = priv->cur_cmd; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci netdev_info(dev, "Timeout submitting command 0x%04x\n", 55262306a36Sopenharmony_ci le16_to_cpu(cmdnode->cmdbuf->command)); 55362306a36Sopenharmony_ci lbs_complete_command(priv, cmdnode, -ETIMEDOUT); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* Reset card, but only when it isn't in the process 55662306a36Sopenharmony_ci * of being shutdown anyway. */ 55762306a36Sopenharmony_ci if (!dev->dismantle && priv->reset_card) 55862306a36Sopenharmony_ci priv->reset_card(priv); 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci priv->cmd_timed_out = 0; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (!priv->fw_ready) 56362306a36Sopenharmony_ci continue; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* Check if we need to confirm Sleep Request received previously */ 56662306a36Sopenharmony_ci if (priv->psstate == PS_STATE_PRE_SLEEP && 56762306a36Sopenharmony_ci !priv->dnld_sent && !priv->cur_cmd) { 56862306a36Sopenharmony_ci if (priv->connect_status == LBS_CONNECTED) { 56962306a36Sopenharmony_ci lbs_deb_thread("pre-sleep, currenttxskb %p, " 57062306a36Sopenharmony_ci "dnld_sent %d, cur_cmd %p\n", 57162306a36Sopenharmony_ci priv->currenttxskb, priv->dnld_sent, 57262306a36Sopenharmony_ci priv->cur_cmd); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci lbs_ps_confirm_sleep(priv); 57562306a36Sopenharmony_ci } else { 57662306a36Sopenharmony_ci /* workaround for firmware sending 57762306a36Sopenharmony_ci * deauth/linkloss event immediately 57862306a36Sopenharmony_ci * after sleep request; remove this 57962306a36Sopenharmony_ci * after firmware fixes it 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_ci priv->psstate = PS_STATE_AWAKE; 58262306a36Sopenharmony_ci netdev_alert(dev, 58362306a36Sopenharmony_ci "ignore PS_SleepConfirm in non-connected state\n"); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* The PS state is changed during processing of Sleep Request 58862306a36Sopenharmony_ci * event above 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_ci if ((priv->psstate == PS_STATE_SLEEP) || 59162306a36Sopenharmony_ci (priv->psstate == PS_STATE_PRE_SLEEP)) 59262306a36Sopenharmony_ci continue; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (priv->is_deep_sleep) 59562306a36Sopenharmony_ci continue; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Execute the next command */ 59862306a36Sopenharmony_ci if (!priv->dnld_sent && !priv->cur_cmd) 59962306a36Sopenharmony_ci lbs_execute_next_command(priv); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci spin_lock_irq(&priv->driver_lock); 60262306a36Sopenharmony_ci if (!priv->dnld_sent && priv->tx_pending_len > 0) { 60362306a36Sopenharmony_ci int ret = priv->hw_host_to_card(priv, MVMS_DAT, 60462306a36Sopenharmony_ci priv->tx_pending_buf, 60562306a36Sopenharmony_ci priv->tx_pending_len); 60662306a36Sopenharmony_ci if (ret) { 60762306a36Sopenharmony_ci lbs_deb_tx("host_to_card failed %d\n", ret); 60862306a36Sopenharmony_ci priv->dnld_sent = DNLD_RES_RECEIVED; 60962306a36Sopenharmony_ci } else { 61062306a36Sopenharmony_ci mod_timer(&priv->tx_lockup_timer, 61162306a36Sopenharmony_ci jiffies + (HZ * 5)); 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci priv->tx_pending_len = 0; 61462306a36Sopenharmony_ci if (!priv->currenttxskb) { 61562306a36Sopenharmony_ci /* We can wake the queues immediately if we aren't 61662306a36Sopenharmony_ci waiting for TX feedback */ 61762306a36Sopenharmony_ci if (priv->connect_status == LBS_CONNECTED) 61862306a36Sopenharmony_ci netif_wake_queue(priv->dev); 61962306a36Sopenharmony_ci if (priv->mesh_dev && 62062306a36Sopenharmony_ci netif_running(priv->mesh_dev)) 62162306a36Sopenharmony_ci netif_wake_queue(priv->mesh_dev); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci spin_unlock_irq(&priv->driver_lock); 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci del_timer(&priv->command_timer); 62862306a36Sopenharmony_ci del_timer(&priv->tx_lockup_timer); 62962306a36Sopenharmony_ci del_timer(&priv->auto_deepsleep_timer); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci return 0; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci/** 63562306a36Sopenharmony_ci * lbs_setup_firmware - gets the HW spec from the firmware and sets 63662306a36Sopenharmony_ci * some basic parameters 63762306a36Sopenharmony_ci * 63862306a36Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 63962306a36Sopenharmony_ci * returns: 0 or -1 64062306a36Sopenharmony_ci */ 64162306a36Sopenharmony_cistatic int lbs_setup_firmware(struct lbs_private *priv) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci int ret = -1; 64462306a36Sopenharmony_ci s16 curlevel = 0, minlevel = 0, maxlevel = 0; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* Read MAC address from firmware */ 64762306a36Sopenharmony_ci eth_broadcast_addr(priv->current_addr); 64862306a36Sopenharmony_ci ret = lbs_update_hw_spec(priv); 64962306a36Sopenharmony_ci if (ret) 65062306a36Sopenharmony_ci goto done; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* Read power levels if available */ 65362306a36Sopenharmony_ci ret = lbs_get_tx_power(priv, &curlevel, &minlevel, &maxlevel); 65462306a36Sopenharmony_ci if (ret == 0) { 65562306a36Sopenharmony_ci priv->txpower_cur = curlevel; 65662306a36Sopenharmony_ci priv->txpower_min = minlevel; 65762306a36Sopenharmony_ci priv->txpower_max = maxlevel; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci /* Send cmd to FW to enable 11D function */ 66162306a36Sopenharmony_ci ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_11D_ENABLE, 1); 66262306a36Sopenharmony_ci if (ret) 66362306a36Sopenharmony_ci goto done; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci ret = lbs_set_mac_control_sync(priv); 66662306a36Sopenharmony_cidone: 66762306a36Sopenharmony_ci return ret; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ciint lbs_suspend(struct lbs_private *priv) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci int ret; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (priv->is_deep_sleep) { 67562306a36Sopenharmony_ci ret = lbs_set_deep_sleep(priv, 0); 67662306a36Sopenharmony_ci if (ret) { 67762306a36Sopenharmony_ci netdev_err(priv->dev, 67862306a36Sopenharmony_ci "deep sleep cancellation failed: %d\n", ret); 67962306a36Sopenharmony_ci return ret; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci priv->deep_sleep_required = 1; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci ret = lbs_set_host_sleep(priv, 1); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci netif_device_detach(priv->dev); 68762306a36Sopenharmony_ci if (priv->mesh_dev) 68862306a36Sopenharmony_ci netif_device_detach(priv->mesh_dev); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci return ret; 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_suspend); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ciint lbs_resume(struct lbs_private *priv) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci int ret; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci ret = lbs_set_host_sleep(priv, 0); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci netif_device_attach(priv->dev); 70162306a36Sopenharmony_ci if (priv->mesh_dev) 70262306a36Sopenharmony_ci netif_device_attach(priv->mesh_dev); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (priv->deep_sleep_required) { 70562306a36Sopenharmony_ci priv->deep_sleep_required = 0; 70662306a36Sopenharmony_ci ret = lbs_set_deep_sleep(priv, 1); 70762306a36Sopenharmony_ci if (ret) 70862306a36Sopenharmony_ci netdev_err(priv->dev, 70962306a36Sopenharmony_ci "deep sleep activation failed: %d\n", ret); 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (priv->setup_fw_on_resume) 71362306a36Sopenharmony_ci ret = lbs_setup_firmware(priv); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci return ret; 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_resume); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci/** 72062306a36Sopenharmony_ci * lbs_cmd_timeout_handler - handles the timeout of command sending. 72162306a36Sopenharmony_ci * It will re-send the same command again. 72262306a36Sopenharmony_ci * 72362306a36Sopenharmony_ci * @t: Context from which to retrieve a &struct lbs_private pointer 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_cistatic void lbs_cmd_timeout_handler(struct timer_list *t) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci struct lbs_private *priv = from_timer(priv, t, command_timer); 72862306a36Sopenharmony_ci unsigned long flags; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (!priv->cur_cmd) 73362306a36Sopenharmony_ci goto out; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci netdev_info(priv->dev, "command 0x%04x timed out\n", 73662306a36Sopenharmony_ci le16_to_cpu(priv->cur_cmd->cmdbuf->command)); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci priv->cmd_timed_out = 1; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* 74162306a36Sopenharmony_ci * If the device didn't even acknowledge the command, reset the state 74262306a36Sopenharmony_ci * so that we don't block all future commands due to this one timeout. 74362306a36Sopenharmony_ci */ 74462306a36Sopenharmony_ci if (priv->dnld_sent == DNLD_CMD_SENT) 74562306a36Sopenharmony_ci priv->dnld_sent = DNLD_RES_RECEIVED; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci wake_up(&priv->waitq); 74862306a36Sopenharmony_ciout: 74962306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci/** 75362306a36Sopenharmony_ci * lbs_tx_lockup_handler - handles the timeout of the passing of TX frames 75462306a36Sopenharmony_ci * to the hardware. This is known to frequently happen with SD8686 when 75562306a36Sopenharmony_ci * waking up after a Wake-on-WLAN-triggered resume. 75662306a36Sopenharmony_ci * 75762306a36Sopenharmony_ci * @t: Context from which to retrieve a &struct lbs_private pointer 75862306a36Sopenharmony_ci */ 75962306a36Sopenharmony_cistatic void lbs_tx_lockup_handler(struct timer_list *t) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci struct lbs_private *priv = from_timer(priv, t, tx_lockup_timer); 76262306a36Sopenharmony_ci unsigned long flags; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci netdev_info(priv->dev, "TX lockup detected\n"); 76762306a36Sopenharmony_ci if (priv->reset_card) 76862306a36Sopenharmony_ci priv->reset_card(priv); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci priv->dnld_sent = DNLD_RES_RECEIVED; 77162306a36Sopenharmony_ci wake_up_interruptible(&priv->waitq); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci/** 77762306a36Sopenharmony_ci * auto_deepsleep_timer_fn - put the device back to deep sleep mode when 77862306a36Sopenharmony_ci * timer expires and no activity (command, event, data etc.) is detected. 77962306a36Sopenharmony_ci * @t: Context from which to retrieve a &struct lbs_private pointer 78062306a36Sopenharmony_ci * returns: N/A 78162306a36Sopenharmony_ci */ 78262306a36Sopenharmony_cistatic void auto_deepsleep_timer_fn(struct timer_list *t) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci struct lbs_private *priv = from_timer(priv, t, auto_deepsleep_timer); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (priv->is_activity_detected) { 78762306a36Sopenharmony_ci priv->is_activity_detected = 0; 78862306a36Sopenharmony_ci } else { 78962306a36Sopenharmony_ci if (priv->is_auto_deep_sleep_enabled && 79062306a36Sopenharmony_ci (!priv->wakeup_dev_required) && 79162306a36Sopenharmony_ci (priv->connect_status != LBS_CONNECTED)) { 79262306a36Sopenharmony_ci struct cmd_header cmd; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci lbs_deb_main("Entering auto deep sleep mode...\n"); 79562306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 79662306a36Sopenharmony_ci cmd.size = cpu_to_le16(sizeof(cmd)); 79762306a36Sopenharmony_ci lbs_cmd_async(priv, CMD_802_11_DEEP_SLEEP, &cmd, 79862306a36Sopenharmony_ci sizeof(cmd)); 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci mod_timer(&priv->auto_deepsleep_timer , jiffies + 80262306a36Sopenharmony_ci (priv->auto_deep_sleep_timeout * HZ)/1000); 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ciint lbs_enter_auto_deep_sleep(struct lbs_private *priv) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci priv->is_auto_deep_sleep_enabled = 1; 80862306a36Sopenharmony_ci if (priv->is_deep_sleep) 80962306a36Sopenharmony_ci priv->wakeup_dev_required = 1; 81062306a36Sopenharmony_ci mod_timer(&priv->auto_deepsleep_timer , 81162306a36Sopenharmony_ci jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci return 0; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ciint lbs_exit_auto_deep_sleep(struct lbs_private *priv) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci priv->is_auto_deep_sleep_enabled = 0; 81962306a36Sopenharmony_ci priv->auto_deep_sleep_timeout = 0; 82062306a36Sopenharmony_ci del_timer(&priv->auto_deepsleep_timer); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci return 0; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic int lbs_init_adapter(struct lbs_private *priv) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci int ret; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci eth_broadcast_addr(priv->current_addr); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci priv->connect_status = LBS_DISCONNECTED; 83262306a36Sopenharmony_ci priv->channel = DEFAULT_AD_HOC_CHANNEL; 83362306a36Sopenharmony_ci priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; 83462306a36Sopenharmony_ci priv->radio_on = 1; 83562306a36Sopenharmony_ci priv->psmode = LBS802_11POWERMODECAM; 83662306a36Sopenharmony_ci priv->psstate = PS_STATE_FULL_POWER; 83762306a36Sopenharmony_ci priv->is_deep_sleep = 0; 83862306a36Sopenharmony_ci priv->is_auto_deep_sleep_enabled = 0; 83962306a36Sopenharmony_ci priv->deep_sleep_required = 0; 84062306a36Sopenharmony_ci priv->wakeup_dev_required = 0; 84162306a36Sopenharmony_ci init_waitqueue_head(&priv->ds_awake_q); 84262306a36Sopenharmony_ci init_waitqueue_head(&priv->scan_q); 84362306a36Sopenharmony_ci priv->authtype_auto = 1; 84462306a36Sopenharmony_ci priv->is_host_sleep_configured = 0; 84562306a36Sopenharmony_ci priv->is_host_sleep_activated = 0; 84662306a36Sopenharmony_ci init_waitqueue_head(&priv->host_sleep_q); 84762306a36Sopenharmony_ci init_waitqueue_head(&priv->fw_waitq); 84862306a36Sopenharmony_ci mutex_init(&priv->lock); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci timer_setup(&priv->command_timer, lbs_cmd_timeout_handler, 0); 85162306a36Sopenharmony_ci timer_setup(&priv->tx_lockup_timer, lbs_tx_lockup_handler, 0); 85262306a36Sopenharmony_ci timer_setup(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn, 0); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci INIT_LIST_HEAD(&priv->cmdfreeq); 85562306a36Sopenharmony_ci INIT_LIST_HEAD(&priv->cmdpendingq); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci spin_lock_init(&priv->driver_lock); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* Allocate the command buffers */ 86062306a36Sopenharmony_ci if (lbs_allocate_cmd_buffer(priv)) { 86162306a36Sopenharmony_ci pr_err("Out of memory allocating command buffers\n"); 86262306a36Sopenharmony_ci ret = -ENOMEM; 86362306a36Sopenharmony_ci goto out; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci priv->resp_idx = 0; 86662306a36Sopenharmony_ci priv->resp_len[0] = priv->resp_len[1] = 0; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* Create the event FIFO */ 86962306a36Sopenharmony_ci ret = kfifo_alloc(&priv->event_fifo, sizeof(u32) * 16, GFP_KERNEL); 87062306a36Sopenharmony_ci if (ret) { 87162306a36Sopenharmony_ci pr_err("Out of memory allocating event FIFO buffer\n"); 87262306a36Sopenharmony_ci lbs_free_cmd_buffer(priv); 87362306a36Sopenharmony_ci goto out; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ciout: 87762306a36Sopenharmony_ci return ret; 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic void lbs_free_adapter(struct lbs_private *priv) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci lbs_free_cmd_buffer(priv); 88362306a36Sopenharmony_ci kfifo_free(&priv->event_fifo); 88462306a36Sopenharmony_ci del_timer(&priv->command_timer); 88562306a36Sopenharmony_ci del_timer(&priv->tx_lockup_timer); 88662306a36Sopenharmony_ci del_timer(&priv->auto_deepsleep_timer); 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cistatic const struct net_device_ops lbs_netdev_ops = { 89062306a36Sopenharmony_ci .ndo_open = lbs_dev_open, 89162306a36Sopenharmony_ci .ndo_stop = lbs_eth_stop, 89262306a36Sopenharmony_ci .ndo_start_xmit = lbs_hard_start_xmit, 89362306a36Sopenharmony_ci .ndo_set_mac_address = lbs_set_mac_address, 89462306a36Sopenharmony_ci .ndo_set_rx_mode = lbs_set_multicast_list, 89562306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 89662306a36Sopenharmony_ci}; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci/** 89962306a36Sopenharmony_ci * lbs_add_card - adds the card. It will probe the 90062306a36Sopenharmony_ci * card, allocate the lbs_priv and initialize the device. 90162306a36Sopenharmony_ci * 90262306a36Sopenharmony_ci * @card: A pointer to card 90362306a36Sopenharmony_ci * @dmdev: A pointer to &struct device 90462306a36Sopenharmony_ci * returns: A pointer to &struct lbs_private structure 90562306a36Sopenharmony_ci */ 90662306a36Sopenharmony_cistruct lbs_private *lbs_add_card(void *card, struct device *dmdev) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci struct net_device *dev; 90962306a36Sopenharmony_ci struct wireless_dev *wdev; 91062306a36Sopenharmony_ci struct lbs_private *priv = NULL; 91162306a36Sopenharmony_ci int err; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci /* Allocate an Ethernet device and register it */ 91462306a36Sopenharmony_ci wdev = lbs_cfg_alloc(dmdev); 91562306a36Sopenharmony_ci if (IS_ERR(wdev)) { 91662306a36Sopenharmony_ci err = PTR_ERR(wdev); 91762306a36Sopenharmony_ci pr_err("cfg80211 init failed\n"); 91862306a36Sopenharmony_ci goto err_cfg; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci wdev->iftype = NL80211_IFTYPE_STATION; 92262306a36Sopenharmony_ci priv = wdev_priv(wdev); 92362306a36Sopenharmony_ci priv->wdev = wdev; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci err = lbs_init_adapter(priv); 92662306a36Sopenharmony_ci if (err) { 92762306a36Sopenharmony_ci pr_err("failed to initialize adapter structure\n"); 92862306a36Sopenharmony_ci goto err_wdev; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci dev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, ether_setup); 93262306a36Sopenharmony_ci if (!dev) { 93362306a36Sopenharmony_ci err = -ENOMEM; 93462306a36Sopenharmony_ci dev_err(dmdev, "no memory for network device instance\n"); 93562306a36Sopenharmony_ci goto err_adapter; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci dev->ieee80211_ptr = wdev; 93962306a36Sopenharmony_ci dev->ml_priv = priv; 94062306a36Sopenharmony_ci SET_NETDEV_DEV(dev, dmdev); 94162306a36Sopenharmony_ci wdev->netdev = dev; 94262306a36Sopenharmony_ci priv->dev = dev; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci dev->netdev_ops = &lbs_netdev_ops; 94562306a36Sopenharmony_ci dev->watchdog_timeo = 5 * HZ; 94662306a36Sopenharmony_ci dev->ethtool_ops = &lbs_ethtool_ops; 94762306a36Sopenharmony_ci dev->flags |= IFF_BROADCAST | IFF_MULTICAST; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci priv->card = card; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci strcpy(dev->name, "wlan%d"); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci lbs_deb_thread("Starting main thread...\n"); 95462306a36Sopenharmony_ci init_waitqueue_head(&priv->waitq); 95562306a36Sopenharmony_ci priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main"); 95662306a36Sopenharmony_ci if (IS_ERR(priv->main_thread)) { 95762306a36Sopenharmony_ci err = PTR_ERR(priv->main_thread); 95862306a36Sopenharmony_ci lbs_deb_thread("Error creating main thread.\n"); 95962306a36Sopenharmony_ci goto err_ndev; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci priv->work_thread = create_singlethread_workqueue("lbs_worker"); 96362306a36Sopenharmony_ci INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci priv->wol_criteria = EHS_REMOVE_WAKEUP; 96662306a36Sopenharmony_ci priv->wol_gpio = 0xff; 96762306a36Sopenharmony_ci priv->wol_gap = 20; 96862306a36Sopenharmony_ci priv->ehs_remove_supported = true; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci return priv; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci err_ndev: 97362306a36Sopenharmony_ci free_netdev(dev); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci err_adapter: 97662306a36Sopenharmony_ci lbs_free_adapter(priv); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci err_wdev: 97962306a36Sopenharmony_ci lbs_cfg_free(priv); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci err_cfg: 98262306a36Sopenharmony_ci return ERR_PTR(err); 98362306a36Sopenharmony_ci} 98462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_add_card); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_civoid lbs_remove_card(struct lbs_private *priv) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci struct net_device *dev = priv->dev; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci lbs_remove_mesh(priv); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (priv->wiphy_registered) 99462306a36Sopenharmony_ci lbs_scan_deinit(priv); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci lbs_wait_for_firmware_load(priv); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* worker thread destruction blocks on the in-flight command which 99962306a36Sopenharmony_ci * should have been cleared already in lbs_stop_card(). 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_ci lbs_deb_main("destroying worker thread\n"); 100262306a36Sopenharmony_ci destroy_workqueue(priv->work_thread); 100362306a36Sopenharmony_ci lbs_deb_main("done destroying worker thread\n"); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci if (priv->psmode == LBS802_11POWERMODEMAX_PSP) { 100662306a36Sopenharmony_ci priv->psmode = LBS802_11POWERMODECAM; 100762306a36Sopenharmony_ci /* no need to wakeup if already woken up, 100862306a36Sopenharmony_ci * on suspend, this exit ps command is not processed 100962306a36Sopenharmony_ci * the driver hangs 101062306a36Sopenharmony_ci */ 101162306a36Sopenharmony_ci if (priv->psstate != PS_STATE_FULL_POWER) 101262306a36Sopenharmony_ci lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, true); 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (priv->is_deep_sleep) { 101662306a36Sopenharmony_ci priv->is_deep_sleep = 0; 101762306a36Sopenharmony_ci wake_up_interruptible(&priv->ds_awake_q); 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci priv->is_host_sleep_configured = 0; 102162306a36Sopenharmony_ci priv->is_host_sleep_activated = 0; 102262306a36Sopenharmony_ci wake_up_interruptible(&priv->host_sleep_q); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci /* Stop the thread servicing the interrupts */ 102562306a36Sopenharmony_ci priv->surpriseremoved = 1; 102662306a36Sopenharmony_ci kthread_stop(priv->main_thread); 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci lbs_free_adapter(priv); 102962306a36Sopenharmony_ci lbs_cfg_free(priv); 103062306a36Sopenharmony_ci free_netdev(dev); 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_remove_card); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ciint lbs_rtap_supported(struct lbs_private *priv) 103662306a36Sopenharmony_ci{ 103762306a36Sopenharmony_ci if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) 103862306a36Sopenharmony_ci return 1; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci /* newer firmware use a capability mask */ 104162306a36Sopenharmony_ci return ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && 104262306a36Sopenharmony_ci (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)); 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ciint lbs_start_card(struct lbs_private *priv) 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci struct net_device *dev = priv->dev; 104962306a36Sopenharmony_ci int ret; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci /* poke the firmware */ 105262306a36Sopenharmony_ci ret = lbs_setup_firmware(priv); 105362306a36Sopenharmony_ci if (ret) 105462306a36Sopenharmony_ci goto done; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci if (!lbs_disablemesh) 105762306a36Sopenharmony_ci lbs_init_mesh(priv); 105862306a36Sopenharmony_ci else 105962306a36Sopenharmony_ci pr_info("%s: mesh disabled\n", dev->name); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci ret = lbs_cfg_register(priv); 106262306a36Sopenharmony_ci if (ret) { 106362306a36Sopenharmony_ci pr_err("cannot register device\n"); 106462306a36Sopenharmony_ci goto done; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (lbs_mesh_activated(priv)) 106862306a36Sopenharmony_ci lbs_start_mesh(priv); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci lbs_debugfs_init_one(priv, dev); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci netdev_info(dev, "Marvell WLAN 802.11 adapter\n"); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci ret = 0; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_cidone: 107762306a36Sopenharmony_ci return ret; 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_start_card); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_civoid lbs_stop_card(struct lbs_private *priv) 108362306a36Sopenharmony_ci{ 108462306a36Sopenharmony_ci struct net_device *dev; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci if (!priv) 108762306a36Sopenharmony_ci return; 108862306a36Sopenharmony_ci dev = priv->dev; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci /* If the netdev isn't registered, it means that lbs_start_card() was 109162306a36Sopenharmony_ci * never called so we have nothing to do here. */ 109262306a36Sopenharmony_ci if (dev->reg_state != NETREG_REGISTERED) 109362306a36Sopenharmony_ci return; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci netif_stop_queue(dev); 109662306a36Sopenharmony_ci netif_carrier_off(dev); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci lbs_debugfs_remove_one(priv); 109962306a36Sopenharmony_ci lbs_deinit_mesh(priv); 110062306a36Sopenharmony_ci unregister_netdev(dev); 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_stop_card); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_civoid lbs_queue_event(struct lbs_private *priv, u32 event) 110662306a36Sopenharmony_ci{ 110762306a36Sopenharmony_ci unsigned long flags; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci if (priv->psstate == PS_STATE_SLEEP) 111262306a36Sopenharmony_ci priv->psstate = PS_STATE_AWAKE; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci kfifo_in(&priv->event_fifo, (unsigned char *) &event, sizeof(u32)); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci wake_up(&priv->waitq); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 111962306a36Sopenharmony_ci} 112062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_queue_event); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_civoid lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci if (priv->psstate == PS_STATE_SLEEP) 112562306a36Sopenharmony_ci priv->psstate = PS_STATE_AWAKE; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci /* Swap buffers by flipping the response index */ 112862306a36Sopenharmony_ci BUG_ON(resp_idx > 1); 112962306a36Sopenharmony_ci priv->resp_idx = resp_idx; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci wake_up(&priv->waitq); 113262306a36Sopenharmony_ci} 113362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_notify_command_response); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_cistatic int __init lbs_init_module(void) 113662306a36Sopenharmony_ci{ 113762306a36Sopenharmony_ci memset(&confirm_sleep, 0, sizeof(confirm_sleep)); 113862306a36Sopenharmony_ci confirm_sleep.hdr.command = cpu_to_le16(CMD_802_11_PS_MODE); 113962306a36Sopenharmony_ci confirm_sleep.hdr.size = cpu_to_le16(sizeof(confirm_sleep)); 114062306a36Sopenharmony_ci confirm_sleep.action = cpu_to_le16(PS_MODE_ACTION_SLEEP_CONFIRMED); 114162306a36Sopenharmony_ci lbs_debugfs_init(); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci return 0; 114462306a36Sopenharmony_ci} 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_cistatic void __exit lbs_exit_module(void) 114762306a36Sopenharmony_ci{ 114862306a36Sopenharmony_ci lbs_debugfs_remove(); 114962306a36Sopenharmony_ci} 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_cimodule_init(lbs_init_module); 115262306a36Sopenharmony_cimodule_exit(lbs_exit_module); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ciMODULE_DESCRIPTION("Libertas WLAN Driver Library"); 115562306a36Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd."); 115662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1157