1 | /* |
2 | * Copyright (c) 2004-2011 Atheros Communications Inc. |
3 | * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. |
4 | * |
5 | * Permission to use, copy, modify, and/or distribute this software for any |
6 | * purpose with or without fee is hereby granted, provided that the above |
7 | * copyright notice and this permission notice appear in all copies. |
8 | * |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 | */ |
17 | |
18 | #include "core.h" |
19 | |
20 | #include <linux/module.h> |
21 | #include <linux/moduleparam.h> |
22 | #include <linux/export.h> |
23 | #include <linux/vmalloc.h> |
24 | |
25 | #include "debug.h" |
26 | #include "hif-ops.h" |
27 | #include "htc-ops.h" |
28 | #include "cfg80211.h" |
29 | |
30 | unsigned int debug_mask; |
31 | static unsigned int suspend_mode; |
32 | static unsigned int wow_mode; |
33 | static unsigned int uart_debug; |
34 | static unsigned int uart_rate = 115200; |
35 | static unsigned int ath6kl_p2p; |
36 | static unsigned int testmode; |
37 | static unsigned int recovery_enable; |
38 | static unsigned int heart_beat_poll; |
39 | |
40 | module_param(debug_mask, uint, 0644); |
41 | module_param(suspend_mode, uint, 0644); |
42 | module_param(wow_mode, uint, 0644); |
43 | module_param(uart_debug, uint, 0644); |
44 | module_param(uart_rate, uint, 0644); |
45 | module_param(ath6kl_p2p, uint, 0644); |
46 | module_param(testmode, uint, 0644); |
47 | module_param(recovery_enable, uint, 0644); |
48 | module_param(heart_beat_poll, uint, 0644); |
49 | MODULE_PARM_DESC(recovery_enable, "Enable recovery from firmware error" ); |
50 | MODULE_PARM_DESC(heart_beat_poll, |
51 | "Enable fw error detection periodic polling in msecs - Also set recovery_enable for this to be effective" ); |
52 | |
53 | |
54 | void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb) |
55 | { |
56 | ath6kl_htc_tx_complete(ar, skb); |
57 | } |
58 | EXPORT_SYMBOL(ath6kl_core_tx_complete); |
59 | |
60 | void ath6kl_core_rx_complete(struct ath6kl *ar, struct sk_buff *skb, u8 pipe) |
61 | { |
62 | ath6kl_htc_rx_complete(ar, skb, pipe); |
63 | } |
64 | EXPORT_SYMBOL(ath6kl_core_rx_complete); |
65 | |
66 | int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) |
67 | { |
68 | struct ath6kl_bmi_target_info targ_info; |
69 | struct wireless_dev *wdev; |
70 | int ret = 0, i; |
71 | |
72 | switch (htc_type) { |
73 | case ATH6KL_HTC_TYPE_MBOX: |
74 | ath6kl_htc_mbox_attach(ar); |
75 | break; |
76 | case ATH6KL_HTC_TYPE_PIPE: |
77 | ath6kl_htc_pipe_attach(ar); |
78 | break; |
79 | default: |
80 | WARN_ON(1); |
81 | return -ENOMEM; |
82 | } |
83 | |
84 | ar->ath6kl_wq = create_singlethread_workqueue("ath6kl" ); |
85 | if (!ar->ath6kl_wq) |
86 | return -ENOMEM; |
87 | |
88 | ret = ath6kl_bmi_init(ar); |
89 | if (ret) |
90 | goto err_wq; |
91 | |
92 | /* |
93 | * Turn on power to get hardware (target) version and leave power |
94 | * on delibrately as we will boot the hardware anyway within few |
95 | * seconds. |
96 | */ |
97 | ret = ath6kl_hif_power_on(ar); |
98 | if (ret) |
99 | goto err_bmi_cleanup; |
100 | |
101 | ret = ath6kl_bmi_get_target_info(ar, targ_info: &targ_info); |
102 | if (ret) |
103 | goto err_power_off; |
104 | |
105 | ar->version.target_ver = le32_to_cpu(targ_info.version); |
106 | ar->target_type = le32_to_cpu(targ_info.type); |
107 | ar->wiphy->hw_version = le32_to_cpu(targ_info.version); |
108 | |
109 | ret = ath6kl_init_hw_params(ar); |
110 | if (ret) |
111 | goto err_power_off; |
112 | |
113 | ar->htc_target = ath6kl_htc_create(ar); |
114 | |
115 | if (!ar->htc_target) { |
116 | ret = -ENOMEM; |
117 | goto err_power_off; |
118 | } |
119 | |
120 | ar->testmode = testmode; |
121 | |
122 | ret = ath6kl_init_fetch_firmwares(ar); |
123 | if (ret) |
124 | goto err_htc_cleanup; |
125 | |
126 | /* FIXME: we should free all firmwares in the error cases below */ |
127 | |
128 | /* |
129 | * Backwards compatibility support for older ar6004 firmware images |
130 | * which do not set these feature flags. |
131 | */ |
132 | if (ar->target_type == TARGET_TYPE_AR6004 && |
133 | ar->fw_api <= 4) { |
134 | __set_bit(ATH6KL_FW_CAPABILITY_64BIT_RATES, |
135 | ar->fw_capabilities); |
136 | __set_bit(ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS, |
137 | ar->fw_capabilities); |
138 | |
139 | if (ar->hw.id == AR6004_HW_1_3_VERSION) |
140 | __set_bit(ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT, |
141 | ar->fw_capabilities); |
142 | } |
143 | |
144 | /* Indicate that WMI is enabled (although not ready yet) */ |
145 | set_bit(nr: WMI_ENABLED, addr: &ar->flag); |
146 | ar->wmi = ath6kl_wmi_init(devt: ar); |
147 | if (!ar->wmi) { |
148 | ath6kl_err(fmt: "failed to initialize wmi\n" ); |
149 | ret = -EIO; |
150 | goto err_htc_cleanup; |
151 | } |
152 | |
153 | ath6kl_dbg(mask: ATH6KL_DBG_TRC, fmt: "%s: got wmi @ 0x%p.\n" , __func__, ar->wmi); |
154 | |
155 | /* setup access class priority mappings */ |
156 | ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest */ |
157 | ar->ac_stream_pri_map[WMM_AC_BE] = 1; |
158 | ar->ac_stream_pri_map[WMM_AC_VI] = 2; |
159 | ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */ |
160 | |
161 | /* allocate some buffers that handle larger AMSDU frames */ |
162 | ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS); |
163 | |
164 | ath6kl_cookie_init(ar); |
165 | |
166 | ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | |
167 | ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; |
168 | |
169 | if (suspend_mode && |
170 | suspend_mode >= WLAN_POWER_STATE_CUT_PWR && |
171 | suspend_mode <= WLAN_POWER_STATE_WOW) |
172 | ar->suspend_mode = suspend_mode; |
173 | else |
174 | ar->suspend_mode = 0; |
175 | |
176 | if (suspend_mode == WLAN_POWER_STATE_WOW && |
177 | (wow_mode == WLAN_POWER_STATE_CUT_PWR || |
178 | wow_mode == WLAN_POWER_STATE_DEEP_SLEEP)) |
179 | ar->wow_suspend_mode = wow_mode; |
180 | else |
181 | ar->wow_suspend_mode = 0; |
182 | |
183 | if (uart_debug) |
184 | ar->conf_flags |= ATH6KL_CONF_UART_DEBUG; |
185 | ar->hw.uarttx_rate = uart_rate; |
186 | |
187 | set_bit(nr: FIRST_BOOT, addr: &ar->flag); |
188 | |
189 | ath6kl_debug_init(ar); |
190 | |
191 | ret = ath6kl_init_hw_start(ar); |
192 | if (ret) { |
193 | ath6kl_err(fmt: "Failed to start hardware: %d\n" , ret); |
194 | goto err_rxbuf_cleanup; |
195 | } |
196 | |
197 | /* give our connected endpoints some buffers */ |
198 | ath6kl_rx_refill(target: ar->htc_target, endpoint: ar->ctrl_ep); |
199 | ath6kl_rx_refill(target: ar->htc_target, endpoint: ar->ac2ep_map[WMM_AC_BE]); |
200 | |
201 | ret = ath6kl_cfg80211_init(ar); |
202 | if (ret) |
203 | goto err_rxbuf_cleanup; |
204 | |
205 | ret = ath6kl_debug_init_fs(ar); |
206 | if (ret) { |
207 | wiphy_unregister(wiphy: ar->wiphy); |
208 | goto err_rxbuf_cleanup; |
209 | } |
210 | |
211 | for (i = 0; i < ar->vif_max; i++) |
212 | ar->avail_idx_map |= BIT(i); |
213 | |
214 | rtnl_lock(); |
215 | wiphy_lock(wiphy: ar->wiphy); |
216 | |
217 | /* Add an initial station interface */ |
218 | wdev = ath6kl_interface_add(ar, name: "wlan%d" , NET_NAME_ENUM, |
219 | type: NL80211_IFTYPE_STATION, fw_vif_idx: 0, nw_type: INFRA_NETWORK); |
220 | |
221 | wiphy_unlock(wiphy: ar->wiphy); |
222 | rtnl_unlock(); |
223 | |
224 | if (!wdev) { |
225 | ath6kl_err(fmt: "Failed to instantiate a network device\n" ); |
226 | ret = -ENOMEM; |
227 | wiphy_unregister(wiphy: ar->wiphy); |
228 | goto err_rxbuf_cleanup; |
229 | } |
230 | |
231 | ath6kl_dbg(mask: ATH6KL_DBG_TRC, fmt: "%s: name=%s dev=0x%p, ar=0x%p\n" , |
232 | __func__, wdev->netdev->name, wdev->netdev, ar); |
233 | |
234 | ar->fw_recovery.enable = !!recovery_enable; |
235 | if (!ar->fw_recovery.enable) |
236 | return ret; |
237 | |
238 | if (heart_beat_poll && |
239 | test_bit(ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL, |
240 | ar->fw_capabilities)) |
241 | ar->fw_recovery.hb_poll = heart_beat_poll; |
242 | |
243 | ath6kl_recovery_init(ar); |
244 | |
245 | return ret; |
246 | |
247 | err_rxbuf_cleanup: |
248 | ath6kl_debug_cleanup(ar); |
249 | ath6kl_htc_flush_rx_buf(target: ar->htc_target); |
250 | ath6kl_cleanup_amsdu_rxbufs(ar); |
251 | ath6kl_wmi_shutdown(wmi: ar->wmi); |
252 | clear_bit(nr: WMI_ENABLED, addr: &ar->flag); |
253 | ar->wmi = NULL; |
254 | err_htc_cleanup: |
255 | ath6kl_htc_cleanup(target: ar->htc_target); |
256 | err_power_off: |
257 | ath6kl_hif_power_off(ar); |
258 | err_bmi_cleanup: |
259 | ath6kl_bmi_cleanup(ar); |
260 | err_wq: |
261 | destroy_workqueue(wq: ar->ath6kl_wq); |
262 | |
263 | return ret; |
264 | } |
265 | EXPORT_SYMBOL(ath6kl_core_init); |
266 | |
267 | struct ath6kl *ath6kl_core_create(struct device *dev) |
268 | { |
269 | struct ath6kl *ar; |
270 | u8 ctr; |
271 | |
272 | ar = ath6kl_cfg80211_create(); |
273 | if (!ar) |
274 | return NULL; |
275 | |
276 | ar->p2p = !!ath6kl_p2p; |
277 | ar->dev = dev; |
278 | |
279 | ar->vif_max = 1; |
280 | |
281 | ar->max_norm_iface = 1; |
282 | |
283 | spin_lock_init(&ar->lock); |
284 | spin_lock_init(&ar->mcastpsq_lock); |
285 | spin_lock_init(&ar->list_lock); |
286 | |
287 | init_waitqueue_head(&ar->event_wq); |
288 | sema_init(sem: &ar->sem, val: 1); |
289 | |
290 | INIT_LIST_HEAD(list: &ar->amsdu_rx_buffer_queue); |
291 | INIT_LIST_HEAD(list: &ar->vif_list); |
292 | |
293 | clear_bit(nr: WMI_ENABLED, addr: &ar->flag); |
294 | clear_bit(nr: SKIP_SCAN, addr: &ar->flag); |
295 | clear_bit(nr: DESTROY_IN_PROGRESS, addr: &ar->flag); |
296 | |
297 | ar->tx_pwr = 0; |
298 | ar->intra_bss = 1; |
299 | ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD; |
300 | |
301 | ar->state = ATH6KL_STATE_OFF; |
302 | |
303 | memset((u8 *)ar->sta_list, 0, |
304 | AP_MAX_NUM_STA * sizeof(struct ath6kl_sta)); |
305 | |
306 | /* Init the PS queues */ |
307 | for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) { |
308 | spin_lock_init(&ar->sta_list[ctr].psq_lock); |
309 | skb_queue_head_init(list: &ar->sta_list[ctr].psq); |
310 | skb_queue_head_init(list: &ar->sta_list[ctr].apsdq); |
311 | ar->sta_list[ctr].mgmt_psq_len = 0; |
312 | INIT_LIST_HEAD(list: &ar->sta_list[ctr].mgmt_psq); |
313 | ar->sta_list[ctr].aggr_conn = |
314 | kzalloc(size: sizeof(struct aggr_info_conn), GFP_KERNEL); |
315 | if (!ar->sta_list[ctr].aggr_conn) { |
316 | ath6kl_err(fmt: "Failed to allocate memory for sta aggregation information\n" ); |
317 | ath6kl_core_destroy(ar); |
318 | return NULL; |
319 | } |
320 | } |
321 | |
322 | skb_queue_head_init(list: &ar->mcastpsq); |
323 | |
324 | memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3); |
325 | |
326 | return ar; |
327 | } |
328 | EXPORT_SYMBOL(ath6kl_core_create); |
329 | |
330 | void ath6kl_core_cleanup(struct ath6kl *ar) |
331 | { |
332 | ath6kl_hif_power_off(ar); |
333 | |
334 | ath6kl_recovery_cleanup(ar); |
335 | |
336 | destroy_workqueue(wq: ar->ath6kl_wq); |
337 | |
338 | if (ar->htc_target) |
339 | ath6kl_htc_cleanup(target: ar->htc_target); |
340 | |
341 | ath6kl_cookie_cleanup(ar); |
342 | |
343 | ath6kl_cleanup_amsdu_rxbufs(ar); |
344 | |
345 | ath6kl_bmi_cleanup(ar); |
346 | |
347 | ath6kl_debug_cleanup(ar); |
348 | |
349 | kfree(objp: ar->fw_board); |
350 | kfree(objp: ar->fw_otp); |
351 | vfree(addr: ar->fw); |
352 | kfree(objp: ar->fw_patch); |
353 | kfree(objp: ar->fw_testscript); |
354 | |
355 | ath6kl_cfg80211_cleanup(ar); |
356 | } |
357 | EXPORT_SYMBOL(ath6kl_core_cleanup); |
358 | |
359 | void ath6kl_core_destroy(struct ath6kl *ar) |
360 | { |
361 | ath6kl_cfg80211_destroy(ar); |
362 | } |
363 | EXPORT_SYMBOL(ath6kl_core_destroy); |
364 | |
365 | MODULE_AUTHOR("Qualcomm Atheros" ); |
366 | MODULE_DESCRIPTION("Core module for AR600x SDIO and USB devices." ); |
367 | MODULE_LICENSE("Dual BSD/GPL" ); |
368 | |