Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
  3 *
  4 * Copyright (c) 2010, ST-Ericsson
  5 * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
  6 *
  7 * Based on:
  8 * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
  9 * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
 10 * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
 11 *
 12 * Based on:
 13 * - the islsm (softmac prism54) driver, which is:
 14 *   Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
 15 * - stlc45xx driver
 16 *   Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
 17 *
 18 * This program is free software; you can redistribute it and/or modify
 19 * it under the terms of the GNU General Public License version 2 as
 20 * published by the Free Software Foundation.
 21 */
 22
 23#include <linux/module.h>
 24#include <linux/firmware.h>
 25#include <linux/etherdevice.h>
 26#include <linux/vmalloc.h>
 27#include <linux/random.h>
 28#include <linux/sched.h>
 29#include <net/mac80211.h>
 30
 31#include "cw1200.h"
 32#include "txrx.h"
 33#include "hwbus.h"
 34#include "fwio.h"
 35#include "hwio.h"
 36#include "bh.h"
 37#include "sta.h"
 38#include "scan.h"
 39#include "debug.h"
 40#include "pm.h"
 41
 42MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
 43MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code");
 44MODULE_LICENSE("GPL");
 45MODULE_ALIAS("cw1200_core");
 46
 47/* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */
 48static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00};
 49module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO);
 50MODULE_PARM_DESC(macaddr, "Override platform_data MAC address");
 51
 52static char *cw1200_sdd_path;
 53module_param(cw1200_sdd_path, charp, 0644);
 54MODULE_PARM_DESC(cw1200_sdd_path, "Override platform_data SDD file");
 55static int cw1200_refclk;
 56module_param(cw1200_refclk, int, 0644);
 57MODULE_PARM_DESC(cw1200_refclk, "Override platform_data reference clock");
 58
 59int cw1200_power_mode = wsm_power_mode_quiescent;
 60module_param(cw1200_power_mode, int, 0644);
 61MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode.  0 == active, 1 == doze, 2 == quiescent (default)");
 62
 63#define RATETAB_ENT(_rate, _rateid, _flags)		\
 64	{						\
 65		.bitrate	= (_rate),		\
 66		.hw_value	= (_rateid),		\
 67		.flags		= (_flags),		\
 68	}
 69
 70static struct ieee80211_rate cw1200_rates[] = {
 71	RATETAB_ENT(10,  0,   0),
 72	RATETAB_ENT(20,  1,   0),
 73	RATETAB_ENT(55,  2,   0),
 74	RATETAB_ENT(110, 3,   0),
 75	RATETAB_ENT(60,  6,  0),
 76	RATETAB_ENT(90,  7,  0),
 77	RATETAB_ENT(120, 8,  0),
 78	RATETAB_ENT(180, 9,  0),
 79	RATETAB_ENT(240, 10, 0),
 80	RATETAB_ENT(360, 11, 0),
 81	RATETAB_ENT(480, 12, 0),
 82	RATETAB_ENT(540, 13, 0),
 83};
 84
 85static struct ieee80211_rate cw1200_mcs_rates[] = {
 86	RATETAB_ENT(65,  14, IEEE80211_TX_RC_MCS),
 87	RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS),
 88	RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS),
 89	RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS),
 90	RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS),
 91	RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS),
 92	RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS),
 93	RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS),
 94};
 95
 96#define cw1200_a_rates		(cw1200_rates + 4)
 97#define cw1200_a_rates_size	(ARRAY_SIZE(cw1200_rates) - 4)
 98#define cw1200_g_rates		(cw1200_rates + 0)
 99#define cw1200_g_rates_size	(ARRAY_SIZE(cw1200_rates))
100#define cw1200_n_rates		(cw1200_mcs_rates)
101#define cw1200_n_rates_size	(ARRAY_SIZE(cw1200_mcs_rates))
102
103
104#define CHAN2G(_channel, _freq, _flags) {			\
105	.band			= IEEE80211_BAND_2GHZ,		\
106	.center_freq		= (_freq),			\
107	.hw_value		= (_channel),			\
108	.flags			= (_flags),			\
109	.max_antenna_gain	= 0,				\
110	.max_power		= 30,				\
111}
112
113#define CHAN5G(_channel, _flags) {				\
114	.band			= IEEE80211_BAND_5GHZ,		\
115	.center_freq	= 5000 + (5 * (_channel)),		\
116	.hw_value		= (_channel),			\
117	.flags			= (_flags),			\
118	.max_antenna_gain	= 0,				\
119	.max_power		= 30,				\
120}
121
122static struct ieee80211_channel cw1200_2ghz_chantable[] = {
123	CHAN2G(1, 2412, 0),
124	CHAN2G(2, 2417, 0),
125	CHAN2G(3, 2422, 0),
126	CHAN2G(4, 2427, 0),
127	CHAN2G(5, 2432, 0),
128	CHAN2G(6, 2437, 0),
129	CHAN2G(7, 2442, 0),
130	CHAN2G(8, 2447, 0),
131	CHAN2G(9, 2452, 0),
132	CHAN2G(10, 2457, 0),
133	CHAN2G(11, 2462, 0),
134	CHAN2G(12, 2467, 0),
135	CHAN2G(13, 2472, 0),
136	CHAN2G(14, 2484, 0),
137};
138
139static struct ieee80211_channel cw1200_5ghz_chantable[] = {
140	CHAN5G(34, 0),		CHAN5G(36, 0),
141	CHAN5G(38, 0),		CHAN5G(40, 0),
142	CHAN5G(42, 0),		CHAN5G(44, 0),
143	CHAN5G(46, 0),		CHAN5G(48, 0),
144	CHAN5G(52, 0),		CHAN5G(56, 0),
145	CHAN5G(60, 0),		CHAN5G(64, 0),
146	CHAN5G(100, 0),		CHAN5G(104, 0),
147	CHAN5G(108, 0),		CHAN5G(112, 0),
148	CHAN5G(116, 0),		CHAN5G(120, 0),
149	CHAN5G(124, 0),		CHAN5G(128, 0),
150	CHAN5G(132, 0),		CHAN5G(136, 0),
151	CHAN5G(140, 0),		CHAN5G(149, 0),
152	CHAN5G(153, 0),		CHAN5G(157, 0),
153	CHAN5G(161, 0),		CHAN5G(165, 0),
154	CHAN5G(184, 0),		CHAN5G(188, 0),
155	CHAN5G(192, 0),		CHAN5G(196, 0),
156	CHAN5G(200, 0),		CHAN5G(204, 0),
157	CHAN5G(208, 0),		CHAN5G(212, 0),
158	CHAN5G(216, 0),
159};
160
161static struct ieee80211_supported_band cw1200_band_2ghz = {
162	.channels = cw1200_2ghz_chantable,
163	.n_channels = ARRAY_SIZE(cw1200_2ghz_chantable),
164	.bitrates = cw1200_g_rates,
165	.n_bitrates = cw1200_g_rates_size,
166	.ht_cap = {
167		.cap = IEEE80211_HT_CAP_GRN_FLD |
168			(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
169			IEEE80211_HT_CAP_MAX_AMSDU,
170		.ht_supported = 1,
171		.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
172		.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
173		.mcs = {
174			.rx_mask[0] = 0xFF,
175			.rx_highest = __cpu_to_le16(0x41),
176			.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
177		},
178	},
179};
180
181static struct ieee80211_supported_band cw1200_band_5ghz = {
182	.channels = cw1200_5ghz_chantable,
183	.n_channels = ARRAY_SIZE(cw1200_5ghz_chantable),
184	.bitrates = cw1200_a_rates,
185	.n_bitrates = cw1200_a_rates_size,
186	.ht_cap = {
187		.cap = IEEE80211_HT_CAP_GRN_FLD |
188			(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
189			IEEE80211_HT_CAP_MAX_AMSDU,
190		.ht_supported = 1,
191		.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
192		.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
193		.mcs = {
194			.rx_mask[0] = 0xFF,
195			.rx_highest = __cpu_to_le16(0x41),
196			.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
197		},
198	},
199};
200
201static const unsigned long cw1200_ttl[] = {
202	1 * HZ,	/* VO */
203	2 * HZ,	/* VI */
204	5 * HZ, /* BE */
205	10 * HZ	/* BK */
206};
207
208static const struct ieee80211_ops cw1200_ops = {
209	.start			= cw1200_start,
210	.stop			= cw1200_stop,
211	.add_interface		= cw1200_add_interface,
212	.remove_interface	= cw1200_remove_interface,
213	.change_interface	= cw1200_change_interface,
214	.tx			= cw1200_tx,
215	.hw_scan		= cw1200_hw_scan,
216	.set_tim		= cw1200_set_tim,
217	.sta_notify		= cw1200_sta_notify,
218	.sta_add		= cw1200_sta_add,
219	.sta_remove		= cw1200_sta_remove,
220	.set_key		= cw1200_set_key,
221	.set_rts_threshold	= cw1200_set_rts_threshold,
222	.config			= cw1200_config,
223	.bss_info_changed	= cw1200_bss_info_changed,
224	.prepare_multicast	= cw1200_prepare_multicast,
225	.configure_filter	= cw1200_configure_filter,
226	.conf_tx		= cw1200_conf_tx,
227	.get_stats		= cw1200_get_stats,
228	.ampdu_action		= cw1200_ampdu_action,
229	.flush			= cw1200_flush,
230#ifdef CONFIG_PM
231	.suspend		= cw1200_wow_suspend,
232	.resume			= cw1200_wow_resume,
233#endif
234	/* Intentionally not offloaded:					*/
235	/*.channel_switch	= cw1200_channel_switch,		*/
236	/*.remain_on_channel	= cw1200_remain_on_channel,		*/
237	/*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel,	*/
238};
239
240static int cw1200_ba_rx_tids = -1;
241static int cw1200_ba_tx_tids = -1;
242module_param(cw1200_ba_rx_tids, int, 0644);
243module_param(cw1200_ba_tx_tids, int, 0644);
244MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs");
245MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs");
246
247#ifdef CONFIG_PM
248static const struct wiphy_wowlan_support cw1200_wowlan_support = {
249	/* Support only for limited wowlan functionalities */
250	.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
251};
252#endif
253
254
255static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr,
256						const bool have_5ghz)
257{
258	int i, band;
259	struct ieee80211_hw *hw;
260	struct cw1200_common *priv;
261
262	hw = ieee80211_alloc_hw(sizeof(struct cw1200_common), &cw1200_ops);
263	if (!hw)
264		return NULL;
265
266	priv = hw->priv;
267	priv->hw = hw;
268	priv->hw_type = -1;
269	priv->mode = NL80211_IFTYPE_UNSPECIFIED;
270	priv->rates = cw1200_rates; /* TODO: fetch from FW */
271	priv->mcs_rates = cw1200_n_rates;
272	if (cw1200_ba_rx_tids != -1)
273		priv->ba_rx_tid_mask = cw1200_ba_rx_tids;
274	else
275		priv->ba_rx_tid_mask = 0xFF; /* Enable RX BLKACK for all TIDs */
276	if (cw1200_ba_tx_tids != -1)
277		priv->ba_tx_tid_mask = cw1200_ba_tx_tids;
278	else
279		priv->ba_tx_tid_mask = 0xff; /* Enable TX BLKACK for all TIDs */
280
281	ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC);
282	ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
283	ieee80211_hw_set(hw, AMPDU_AGGREGATION);
284	ieee80211_hw_set(hw, CONNECTION_MONITOR);
285	ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
286	ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
287	ieee80211_hw_set(hw, SIGNAL_DBM);
288	ieee80211_hw_set(hw, SUPPORTS_PS);
289
290	hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
291					  BIT(NL80211_IFTYPE_ADHOC) |
292					  BIT(NL80211_IFTYPE_AP) |
293					  BIT(NL80211_IFTYPE_MESH_POINT) |
294					  BIT(NL80211_IFTYPE_P2P_CLIENT) |
295					  BIT(NL80211_IFTYPE_P2P_GO);
296
297#ifdef CONFIG_PM
298	hw->wiphy->wowlan = &cw1200_wowlan_support;
299#endif
300
301	hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
302
303	hw->queues = 4;
304
305	priv->rts_threshold = -1;
306
307	hw->max_rates = 8;
308	hw->max_rate_tries = 15;
309	hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM +
310		8;  /* TKIP IV */
311
312	hw->sta_data_size = sizeof(struct cw1200_sta_priv);
313
314	hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz;
315	if (have_5ghz)
316		hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz;
317
318	/* Channel params have to be cleared before registering wiphy again */
319	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
320		struct ieee80211_supported_band *sband = hw->wiphy->bands[band];
321		if (!sband)
322			continue;
323		for (i = 0; i < sband->n_channels; i++) {
324			sband->channels[i].flags = 0;
325			sband->channels[i].max_antenna_gain = 0;
326			sband->channels[i].max_power = 30;
327		}
328	}
329
330	hw->wiphy->max_scan_ssids = 2;
331	hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
332
333	if (macaddr)
334		SET_IEEE80211_PERM_ADDR(hw, (u8 *)macaddr);
335	else
336		SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template);
337
338	/* Fix up mac address if necessary */
339	if (hw->wiphy->perm_addr[3] == 0 &&
340	    hw->wiphy->perm_addr[4] == 0 &&
341	    hw->wiphy->perm_addr[5] == 0) {
342		get_random_bytes(&hw->wiphy->perm_addr[3], 3);
343	}
344
345	mutex_init(&priv->wsm_cmd_mux);
346	mutex_init(&priv->conf_mutex);
347	priv->workqueue = create_singlethread_workqueue("cw1200_wq");
348	sema_init(&priv->scan.lock, 1);
349	INIT_WORK(&priv->scan.work, cw1200_scan_work);
350	INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work);
351	INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout);
352	INIT_DELAYED_WORK(&priv->clear_recent_scan_work,
353			  cw1200_clear_recent_scan_work);
354	INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout);
355	INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work);
356	INIT_WORK(&priv->join_complete_work, cw1200_join_complete_work);
357	INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work);
358	INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work);
359	spin_lock_init(&priv->event_queue_lock);
360	INIT_LIST_HEAD(&priv->event_queue);
361	INIT_WORK(&priv->event_handler, cw1200_event_handler);
362	INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work);
363	INIT_WORK(&priv->bss_params_work, cw1200_bss_params_work);
364	spin_lock_init(&priv->bss_loss_lock);
365	spin_lock_init(&priv->ps_state_lock);
366	INIT_WORK(&priv->set_cts_work, cw1200_set_cts_work);
367	INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work);
368	INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work);
369	INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work);
370	INIT_WORK(&priv->link_id_work, cw1200_link_id_work);
371	INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work);
372	INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset);
373	INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work);
374	INIT_WORK(&priv->set_beacon_wakeup_period_work,
375		  cw1200_set_beacon_wakeup_period_work);
376	setup_timer(&priv->mcast_timeout, cw1200_mcast_timeout,
377		    (unsigned long)priv);
378
379	if (cw1200_queue_stats_init(&priv->tx_queue_stats,
380				    CW1200_LINK_ID_MAX,
381				    cw1200_skb_dtor,
382				    priv)) {
383		ieee80211_free_hw(hw);
384		return NULL;
385	}
386
387	for (i = 0; i < 4; ++i) {
388		if (cw1200_queue_init(&priv->tx_queue[i],
389				      &priv->tx_queue_stats, i, 16,
390				      cw1200_ttl[i])) {
391			for (; i > 0; i--)
392				cw1200_queue_deinit(&priv->tx_queue[i - 1]);
393			cw1200_queue_stats_deinit(&priv->tx_queue_stats);
394			ieee80211_free_hw(hw);
395			return NULL;
396		}
397	}
398
399	init_waitqueue_head(&priv->channel_switch_done);
400	init_waitqueue_head(&priv->wsm_cmd_wq);
401	init_waitqueue_head(&priv->wsm_startup_done);
402	init_waitqueue_head(&priv->ps_mode_switch_done);
403	wsm_buf_init(&priv->wsm_cmd_buf);
404	spin_lock_init(&priv->wsm_cmd.lock);
405	priv->wsm_cmd.done = 1;
406	tx_policy_init(priv);
407
408	return hw;
409}
410
411static int cw1200_register_common(struct ieee80211_hw *dev)
412{
413	struct cw1200_common *priv = dev->priv;
414	int err;
415
416#ifdef CONFIG_PM
417	err = cw1200_pm_init(&priv->pm_state, priv);
418	if (err) {
419		pr_err("Cannot init PM. (%d).\n",
420		       err);
421		return err;
422	}
423#endif
424
425	err = ieee80211_register_hw(dev);
426	if (err) {
427		pr_err("Cannot register device (%d).\n",
428		       err);
429#ifdef CONFIG_PM
430		cw1200_pm_deinit(&priv->pm_state);
431#endif
432		return err;
433	}
434
435	cw1200_debug_init(priv);
436
437	pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy));
438	return 0;
439}
440
441static void cw1200_free_common(struct ieee80211_hw *dev)
442{
443	ieee80211_free_hw(dev);
444}
445
446static void cw1200_unregister_common(struct ieee80211_hw *dev)
447{
448	struct cw1200_common *priv = dev->priv;
449	int i;
450
451	ieee80211_unregister_hw(dev);
452
453	del_timer_sync(&priv->mcast_timeout);
454	cw1200_unregister_bh(priv);
455
456	cw1200_debug_release(priv);
457
458	mutex_destroy(&priv->conf_mutex);
459
460	wsm_buf_deinit(&priv->wsm_cmd_buf);
461
462	destroy_workqueue(priv->workqueue);
463	priv->workqueue = NULL;
464
465	if (priv->sdd) {
466		release_firmware(priv->sdd);
467		priv->sdd = NULL;
468	}
469
470	for (i = 0; i < 4; ++i)
471		cw1200_queue_deinit(&priv->tx_queue[i]);
472
473	cw1200_queue_stats_deinit(&priv->tx_queue_stats);
474#ifdef CONFIG_PM
475	cw1200_pm_deinit(&priv->pm_state);
476#endif
477}
478
479/* Clock is in KHz */
480u32 cw1200_dpll_from_clk(u16 clk_khz)
481{
482	switch (clk_khz) {
483	case 0x32C8: /* 13000 KHz */
484		return 0x1D89D241;
485	case 0x3E80: /* 16000 KHz */
486		return 0x000001E1;
487	case 0x41A0: /* 16800 KHz */
488		return 0x124931C1;
489	case 0x4B00: /* 19200 KHz */
490		return 0x00000191;
491	case 0x5DC0: /* 24000 KHz */
492		return 0x00000141;
493	case 0x6590: /* 26000 KHz */
494		return 0x0EC4F121;
495	case 0x8340: /* 33600 KHz */
496		return 0x092490E1;
497	case 0x9600: /* 38400 KHz */
498		return 0x100010C1;
499	case 0x9C40: /* 40000 KHz */
500		return 0x000000C1;
501	case 0xBB80: /* 48000 KHz */
502		return 0x000000A1;
503	case 0xCB20: /* 52000 KHz */
504		return 0x07627091;
505	default:
506		pr_err("Unknown Refclk freq (0x%04x), using 26000KHz\n",
507		       clk_khz);
508		return 0x0EC4F121;
509	}
510}
511
512int cw1200_core_probe(const struct hwbus_ops *hwbus_ops,
513		      struct hwbus_priv *hwbus,
514		      struct device *pdev,
515		      struct cw1200_common **core,
516		      int ref_clk, const u8 *macaddr,
517		      const char *sdd_path, bool have_5ghz)
518{
519	int err = -EINVAL;
520	struct ieee80211_hw *dev;
521	struct cw1200_common *priv;
522	struct wsm_operational_mode mode = {
523		.power_mode = cw1200_power_mode,
524		.disable_more_flag_usage = true,
525	};
526
527	dev = cw1200_init_common(macaddr, have_5ghz);
528	if (!dev)
529		goto err;
530
531	priv = dev->priv;
532	priv->hw_refclk = ref_clk;
533	if (cw1200_refclk)
534		priv->hw_refclk = cw1200_refclk;
535
536	priv->sdd_path = (char *)sdd_path;
537	if (cw1200_sdd_path)
538		priv->sdd_path = cw1200_sdd_path;
539
540	priv->hwbus_ops = hwbus_ops;
541	priv->hwbus_priv = hwbus;
542	priv->pdev = pdev;
543	SET_IEEE80211_DEV(priv->hw, pdev);
544
545	/* Pass struct cw1200_common back up */
546	*core = priv;
547
548	err = cw1200_register_bh(priv);
549	if (err)
550		goto err1;
551
552	err = cw1200_load_firmware(priv);
553	if (err)
554		goto err2;
555
556	if (wait_event_interruptible_timeout(priv->wsm_startup_done,
557					     priv->firmware_ready,
558					     3*HZ) <= 0) {
559		/* TODO: Need to find how to reset device
560		   in QUEUE mode properly.
561		*/
562		pr_err("Timeout waiting on device startup\n");
563		err = -ETIMEDOUT;
564		goto err2;
565	}
566
567	/* Set low-power mode. */
568	wsm_set_operational_mode(priv, &mode);
569
570	/* Enable multi-TX confirmation */
571	wsm_use_multi_tx_conf(priv, true);
572
573	err = cw1200_register_common(dev);
574	if (err)
575		goto err2;
576
577	return err;
578
579err2:
580	cw1200_unregister_bh(priv);
581err1:
582	cw1200_free_common(dev);
583err:
584	*core = NULL;
585	return err;
586}
587EXPORT_SYMBOL_GPL(cw1200_core_probe);
588
589void cw1200_core_release(struct cw1200_common *self)
590{
591	/* Disable device interrupts */
592	self->hwbus_ops->lock(self->hwbus_priv);
593	__cw1200_irq_enable(self, 0);
594	self->hwbus_ops->unlock(self->hwbus_priv);
595
596	/* And then clean up */
597	cw1200_unregister_common(self->hw);
598	cw1200_free_common(self->hw);
599	return;
600}
601EXPORT_SYMBOL_GPL(cw1200_core_release);