0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020 #include <linux/module.h>
0021 #include <linux/firmware.h>
0022 #include <linux/etherdevice.h>
0023 #include <linux/vmalloc.h>
0024 #include <linux/random.h>
0025 #include <linux/sched.h>
0026 #include <net/mac80211.h>
0027
0028 #include "cw1200.h"
0029 #include "txrx.h"
0030 #include "hwbus.h"
0031 #include "fwio.h"
0032 #include "hwio.h"
0033 #include "bh.h"
0034 #include "sta.h"
0035 #include "scan.h"
0036 #include "debug.h"
0037 #include "pm.h"
0038
0039 MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
0040 MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code");
0041 MODULE_LICENSE("GPL");
0042 MODULE_ALIAS("cw1200_core");
0043
0044
0045 static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00};
0046 module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, 0444);
0047 MODULE_PARM_DESC(macaddr, "Override platform_data MAC address");
0048
0049 static char *cw1200_sdd_path;
0050 module_param(cw1200_sdd_path, charp, 0644);
0051 MODULE_PARM_DESC(cw1200_sdd_path, "Override platform_data SDD file");
0052 static int cw1200_refclk;
0053 module_param(cw1200_refclk, int, 0644);
0054 MODULE_PARM_DESC(cw1200_refclk, "Override platform_data reference clock");
0055
0056 int cw1200_power_mode = wsm_power_mode_quiescent;
0057 module_param(cw1200_power_mode, int, 0644);
0058 MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode. 0 == active, 1 == doze, 2 == quiescent (default)");
0059
0060 #define RATETAB_ENT(_rate, _rateid, _flags) \
0061 { \
0062 .bitrate = (_rate), \
0063 .hw_value = (_rateid), \
0064 .flags = (_flags), \
0065 }
0066
0067 static struct ieee80211_rate cw1200_rates[] = {
0068 RATETAB_ENT(10, 0, 0),
0069 RATETAB_ENT(20, 1, 0),
0070 RATETAB_ENT(55, 2, 0),
0071 RATETAB_ENT(110, 3, 0),
0072 RATETAB_ENT(60, 6, 0),
0073 RATETAB_ENT(90, 7, 0),
0074 RATETAB_ENT(120, 8, 0),
0075 RATETAB_ENT(180, 9, 0),
0076 RATETAB_ENT(240, 10, 0),
0077 RATETAB_ENT(360, 11, 0),
0078 RATETAB_ENT(480, 12, 0),
0079 RATETAB_ENT(540, 13, 0),
0080 };
0081
0082 static struct ieee80211_rate cw1200_mcs_rates[] = {
0083 RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS),
0084 RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS),
0085 RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS),
0086 RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS),
0087 RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS),
0088 RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS),
0089 RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS),
0090 RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS),
0091 };
0092
0093 #define cw1200_a_rates (cw1200_rates + 4)
0094 #define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4)
0095 #define cw1200_g_rates (cw1200_rates + 0)
0096 #define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates))
0097 #define cw1200_n_rates (cw1200_mcs_rates)
0098 #define cw1200_n_rates_size (ARRAY_SIZE(cw1200_mcs_rates))
0099
0100
0101 #define CHAN2G(_channel, _freq, _flags) { \
0102 .band = NL80211_BAND_2GHZ, \
0103 .center_freq = (_freq), \
0104 .hw_value = (_channel), \
0105 .flags = (_flags), \
0106 .max_antenna_gain = 0, \
0107 .max_power = 30, \
0108 }
0109
0110 #define CHAN5G(_channel, _flags) { \
0111 .band = NL80211_BAND_5GHZ, \
0112 .center_freq = 5000 + (5 * (_channel)), \
0113 .hw_value = (_channel), \
0114 .flags = (_flags), \
0115 .max_antenna_gain = 0, \
0116 .max_power = 30, \
0117 }
0118
0119 static struct ieee80211_channel cw1200_2ghz_chantable[] = {
0120 CHAN2G(1, 2412, 0),
0121 CHAN2G(2, 2417, 0),
0122 CHAN2G(3, 2422, 0),
0123 CHAN2G(4, 2427, 0),
0124 CHAN2G(5, 2432, 0),
0125 CHAN2G(6, 2437, 0),
0126 CHAN2G(7, 2442, 0),
0127 CHAN2G(8, 2447, 0),
0128 CHAN2G(9, 2452, 0),
0129 CHAN2G(10, 2457, 0),
0130 CHAN2G(11, 2462, 0),
0131 CHAN2G(12, 2467, 0),
0132 CHAN2G(13, 2472, 0),
0133 CHAN2G(14, 2484, 0),
0134 };
0135
0136 static struct ieee80211_channel cw1200_5ghz_chantable[] = {
0137 CHAN5G(34, 0), CHAN5G(36, 0),
0138 CHAN5G(38, 0), CHAN5G(40, 0),
0139 CHAN5G(42, 0), CHAN5G(44, 0),
0140 CHAN5G(46, 0), CHAN5G(48, 0),
0141 CHAN5G(52, 0), CHAN5G(56, 0),
0142 CHAN5G(60, 0), CHAN5G(64, 0),
0143 CHAN5G(100, 0), CHAN5G(104, 0),
0144 CHAN5G(108, 0), CHAN5G(112, 0),
0145 CHAN5G(116, 0), CHAN5G(120, 0),
0146 CHAN5G(124, 0), CHAN5G(128, 0),
0147 CHAN5G(132, 0), CHAN5G(136, 0),
0148 CHAN5G(140, 0), CHAN5G(149, 0),
0149 CHAN5G(153, 0), CHAN5G(157, 0),
0150 CHAN5G(161, 0), CHAN5G(165, 0),
0151 CHAN5G(184, 0), CHAN5G(188, 0),
0152 CHAN5G(192, 0), CHAN5G(196, 0),
0153 CHAN5G(200, 0), CHAN5G(204, 0),
0154 CHAN5G(208, 0), CHAN5G(212, 0),
0155 CHAN5G(216, 0),
0156 };
0157
0158 static struct ieee80211_supported_band cw1200_band_2ghz = {
0159 .channels = cw1200_2ghz_chantable,
0160 .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable),
0161 .bitrates = cw1200_g_rates,
0162 .n_bitrates = cw1200_g_rates_size,
0163 .ht_cap = {
0164 .cap = IEEE80211_HT_CAP_GRN_FLD |
0165 (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
0166 IEEE80211_HT_CAP_MAX_AMSDU,
0167 .ht_supported = 1,
0168 .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
0169 .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
0170 .mcs = {
0171 .rx_mask[0] = 0xFF,
0172 .rx_highest = __cpu_to_le16(0x41),
0173 .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
0174 },
0175 },
0176 };
0177
0178 static struct ieee80211_supported_band cw1200_band_5ghz = {
0179 .channels = cw1200_5ghz_chantable,
0180 .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable),
0181 .bitrates = cw1200_a_rates,
0182 .n_bitrates = cw1200_a_rates_size,
0183 .ht_cap = {
0184 .cap = IEEE80211_HT_CAP_GRN_FLD |
0185 (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
0186 IEEE80211_HT_CAP_MAX_AMSDU,
0187 .ht_supported = 1,
0188 .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
0189 .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
0190 .mcs = {
0191 .rx_mask[0] = 0xFF,
0192 .rx_highest = __cpu_to_le16(0x41),
0193 .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
0194 },
0195 },
0196 };
0197
0198 static const unsigned long cw1200_ttl[] = {
0199 1 * HZ,
0200 2 * HZ,
0201 5 * HZ,
0202 10 * HZ
0203 };
0204
0205 static const struct ieee80211_ops cw1200_ops = {
0206 .start = cw1200_start,
0207 .stop = cw1200_stop,
0208 .add_interface = cw1200_add_interface,
0209 .remove_interface = cw1200_remove_interface,
0210 .change_interface = cw1200_change_interface,
0211 .tx = cw1200_tx,
0212 .hw_scan = cw1200_hw_scan,
0213 .set_tim = cw1200_set_tim,
0214 .sta_notify = cw1200_sta_notify,
0215 .sta_add = cw1200_sta_add,
0216 .sta_remove = cw1200_sta_remove,
0217 .set_key = cw1200_set_key,
0218 .set_rts_threshold = cw1200_set_rts_threshold,
0219 .config = cw1200_config,
0220 .bss_info_changed = cw1200_bss_info_changed,
0221 .prepare_multicast = cw1200_prepare_multicast,
0222 .configure_filter = cw1200_configure_filter,
0223 .conf_tx = cw1200_conf_tx,
0224 .get_stats = cw1200_get_stats,
0225 .ampdu_action = cw1200_ampdu_action,
0226 .flush = cw1200_flush,
0227 #ifdef CONFIG_PM
0228 .suspend = cw1200_wow_suspend,
0229 .resume = cw1200_wow_resume,
0230 #endif
0231
0232
0233
0234
0235 };
0236
0237 static int cw1200_ba_rx_tids = -1;
0238 static int cw1200_ba_tx_tids = -1;
0239 module_param(cw1200_ba_rx_tids, int, 0644);
0240 module_param(cw1200_ba_tx_tids, int, 0644);
0241 MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs");
0242 MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs");
0243
0244 #ifdef CONFIG_PM
0245 static const struct wiphy_wowlan_support cw1200_wowlan_support = {
0246
0247 .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
0248 };
0249 #endif
0250
0251
0252 static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr,
0253 const bool have_5ghz)
0254 {
0255 int i, band;
0256 struct ieee80211_hw *hw;
0257 struct cw1200_common *priv;
0258
0259 hw = ieee80211_alloc_hw(sizeof(struct cw1200_common), &cw1200_ops);
0260 if (!hw)
0261 return NULL;
0262
0263 priv = hw->priv;
0264 priv->hw = hw;
0265 priv->hw_type = -1;
0266 priv->mode = NL80211_IFTYPE_UNSPECIFIED;
0267 priv->rates = cw1200_rates;
0268 priv->mcs_rates = cw1200_n_rates;
0269 if (cw1200_ba_rx_tids != -1)
0270 priv->ba_rx_tid_mask = cw1200_ba_rx_tids;
0271 else
0272 priv->ba_rx_tid_mask = 0xFF;
0273 if (cw1200_ba_tx_tids != -1)
0274 priv->ba_tx_tid_mask = cw1200_ba_tx_tids;
0275 else
0276 priv->ba_tx_tid_mask = 0xff;
0277
0278 ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC);
0279 ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
0280 ieee80211_hw_set(hw, AMPDU_AGGREGATION);
0281 ieee80211_hw_set(hw, CONNECTION_MONITOR);
0282 ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
0283 ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
0284 ieee80211_hw_set(hw, SIGNAL_DBM);
0285 ieee80211_hw_set(hw, SUPPORTS_PS);
0286
0287 hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
0288 BIT(NL80211_IFTYPE_ADHOC) |
0289 BIT(NL80211_IFTYPE_AP) |
0290 BIT(NL80211_IFTYPE_MESH_POINT) |
0291 BIT(NL80211_IFTYPE_P2P_CLIENT) |
0292 BIT(NL80211_IFTYPE_P2P_GO);
0293
0294 #ifdef CONFIG_PM
0295 hw->wiphy->wowlan = &cw1200_wowlan_support;
0296 #endif
0297
0298 hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
0299
0300 hw->queues = 4;
0301
0302 priv->rts_threshold = -1;
0303
0304 hw->max_rates = 8;
0305 hw->max_rate_tries = 15;
0306 hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM +
0307 8;
0308
0309 hw->sta_data_size = sizeof(struct cw1200_sta_priv);
0310
0311 hw->wiphy->bands[NL80211_BAND_2GHZ] = &cw1200_band_2ghz;
0312 if (have_5ghz)
0313 hw->wiphy->bands[NL80211_BAND_5GHZ] = &cw1200_band_5ghz;
0314
0315
0316 for (band = 0; band < NUM_NL80211_BANDS; band++) {
0317 struct ieee80211_supported_band *sband = hw->wiphy->bands[band];
0318 if (!sband)
0319 continue;
0320 for (i = 0; i < sband->n_channels; i++) {
0321 sband->channels[i].flags = 0;
0322 sband->channels[i].max_antenna_gain = 0;
0323 sband->channels[i].max_power = 30;
0324 }
0325 }
0326
0327 hw->wiphy->max_scan_ssids = 2;
0328 hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
0329
0330 if (macaddr)
0331 SET_IEEE80211_PERM_ADDR(hw, (u8 *)macaddr);
0332 else
0333 SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template);
0334
0335
0336 if (hw->wiphy->perm_addr[3] == 0 &&
0337 hw->wiphy->perm_addr[4] == 0 &&
0338 hw->wiphy->perm_addr[5] == 0) {
0339 get_random_bytes(&hw->wiphy->perm_addr[3], 3);
0340 }
0341
0342 mutex_init(&priv->wsm_cmd_mux);
0343 mutex_init(&priv->conf_mutex);
0344 priv->workqueue = create_singlethread_workqueue("cw1200_wq");
0345 if (!priv->workqueue) {
0346 ieee80211_free_hw(hw);
0347 return NULL;
0348 }
0349
0350 sema_init(&priv->scan.lock, 1);
0351 INIT_WORK(&priv->scan.work, cw1200_scan_work);
0352 INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work);
0353 INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout);
0354 INIT_DELAYED_WORK(&priv->clear_recent_scan_work,
0355 cw1200_clear_recent_scan_work);
0356 INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout);
0357 INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work);
0358 INIT_WORK(&priv->join_complete_work, cw1200_join_complete_work);
0359 INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work);
0360 INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work);
0361 spin_lock_init(&priv->event_queue_lock);
0362 INIT_LIST_HEAD(&priv->event_queue);
0363 INIT_WORK(&priv->event_handler, cw1200_event_handler);
0364 INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work);
0365 INIT_WORK(&priv->bss_params_work, cw1200_bss_params_work);
0366 spin_lock_init(&priv->bss_loss_lock);
0367 spin_lock_init(&priv->ps_state_lock);
0368 INIT_WORK(&priv->set_cts_work, cw1200_set_cts_work);
0369 INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work);
0370 INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work);
0371 INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work);
0372 INIT_WORK(&priv->link_id_work, cw1200_link_id_work);
0373 INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work);
0374 INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset);
0375 INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work);
0376 INIT_WORK(&priv->set_beacon_wakeup_period_work,
0377 cw1200_set_beacon_wakeup_period_work);
0378 timer_setup(&priv->mcast_timeout, cw1200_mcast_timeout, 0);
0379
0380 if (cw1200_queue_stats_init(&priv->tx_queue_stats,
0381 CW1200_LINK_ID_MAX,
0382 cw1200_skb_dtor,
0383 priv)) {
0384 destroy_workqueue(priv->workqueue);
0385 ieee80211_free_hw(hw);
0386 return NULL;
0387 }
0388
0389 for (i = 0; i < 4; ++i) {
0390 if (cw1200_queue_init(&priv->tx_queue[i],
0391 &priv->tx_queue_stats, i, 16,
0392 cw1200_ttl[i])) {
0393 for (; i > 0; i--)
0394 cw1200_queue_deinit(&priv->tx_queue[i - 1]);
0395 cw1200_queue_stats_deinit(&priv->tx_queue_stats);
0396 destroy_workqueue(priv->workqueue);
0397 ieee80211_free_hw(hw);
0398 return NULL;
0399 }
0400 }
0401
0402 init_waitqueue_head(&priv->channel_switch_done);
0403 init_waitqueue_head(&priv->wsm_cmd_wq);
0404 init_waitqueue_head(&priv->wsm_startup_done);
0405 init_waitqueue_head(&priv->ps_mode_switch_done);
0406 wsm_buf_init(&priv->wsm_cmd_buf);
0407 spin_lock_init(&priv->wsm_cmd.lock);
0408 priv->wsm_cmd.done = 1;
0409 tx_policy_init(priv);
0410
0411 return hw;
0412 }
0413
0414 static int cw1200_register_common(struct ieee80211_hw *dev)
0415 {
0416 struct cw1200_common *priv = dev->priv;
0417 int err;
0418
0419 #ifdef CONFIG_PM
0420 err = cw1200_pm_init(&priv->pm_state, priv);
0421 if (err) {
0422 pr_err("Cannot init PM. (%d).\n",
0423 err);
0424 return err;
0425 }
0426 #endif
0427
0428 err = ieee80211_register_hw(dev);
0429 if (err) {
0430 pr_err("Cannot register device (%d).\n",
0431 err);
0432 #ifdef CONFIG_PM
0433 cw1200_pm_deinit(&priv->pm_state);
0434 #endif
0435 return err;
0436 }
0437
0438 cw1200_debug_init(priv);
0439
0440 pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy));
0441 return 0;
0442 }
0443
0444 static void cw1200_free_common(struct ieee80211_hw *dev)
0445 {
0446 ieee80211_free_hw(dev);
0447 }
0448
0449 static void cw1200_unregister_common(struct ieee80211_hw *dev)
0450 {
0451 struct cw1200_common *priv = dev->priv;
0452 int i;
0453
0454 ieee80211_unregister_hw(dev);
0455
0456 del_timer_sync(&priv->mcast_timeout);
0457 cw1200_unregister_bh(priv);
0458
0459 cw1200_debug_release(priv);
0460
0461 mutex_destroy(&priv->conf_mutex);
0462
0463 wsm_buf_deinit(&priv->wsm_cmd_buf);
0464
0465 destroy_workqueue(priv->workqueue);
0466 priv->workqueue = NULL;
0467
0468 if (priv->sdd) {
0469 release_firmware(priv->sdd);
0470 priv->sdd = NULL;
0471 }
0472
0473 for (i = 0; i < 4; ++i)
0474 cw1200_queue_deinit(&priv->tx_queue[i]);
0475
0476 cw1200_queue_stats_deinit(&priv->tx_queue_stats);
0477 #ifdef CONFIG_PM
0478 cw1200_pm_deinit(&priv->pm_state);
0479 #endif
0480 }
0481
0482
0483 u32 cw1200_dpll_from_clk(u16 clk_khz)
0484 {
0485 switch (clk_khz) {
0486 case 0x32C8:
0487 return 0x1D89D241;
0488 case 0x3E80:
0489 return 0x000001E1;
0490 case 0x41A0:
0491 return 0x124931C1;
0492 case 0x4B00:
0493 return 0x00000191;
0494 case 0x5DC0:
0495 return 0x00000141;
0496 case 0x6590:
0497 return 0x0EC4F121;
0498 case 0x8340:
0499 return 0x092490E1;
0500 case 0x9600:
0501 return 0x100010C1;
0502 case 0x9C40:
0503 return 0x000000C1;
0504 case 0xBB80:
0505 return 0x000000A1;
0506 case 0xCB20:
0507 return 0x07627091;
0508 default:
0509 pr_err("Unknown Refclk freq (0x%04x), using 26000KHz\n",
0510 clk_khz);
0511 return 0x0EC4F121;
0512 }
0513 }
0514
0515 int cw1200_core_probe(const struct hwbus_ops *hwbus_ops,
0516 struct hwbus_priv *hwbus,
0517 struct device *pdev,
0518 struct cw1200_common **core,
0519 int ref_clk, const u8 *macaddr,
0520 const char *sdd_path, bool have_5ghz)
0521 {
0522 int err = -EINVAL;
0523 struct ieee80211_hw *dev;
0524 struct cw1200_common *priv;
0525 struct wsm_operational_mode mode = {
0526 .power_mode = cw1200_power_mode,
0527 .disable_more_flag_usage = true,
0528 };
0529
0530 dev = cw1200_init_common(macaddr, have_5ghz);
0531 if (!dev)
0532 goto err;
0533
0534 priv = dev->priv;
0535 priv->hw_refclk = ref_clk;
0536 if (cw1200_refclk)
0537 priv->hw_refclk = cw1200_refclk;
0538
0539 priv->sdd_path = (char *)sdd_path;
0540 if (cw1200_sdd_path)
0541 priv->sdd_path = cw1200_sdd_path;
0542
0543 priv->hwbus_ops = hwbus_ops;
0544 priv->hwbus_priv = hwbus;
0545 priv->pdev = pdev;
0546 SET_IEEE80211_DEV(priv->hw, pdev);
0547
0548
0549 *core = priv;
0550
0551 err = cw1200_register_bh(priv);
0552 if (err)
0553 goto err1;
0554
0555 err = cw1200_load_firmware(priv);
0556 if (err)
0557 goto err2;
0558
0559 if (wait_event_interruptible_timeout(priv->wsm_startup_done,
0560 priv->firmware_ready,
0561 3*HZ) <= 0) {
0562
0563
0564
0565 pr_err("Timeout waiting on device startup\n");
0566 err = -ETIMEDOUT;
0567 goto err2;
0568 }
0569
0570
0571 wsm_set_operational_mode(priv, &mode);
0572
0573
0574 wsm_use_multi_tx_conf(priv, true);
0575
0576 err = cw1200_register_common(dev);
0577 if (err)
0578 goto err2;
0579
0580 return err;
0581
0582 err2:
0583 cw1200_unregister_bh(priv);
0584 err1:
0585 cw1200_free_common(dev);
0586 err:
0587 *core = NULL;
0588 return err;
0589 }
0590 EXPORT_SYMBOL_GPL(cw1200_core_probe);
0591
0592 void cw1200_core_release(struct cw1200_common *self)
0593 {
0594
0595 self->hwbus_ops->lock(self->hwbus_priv);
0596 __cw1200_irq_enable(self, 0);
0597 self->hwbus_ops->unlock(self->hwbus_priv);
0598
0599
0600 cw1200_unregister_common(self->hw);
0601 cw1200_free_common(self->hw);
0602 return;
0603 }
0604 EXPORT_SYMBOL_GPL(cw1200_core_release);