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