brintos

brintos / linux-shallow public Read only

0
0
Text · 18.6 KiB · b47a832 Raw
733 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  Copyright (C) 2008, cozybit Inc.4 *  Copyright (C) 2003-2006, Marvell International Ltd.5 */6#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt7 8#include <linux/hardirq.h>9#include <linux/slab.h>10 11#include <linux/etherdevice.h>12#include <linux/module.h>13#include "libertas_tf.h"14 15/* thinfirm version: 5.132.X.pX */16#define LBTF_FW_VER_MIN		0x0584030017#define LBTF_FW_VER_MAX		0x0584ffff18 19/* Module parameters */20unsigned int lbtf_debug;21EXPORT_SYMBOL_GPL(lbtf_debug);22module_param_named(libertas_tf_debug, lbtf_debug, int, 0644);23 24struct workqueue_struct *lbtf_wq;25 26static const struct ieee80211_channel lbtf_channels[] = {27	{ .center_freq = 2412, .hw_value = 1 },28	{ .center_freq = 2417, .hw_value = 2 },29	{ .center_freq = 2422, .hw_value = 3 },30	{ .center_freq = 2427, .hw_value = 4 },31	{ .center_freq = 2432, .hw_value = 5 },32	{ .center_freq = 2437, .hw_value = 6 },33	{ .center_freq = 2442, .hw_value = 7 },34	{ .center_freq = 2447, .hw_value = 8 },35	{ .center_freq = 2452, .hw_value = 9 },36	{ .center_freq = 2457, .hw_value = 10 },37	{ .center_freq = 2462, .hw_value = 11 },38	{ .center_freq = 2467, .hw_value = 12 },39	{ .center_freq = 2472, .hw_value = 13 },40	{ .center_freq = 2484, .hw_value = 14 },41};42 43/* This table contains the hardware specific values for the modulation rates. */44static const struct ieee80211_rate lbtf_rates[] = {45	{ .bitrate = 10,46	  .hw_value = 0, },47	{ .bitrate = 20,48	  .hw_value = 1,49	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },50	{ .bitrate = 55,51	  .hw_value = 2,52	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },53	{ .bitrate = 110,54	  .hw_value = 3,55	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },56	{ .bitrate = 60,57	  .hw_value = 5,58	  .flags = 0 },59	{ .bitrate = 90,60	  .hw_value = 6,61	  .flags = 0 },62	{ .bitrate = 120,63	  .hw_value = 7,64	  .flags = 0 },65	{ .bitrate = 180,66	  .hw_value = 8,67	  .flags = 0 },68	{ .bitrate = 240,69	  .hw_value = 9,70	  .flags = 0 },71	{ .bitrate = 360,72	  .hw_value = 10,73	  .flags = 0 },74	{ .bitrate = 480,75	  .hw_value = 11,76	  .flags = 0 },77	{ .bitrate = 540,78	  .hw_value = 12,79	  .flags = 0 },80};81 82static void lbtf_cmd_work(struct work_struct *work)83{84	struct lbtf_private *priv = container_of(work, struct lbtf_private,85					 cmd_work);86 87	lbtf_deb_enter(LBTF_DEB_CMD);88 89	spin_lock_irq(&priv->driver_lock);90	/* command response? */91	if (priv->cmd_response_rxed) {92		priv->cmd_response_rxed = 0;93		spin_unlock_irq(&priv->driver_lock);94		lbtf_process_rx_command(priv);95		spin_lock_irq(&priv->driver_lock);96	}97 98	if (priv->cmd_timed_out && priv->cur_cmd) {99		struct cmd_ctrl_node *cmdnode = priv->cur_cmd;100 101		if (++priv->nr_retries > 10) {102			lbtf_complete_command(priv, cmdnode,103					      -ETIMEDOUT);104			priv->nr_retries = 0;105		} else {106			priv->cur_cmd = NULL;107 108			/* Stick it back at the _top_ of the pending109			 * queue for immediate resubmission */110			list_add(&cmdnode->list, &priv->cmdpendingq);111		}112	}113	priv->cmd_timed_out = 0;114	spin_unlock_irq(&priv->driver_lock);115 116	/* Execute the next command */117	if (!priv->cur_cmd)118		lbtf_execute_next_command(priv);119 120	lbtf_deb_leave(LBTF_DEB_CMD);121}122 123/*124 *  This function handles the timeout of command sending.125 *  It will re-send the same command again.126 */127static void command_timer_fn(struct timer_list *t)128{129	struct lbtf_private *priv = from_timer(priv, t, command_timer);130	unsigned long flags;131	lbtf_deb_enter(LBTF_DEB_CMD);132 133	spin_lock_irqsave(&priv->driver_lock, flags);134 135	if (!priv->cur_cmd) {136		printk(KERN_DEBUG "libertastf: command timer expired; "137				  "no pending command\n");138		goto out;139	}140 141	printk(KERN_DEBUG "libertas: command %x timed out\n",142		le16_to_cpu(priv->cur_cmd->cmdbuf->command));143 144	priv->cmd_timed_out = 1;145	queue_work(lbtf_wq, &priv->cmd_work);146out:147	spin_unlock_irqrestore(&priv->driver_lock, flags);148	lbtf_deb_leave(LBTF_DEB_CMD);149}150 151static int lbtf_init_adapter(struct lbtf_private *priv)152{153	lbtf_deb_enter(LBTF_DEB_MAIN);154	eth_broadcast_addr(priv->current_addr);155	mutex_init(&priv->lock);156 157	priv->vif = NULL;158	timer_setup(&priv->command_timer, command_timer_fn, 0);159 160	INIT_LIST_HEAD(&priv->cmdfreeq);161	INIT_LIST_HEAD(&priv->cmdpendingq);162 163	spin_lock_init(&priv->driver_lock);164 165	/* Allocate the command buffers */166	if (lbtf_allocate_cmd_buffer(priv))167		return -1;168 169	lbtf_deb_leave(LBTF_DEB_MAIN);170	return 0;171}172 173static void lbtf_free_adapter(struct lbtf_private *priv)174{175	lbtf_deb_enter(LBTF_DEB_MAIN);176	lbtf_free_cmd_buffer(priv);177	del_timer(&priv->command_timer);178	lbtf_deb_leave(LBTF_DEB_MAIN);179}180 181static void lbtf_op_tx(struct ieee80211_hw *hw,182		       struct ieee80211_tx_control *control,183		       struct sk_buff *skb)184{185	struct lbtf_private *priv = hw->priv;186 187	priv->skb_to_tx = skb;188	queue_work(lbtf_wq, &priv->tx_work);189	/*190	 * queue will be restarted when we receive transmission feedback if191	 * there are no buffered multicast frames to send192	 */193	ieee80211_stop_queues(priv->hw);194}195 196static void lbtf_tx_work(struct work_struct *work)197{198	struct lbtf_private *priv = container_of(work, struct lbtf_private,199					 tx_work);200	unsigned int len;201	struct ieee80211_tx_info *info;202	struct txpd *txpd;203	struct sk_buff *skb = NULL;204	int err;205 206	lbtf_deb_enter(LBTF_DEB_MACOPS | LBTF_DEB_TX);207 208	if ((priv->vif->type == NL80211_IFTYPE_AP) &&209	    (!skb_queue_empty(&priv->bc_ps_buf)))210		skb = skb_dequeue(&priv->bc_ps_buf);211	else if (priv->skb_to_tx) {212		skb = priv->skb_to_tx;213		priv->skb_to_tx = NULL;214	} else {215		lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);216		return;217	}218 219	len = skb->len;220	info  = IEEE80211_SKB_CB(skb);221	txpd = skb_push(skb, sizeof(struct txpd));222 223	if (priv->surpriseremoved) {224		dev_kfree_skb_any(skb);225		lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);226		return;227	}228 229	memset(txpd, 0, sizeof(struct txpd));230	/* Activate per-packet rate selection */231	txpd->tx_control |= cpu_to_le32(MRVL_PER_PACKET_RATE |232			     ieee80211_get_tx_rate(priv->hw, info)->hw_value);233 234	/* copy destination address from 802.11 header */235	BUILD_BUG_ON(sizeof(txpd->tx_dest_addr) != ETH_ALEN);236	memcpy(&txpd->tx_dest_addr, skb->data + sizeof(struct txpd) + 4,237		ETH_ALEN);238	txpd->tx_packet_length = cpu_to_le16(len);239	txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd));240	lbtf_deb_hex(LBTF_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100));241	BUG_ON(priv->tx_skb);242	spin_lock_irq(&priv->driver_lock);243	priv->tx_skb = skb;244	err = priv->ops->hw_host_to_card(priv, MVMS_DAT, skb->data, skb->len);245	spin_unlock_irq(&priv->driver_lock);246	if (err) {247		dev_kfree_skb_any(skb);248		priv->tx_skb = NULL;249		pr_err("TX error: %d", err);250	}251	lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);252}253 254static int lbtf_op_start(struct ieee80211_hw *hw)255{256	struct lbtf_private *priv = hw->priv;257 258	lbtf_deb_enter(LBTF_DEB_MACOPS);259 260	priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE;261	priv->radioon = RADIO_ON;262	priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON;263	lbtf_set_mac_control(priv);264	lbtf_set_radio_control(priv);265 266	lbtf_deb_leave(LBTF_DEB_MACOPS);267	return 0;268}269 270static void lbtf_op_stop(struct ieee80211_hw *hw, bool suspend)271{272	struct lbtf_private *priv = hw->priv;273	unsigned long flags;274	struct sk_buff *skb;275 276	struct cmd_ctrl_node *cmdnode;277 278	lbtf_deb_enter(LBTF_DEB_MACOPS);279 280	/* Flush pending command nodes */281	spin_lock_irqsave(&priv->driver_lock, flags);282	list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {283		cmdnode->result = -ENOENT;284		cmdnode->cmdwaitqwoken = 1;285		wake_up_interruptible(&cmdnode->cmdwait_q);286	}287 288	spin_unlock_irqrestore(&priv->driver_lock, flags);289	cancel_work_sync(&priv->cmd_work);290	cancel_work_sync(&priv->tx_work);291	while ((skb = skb_dequeue(&priv->bc_ps_buf)))292		dev_kfree_skb_any(skb);293	priv->radioon = RADIO_OFF;294	lbtf_set_radio_control(priv);295 296	lbtf_deb_leave(LBTF_DEB_MACOPS);297}298 299static int lbtf_op_add_interface(struct ieee80211_hw *hw,300			struct ieee80211_vif *vif)301{302	struct lbtf_private *priv = hw->priv;303	lbtf_deb_enter(LBTF_DEB_MACOPS);304	if (priv->vif != NULL)305		return -EOPNOTSUPP;306 307	priv->vif = vif;308	switch (vif->type) {309	case NL80211_IFTYPE_MESH_POINT:310	case NL80211_IFTYPE_AP:311		lbtf_set_mode(priv, LBTF_AP_MODE);312		break;313	case NL80211_IFTYPE_STATION:314		lbtf_set_mode(priv, LBTF_STA_MODE);315		break;316	default:317		priv->vif = NULL;318		return -EOPNOTSUPP;319	}320	lbtf_set_mac_address(priv, (u8 *) vif->addr);321	lbtf_deb_leave(LBTF_DEB_MACOPS);322	return 0;323}324 325static void lbtf_op_remove_interface(struct ieee80211_hw *hw,326			struct ieee80211_vif *vif)327{328	struct lbtf_private *priv = hw->priv;329	lbtf_deb_enter(LBTF_DEB_MACOPS);330 331	if (priv->vif->type == NL80211_IFTYPE_AP ||332	    priv->vif->type == NL80211_IFTYPE_MESH_POINT)333		lbtf_beacon_ctrl(priv, 0, 0);334	lbtf_set_mode(priv, LBTF_PASSIVE_MODE);335	lbtf_set_bssid(priv, 0, NULL);336	priv->vif = NULL;337	lbtf_deb_leave(LBTF_DEB_MACOPS);338}339 340static int lbtf_op_config(struct ieee80211_hw *hw, u32 changed)341{342	struct lbtf_private *priv = hw->priv;343	struct ieee80211_conf *conf = &hw->conf;344	lbtf_deb_enter(LBTF_DEB_MACOPS);345 346	if (conf->chandef.chan->center_freq != priv->cur_freq) {347		priv->cur_freq = conf->chandef.chan->center_freq;348		lbtf_set_channel(priv, conf->chandef.chan->hw_value);349	}350	lbtf_deb_leave(LBTF_DEB_MACOPS);351	return 0;352}353 354static u64 lbtf_op_prepare_multicast(struct ieee80211_hw *hw,355				     struct netdev_hw_addr_list *mc_list)356{357	struct lbtf_private *priv = hw->priv;358	int i;359	struct netdev_hw_addr *ha;360	int mc_count = netdev_hw_addr_list_count(mc_list);361 362	if (!mc_count || mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE)363		return mc_count;364 365	priv->nr_of_multicastmacaddr = mc_count;366	i = 0;367	netdev_hw_addr_list_for_each(ha, mc_list)368		memcpy(&priv->multicastlist[i++], ha->addr, ETH_ALEN);369 370	return mc_count;371}372 373#define SUPPORTED_FIF_FLAGS  FIF_ALLMULTI374static void lbtf_op_configure_filter(struct ieee80211_hw *hw,375			unsigned int changed_flags,376			unsigned int *new_flags,377			u64 multicast)378{379	struct lbtf_private *priv = hw->priv;380	int old_mac_control = priv->mac_control;381 382	lbtf_deb_enter(LBTF_DEB_MACOPS);383 384	changed_flags &= SUPPORTED_FIF_FLAGS;385	*new_flags &= SUPPORTED_FIF_FLAGS;386 387	if (!changed_flags) {388		lbtf_deb_leave(LBTF_DEB_MACOPS);389		return;390	}391 392	priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE;393	if (*new_flags & (FIF_ALLMULTI) ||394	    multicast > MRVDRV_MAX_MULTICAST_LIST_SIZE) {395		priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE;396		priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE;397	} else if (multicast) {398		priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE;399		priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE;400		lbtf_cmd_set_mac_multicast_addr(priv);401	} else {402		priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE |403				       CMD_ACT_MAC_ALL_MULTICAST_ENABLE);404		if (priv->nr_of_multicastmacaddr) {405			priv->nr_of_multicastmacaddr = 0;406			lbtf_cmd_set_mac_multicast_addr(priv);407		}408	}409 410 411	if (priv->mac_control != old_mac_control)412		lbtf_set_mac_control(priv);413 414	lbtf_deb_leave(LBTF_DEB_MACOPS);415}416 417static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw,418			struct ieee80211_vif *vif,419			struct ieee80211_bss_conf *bss_conf,420			u64 changes)421{422	struct lbtf_private *priv = hw->priv;423	struct sk_buff *beacon;424	lbtf_deb_enter(LBTF_DEB_MACOPS);425 426	if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_INT)) {427		switch (priv->vif->type) {428		case NL80211_IFTYPE_AP:429		case NL80211_IFTYPE_MESH_POINT:430			beacon = ieee80211_beacon_get(hw, vif, 0);431			if (beacon) {432				lbtf_beacon_set(priv, beacon);433				kfree_skb(beacon);434				lbtf_beacon_ctrl(priv, 1,435						 bss_conf->beacon_int);436			}437			break;438		default:439			break;440		}441	}442 443	if (changes & BSS_CHANGED_BSSID) {444		bool activate = !is_zero_ether_addr(bss_conf->bssid);445		lbtf_set_bssid(priv, activate, bss_conf->bssid);446	}447 448	if (changes & BSS_CHANGED_ERP_PREAMBLE) {449		if (bss_conf->use_short_preamble)450			priv->preamble = CMD_TYPE_SHORT_PREAMBLE;451		else452			priv->preamble = CMD_TYPE_LONG_PREAMBLE;453		lbtf_set_radio_control(priv);454	}455 456	lbtf_deb_leave(LBTF_DEB_MACOPS);457}458 459static int lbtf_op_get_survey(struct ieee80211_hw *hw, int idx,460				struct survey_info *survey)461{462	struct lbtf_private *priv = hw->priv;463	struct ieee80211_conf *conf = &hw->conf;464 465	if (idx != 0)466		return -ENOENT;467 468	survey->channel = conf->chandef.chan;469	survey->filled = SURVEY_INFO_NOISE_DBM;470	survey->noise = priv->noise;471 472	return 0;473}474 475static const struct ieee80211_ops lbtf_ops = {476	.add_chanctx = ieee80211_emulate_add_chanctx,477	.remove_chanctx = ieee80211_emulate_remove_chanctx,478	.change_chanctx = ieee80211_emulate_change_chanctx,479	.switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx,480	.tx			= lbtf_op_tx,481	.wake_tx_queue		= ieee80211_handle_wake_tx_queue,482	.start			= lbtf_op_start,483	.stop			= lbtf_op_stop,484	.add_interface		= lbtf_op_add_interface,485	.remove_interface	= lbtf_op_remove_interface,486	.config			= lbtf_op_config,487	.prepare_multicast	= lbtf_op_prepare_multicast,488	.configure_filter	= lbtf_op_configure_filter,489	.bss_info_changed	= lbtf_op_bss_info_changed,490	.get_survey		= lbtf_op_get_survey,491};492 493int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb)494{495	struct ieee80211_rx_status stats;496	struct rxpd *prxpd;497	int need_padding;498	struct ieee80211_hdr *hdr;499 500	lbtf_deb_enter(LBTF_DEB_RX);501 502	if (priv->radioon != RADIO_ON) {503		lbtf_deb_rx("rx before we turned on the radio");504		goto done;505	}506 507	prxpd = (struct rxpd *) skb->data;508 509	memset(&stats, 0, sizeof(stats));510	if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK)))511		stats.flag |= RX_FLAG_FAILED_FCS_CRC;512	stats.freq = priv->cur_freq;513	stats.band = NL80211_BAND_2GHZ;514	stats.signal = prxpd->snr - prxpd->nf;515	priv->noise = prxpd->nf;516	/* Marvell rate index has a hole at value 4 */517	if (prxpd->rx_rate > 4)518		--prxpd->rx_rate;519	stats.rate_idx = prxpd->rx_rate;520	skb_pull(skb, sizeof(struct rxpd));521 522	hdr = (struct ieee80211_hdr *)skb->data;523 524	need_padding = ieee80211_is_data_qos(hdr->frame_control);525	need_padding ^= ieee80211_has_a4(hdr->frame_control);526	need_padding ^= ieee80211_is_data_qos(hdr->frame_control) &&527			(*ieee80211_get_qos_ctl(hdr) &528			 IEEE80211_QOS_CTL_A_MSDU_PRESENT);529 530	if (need_padding) {531		memmove(skb->data + 2, skb->data, skb->len);532		skb_reserve(skb, 2);533	}534 535	memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));536 537	lbtf_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n",538	       skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd));539	lbtf_deb_hex(LBTF_DEB_RX, "RX Data", skb->data,540	             min_t(unsigned int, skb->len, 100));541 542	ieee80211_rx_irqsafe(priv->hw, skb);543 544done:545	lbtf_deb_leave(LBTF_DEB_RX);546	return 0;547}548EXPORT_SYMBOL_GPL(lbtf_rx);549 550/*551 * lbtf_add_card: Add and initialize the card.552 *553 *  Returns: pointer to struct lbtf_priv.554 */555struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev,556				   const struct lbtf_ops *ops)557{558	struct ieee80211_hw *hw;559	struct lbtf_private *priv = NULL;560 561	lbtf_deb_enter(LBTF_DEB_MAIN);562 563	hw = ieee80211_alloc_hw(sizeof(struct lbtf_private), &lbtf_ops);564	if (!hw)565		goto done;566 567	priv = hw->priv;568	if (lbtf_init_adapter(priv))569		goto err_init_adapter;570 571	priv->hw = hw;572	priv->card = card;573	priv->ops = ops;574	priv->tx_skb = NULL;575	priv->radioon = RADIO_OFF;576 577	hw->queues = 1;578	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);579	ieee80211_hw_set(hw, SIGNAL_DBM);580	hw->extra_tx_headroom = sizeof(struct txpd);581	memcpy(priv->channels, lbtf_channels, sizeof(lbtf_channels));582	memcpy(priv->rates, lbtf_rates, sizeof(lbtf_rates));583	priv->band.n_bitrates = ARRAY_SIZE(lbtf_rates);584	priv->band.bitrates = priv->rates;585	priv->band.n_channels = ARRAY_SIZE(lbtf_channels);586	priv->band.channels = priv->channels;587	hw->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band;588	hw->wiphy->interface_modes =589		BIT(NL80211_IFTYPE_STATION) |590		BIT(NL80211_IFTYPE_ADHOC);591	skb_queue_head_init(&priv->bc_ps_buf);592 593	wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);594 595	SET_IEEE80211_DEV(hw, dmdev);596 597	INIT_WORK(&priv->cmd_work, lbtf_cmd_work);598	INIT_WORK(&priv->tx_work, lbtf_tx_work);599 600	if (priv->ops->hw_prog_firmware(priv)) {601		lbtf_deb_usbd(dmdev, "Error programming the firmware\n");602		priv->ops->hw_reset_device(priv);603		goto err_init_adapter;604	}605 606	eth_broadcast_addr(priv->current_addr);607	if (lbtf_update_hw_spec(priv))608		goto err_init_adapter;609 610	if (priv->fwrelease < LBTF_FW_VER_MIN ||611	    priv->fwrelease > LBTF_FW_VER_MAX) {612		goto err_init_adapter;613	}614 615	/* The firmware seems to start with the radio enabled. Turn it616	 * off before an actual mac80211 start callback is invoked.617	 */618	lbtf_set_radio_control(priv);619 620	if (ieee80211_register_hw(hw))621		goto err_init_adapter;622 623	dev_info(dmdev, "libertastf: Marvell WLAN 802.11 thinfirm adapter\n");624	goto done;625 626err_init_adapter:627	lbtf_free_adapter(priv);628	ieee80211_free_hw(hw);629	priv = NULL;630 631done:632	lbtf_deb_leave_args(LBTF_DEB_MAIN, "priv %p", priv);633	return priv;634}635EXPORT_SYMBOL_GPL(lbtf_add_card);636 637 638int lbtf_remove_card(struct lbtf_private *priv)639{640	struct ieee80211_hw *hw = priv->hw;641 642	lbtf_deb_enter(LBTF_DEB_MAIN);643 644	priv->surpriseremoved = 1;645	del_timer(&priv->command_timer);646	lbtf_free_adapter(priv);647	priv->hw = NULL;648	ieee80211_unregister_hw(hw);649	ieee80211_free_hw(hw);650 651	lbtf_deb_leave(LBTF_DEB_MAIN);652	return 0;653}654EXPORT_SYMBOL_GPL(lbtf_remove_card);655 656void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail)657{658	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb);659 660	ieee80211_tx_info_clear_status(info);661	/*662	 * Commented out, otherwise we never go beyond 1Mbit/s using mac80211663	 * default pid rc algorithm.664	 *665	 * info->status.retry_count = MRVL_DEFAULT_RETRIES - retrycnt;666	 */667	if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !fail)668		info->flags |= IEEE80211_TX_STAT_ACK;669	skb_pull(priv->tx_skb, sizeof(struct txpd));670	ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb);671	priv->tx_skb = NULL;672	if (!priv->skb_to_tx && skb_queue_empty(&priv->bc_ps_buf))673		ieee80211_wake_queues(priv->hw);674	else675		queue_work(lbtf_wq, &priv->tx_work);676}677EXPORT_SYMBOL_GPL(lbtf_send_tx_feedback);678 679void lbtf_bcn_sent(struct lbtf_private *priv)680{681	struct sk_buff *skb = NULL;682 683	if (priv->vif->type != NL80211_IFTYPE_AP)684		return;685 686	if (skb_queue_empty(&priv->bc_ps_buf)) {687		bool tx_buff_bc = false;688 689		while ((skb = ieee80211_get_buffered_bc(priv->hw, priv->vif))) {690			skb_queue_tail(&priv->bc_ps_buf, skb);691			tx_buff_bc = true;692		}693		if (tx_buff_bc) {694			ieee80211_stop_queues(priv->hw);695			queue_work(lbtf_wq, &priv->tx_work);696		}697	}698 699	skb = ieee80211_beacon_get(priv->hw, priv->vif, 0);700 701	if (skb) {702		lbtf_beacon_set(priv, skb);703		kfree_skb(skb);704	}705}706EXPORT_SYMBOL_GPL(lbtf_bcn_sent);707 708static int __init lbtf_init_module(void)709{710	lbtf_deb_enter(LBTF_DEB_MAIN);711	lbtf_wq = alloc_workqueue("libertastf", WQ_MEM_RECLAIM, 0);712	if (lbtf_wq == NULL) {713		printk(KERN_ERR "libertastf: couldn't create workqueue\n");714		return -ENOMEM;715	}716	lbtf_deb_leave(LBTF_DEB_MAIN);717	return 0;718}719 720static void __exit lbtf_exit_module(void)721{722	lbtf_deb_enter(LBTF_DEB_MAIN);723	destroy_workqueue(lbtf_wq);724	lbtf_deb_leave(LBTF_DEB_MAIN);725}726 727module_init(lbtf_init_module);728module_exit(lbtf_exit_module);729 730MODULE_DESCRIPTION("Libertas WLAN Thinfirm Driver Library");731MODULE_AUTHOR("Cozybit Inc.");732MODULE_LICENSE("GPL");733