diff options
author | Solomon Peachy <pizza@shaftnet.org> | 2013-05-24 20:04:38 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-05-29 15:26:40 -0400 |
commit | a910e4a94f6923c8c988565525f017f687bf7205 (patch) | |
tree | b324e2e129ee4d9dcb1c1466590ce71d087e8158 /drivers/net | |
parent | 5f07d15a77e3098e7286b016247ecca20a0209d4 (diff) |
cw1200: add driver for the ST-E CW1100 & CW1200 WLAN chipsets
Signed-off-by: Solomon Peachy <pizza@shaftnet.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net')
32 files changed, 14989 insertions, 0 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index f8f0156dff4e..200020eb3005 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig | |||
@@ -280,5 +280,6 @@ source "drivers/net/wireless/rtlwifi/Kconfig" | |||
280 | source "drivers/net/wireless/ti/Kconfig" | 280 | source "drivers/net/wireless/ti/Kconfig" |
281 | source "drivers/net/wireless/zd1211rw/Kconfig" | 281 | source "drivers/net/wireless/zd1211rw/Kconfig" |
282 | source "drivers/net/wireless/mwifiex/Kconfig" | 282 | source "drivers/net/wireless/mwifiex/Kconfig" |
283 | source "drivers/net/wireless/cw1200/Kconfig" | ||
283 | 284 | ||
284 | endif # WLAN | 285 | endif # WLAN |
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 67156efe14c4..0fab227025be 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile | |||
@@ -57,3 +57,5 @@ obj-$(CONFIG_MWIFIEX) += mwifiex/ | |||
57 | 57 | ||
58 | obj-$(CONFIG_BRCMFMAC) += brcm80211/ | 58 | obj-$(CONFIG_BRCMFMAC) += brcm80211/ |
59 | obj-$(CONFIG_BRCMSMAC) += brcm80211/ | 59 | obj-$(CONFIG_BRCMSMAC) += brcm80211/ |
60 | |||
61 | obj-$(CONFIG_CW1200) += cw1200/ | ||
diff --git a/drivers/net/wireless/cw1200/Kconfig b/drivers/net/wireless/cw1200/Kconfig new file mode 100644 index 000000000000..13e36119ae9f --- /dev/null +++ b/drivers/net/wireless/cw1200/Kconfig | |||
@@ -0,0 +1,46 @@ | |||
1 | config CW1200 | ||
2 | tristate "CW1200 WLAN support" | ||
3 | depends on MAC80211 && CFG80211 | ||
4 | help | ||
5 | This is a driver for the ST-E CW1100 & CW1200 WLAN chipsets. | ||
6 | This option just enables the driver core, see below for | ||
7 | specific bus support. | ||
8 | |||
9 | if CW1200 | ||
10 | |||
11 | config CW1200_WLAN_SDIO | ||
12 | tristate "Support SDIO platforms" | ||
13 | depends on CW1200 && MMC | ||
14 | help | ||
15 | Enable support for the CW1200 connected via an SDIO bus. | ||
16 | |||
17 | config CW1200_WLAN_SPI | ||
18 | tristate "Support SPI platforms" | ||
19 | depends on CW1200 && SPI | ||
20 | help | ||
21 | Enables support for the CW1200 connected via a SPI bus. | ||
22 | |||
23 | config CW1200_WLAN_SAGRAD | ||
24 | tristate "Support Sagrad SG901-1091/1098 modules" | ||
25 | depends on CW1200_WLAN_SDIO | ||
26 | help | ||
27 | This provides the platform data glue to support the | ||
28 | Sagrad SG901-1091/1098 modules in their standard SDIO EVK. | ||
29 | It also includes example SPI platform data. | ||
30 | |||
31 | menu "Driver debug features" | ||
32 | depends on CW1200 && DEBUG_FS | ||
33 | |||
34 | config CW1200_ETF | ||
35 | bool "Enable CW1200 Engineering Test Framework hooks" | ||
36 | help | ||
37 | If you don't know what this is, just say N. | ||
38 | |||
39 | config CW1200_ITP | ||
40 | bool "Enable ITP access" | ||
41 | help | ||
42 | If you don't know what this is, just say N. | ||
43 | |||
44 | endmenu | ||
45 | |||
46 | endif | ||
diff --git a/drivers/net/wireless/cw1200/Makefile b/drivers/net/wireless/cw1200/Makefile new file mode 100644 index 000000000000..c19737276be2 --- /dev/null +++ b/drivers/net/wireless/cw1200/Makefile | |||
@@ -0,0 +1,24 @@ | |||
1 | cw1200_core-y := \ | ||
2 | fwio.o \ | ||
3 | txrx.o \ | ||
4 | main.o \ | ||
5 | queue.o \ | ||
6 | hwio.o \ | ||
7 | bh.o \ | ||
8 | wsm.o \ | ||
9 | sta.o \ | ||
10 | scan.o \ | ||
11 | pm.o \ | ||
12 | debug.o | ||
13 | cw1200_core-$(CONFIG_CW1200_ITP) += itp.o | ||
14 | |||
15 | # CFLAGS_sta.o += -DDEBUG | ||
16 | |||
17 | cw1200_wlan_sdio-y := cw1200_sdio.o | ||
18 | cw1200_wlan_spi-y := cw1200_spi.o | ||
19 | cw1200_wlan_sagrad-y := cw1200_sagrad.o | ||
20 | |||
21 | obj-$(CONFIG_CW1200) += cw1200_core.o | ||
22 | obj-$(CONFIG_CW1200_WLAN_SDIO) += cw1200_wlan_sdio.o | ||
23 | obj-$(CONFIG_CW1200_WLAN_SPI) += cw1200_wlan_spi.o | ||
24 | obj-$(CONFIG_CW1200_WLAN_SAGRAD) += cw1200_wlan_sagrad.o | ||
diff --git a/drivers/net/wireless/cw1200/bh.c b/drivers/net/wireless/cw1200/bh.c new file mode 100644 index 000000000000..cf7375f92fb3 --- /dev/null +++ b/drivers/net/wireless/cw1200/bh.c | |||
@@ -0,0 +1,616 @@ | |||
1 | /* | ||
2 | * Device handling thread implementation 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 | * ST-Ericsson UMAC CW1200 driver, which is | ||
9 | * Copyright (c) 2010, ST-Ericsson | ||
10 | * Author: Ajitpal Singh <ajitpal.singh@stericsson.com> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <net/mac80211.h> | ||
19 | #include <linux/kthread.h> | ||
20 | #include <linux/timer.h> | ||
21 | |||
22 | #include "cw1200.h" | ||
23 | #include "bh.h" | ||
24 | #include "hwio.h" | ||
25 | #include "wsm.h" | ||
26 | #include "sbus.h" | ||
27 | #include "debug.h" | ||
28 | #include "fwio.h" | ||
29 | |||
30 | static int cw1200_bh(void *arg); | ||
31 | |||
32 | #define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4) | ||
33 | /* an SPI message cannot be bigger than (2"12-1)*2 bytes | ||
34 | * "*2" to cvt to bytes */ | ||
35 | #define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2) | ||
36 | #define PIGGYBACK_CTRL_REG (2) | ||
37 | #define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG) | ||
38 | |||
39 | /* Suspend state privates */ | ||
40 | enum cw1200_bh_pm_state { | ||
41 | CW1200_BH_RESUMED = 0, | ||
42 | CW1200_BH_SUSPEND, | ||
43 | CW1200_BH_SUSPENDED, | ||
44 | CW1200_BH_RESUME, | ||
45 | }; | ||
46 | |||
47 | typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv, | ||
48 | u8 *data, size_t size); | ||
49 | |||
50 | static void cw1200_bh_work(struct work_struct *work) | ||
51 | { | ||
52 | struct cw1200_common *priv = | ||
53 | container_of(work, struct cw1200_common, bh_work); | ||
54 | cw1200_bh(priv); | ||
55 | } | ||
56 | |||
57 | int cw1200_register_bh(struct cw1200_common *priv) | ||
58 | { | ||
59 | int err = 0; | ||
60 | /* Realtime workqueue */ | ||
61 | priv->bh_workqueue = alloc_workqueue("cw1200_bh", | ||
62 | WQ_MEM_RECLAIM | WQ_HIGHPRI | ||
63 | | WQ_CPU_INTENSIVE, 1); | ||
64 | |||
65 | if (!priv->bh_workqueue) | ||
66 | return -ENOMEM; | ||
67 | |||
68 | INIT_WORK(&priv->bh_work, cw1200_bh_work); | ||
69 | |||
70 | pr_debug("[BH] register.\n"); | ||
71 | |||
72 | atomic_set(&priv->bh_rx, 0); | ||
73 | atomic_set(&priv->bh_tx, 0); | ||
74 | atomic_set(&priv->bh_term, 0); | ||
75 | atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED); | ||
76 | priv->bh_error = 0; | ||
77 | priv->hw_bufs_used = 0; | ||
78 | priv->buf_id_tx = 0; | ||
79 | priv->buf_id_rx = 0; | ||
80 | init_waitqueue_head(&priv->bh_wq); | ||
81 | init_waitqueue_head(&priv->bh_evt_wq); | ||
82 | |||
83 | err = !queue_work(priv->bh_workqueue, &priv->bh_work); | ||
84 | WARN_ON(err); | ||
85 | return err; | ||
86 | } | ||
87 | |||
88 | void cw1200_unregister_bh(struct cw1200_common *priv) | ||
89 | { | ||
90 | atomic_add(1, &priv->bh_term); | ||
91 | wake_up(&priv->bh_wq); | ||
92 | |||
93 | flush_workqueue(priv->bh_workqueue); | ||
94 | |||
95 | destroy_workqueue(priv->bh_workqueue); | ||
96 | priv->bh_workqueue = NULL; | ||
97 | |||
98 | pr_debug("[BH] unregistered.\n"); | ||
99 | } | ||
100 | |||
101 | void cw1200_irq_handler(struct cw1200_common *priv) | ||
102 | { | ||
103 | pr_debug("[BH] irq.\n"); | ||
104 | |||
105 | /* Disable Interrupts! */ | ||
106 | /* NOTE: sbus_ops->lock already held */ | ||
107 | __cw1200_irq_enable(priv, 0); | ||
108 | |||
109 | if (/* WARN_ON */(priv->bh_error)) | ||
110 | return; | ||
111 | |||
112 | if (atomic_add_return(1, &priv->bh_rx) == 1) | ||
113 | wake_up(&priv->bh_wq); | ||
114 | } | ||
115 | EXPORT_SYMBOL_GPL(cw1200_irq_handler); | ||
116 | |||
117 | void cw1200_bh_wakeup(struct cw1200_common *priv) | ||
118 | { | ||
119 | pr_debug("[BH] wakeup.\n"); | ||
120 | if (priv->bh_error) { | ||
121 | pr_err("[BH] wakeup failed (BH error)\n"); | ||
122 | return; | ||
123 | } | ||
124 | |||
125 | if (atomic_add_return(1, &priv->bh_tx) == 1) | ||
126 | wake_up(&priv->bh_wq); | ||
127 | } | ||
128 | |||
129 | int cw1200_bh_suspend(struct cw1200_common *priv) | ||
130 | { | ||
131 | pr_debug("[BH] suspend.\n"); | ||
132 | if (priv->bh_error) { | ||
133 | wiphy_warn(priv->hw->wiphy, "BH error -- can't suspend\n"); | ||
134 | return -EINVAL; | ||
135 | } | ||
136 | |||
137 | atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND); | ||
138 | wake_up(&priv->bh_wq); | ||
139 | return wait_event_timeout(priv->bh_evt_wq, priv->bh_error || | ||
140 | (CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)), | ||
141 | 1 * HZ) ? 0 : -ETIMEDOUT; | ||
142 | } | ||
143 | |||
144 | int cw1200_bh_resume(struct cw1200_common *priv) | ||
145 | { | ||
146 | pr_debug("[BH] resume.\n"); | ||
147 | if (priv->bh_error) { | ||
148 | wiphy_warn(priv->hw->wiphy, "BH error -- can't resume\n"); | ||
149 | return -EINVAL; | ||
150 | } | ||
151 | |||
152 | atomic_set(&priv->bh_suspend, CW1200_BH_RESUME); | ||
153 | wake_up(&priv->bh_wq); | ||
154 | return wait_event_timeout(priv->bh_evt_wq, priv->bh_error || | ||
155 | (CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)), | ||
156 | 1 * HZ) ? 0 : -ETIMEDOUT; | ||
157 | } | ||
158 | |||
159 | static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv) | ||
160 | { | ||
161 | ++priv->hw_bufs_used; | ||
162 | } | ||
163 | |||
164 | int wsm_release_tx_buffer(struct cw1200_common *priv, int count) | ||
165 | { | ||
166 | int ret = 0; | ||
167 | int hw_bufs_used = priv->hw_bufs_used; | ||
168 | |||
169 | priv->hw_bufs_used -= count; | ||
170 | if (WARN_ON(priv->hw_bufs_used < 0)) | ||
171 | ret = -1; | ||
172 | else if (hw_bufs_used >= priv->wsm_caps.input_buffers) | ||
173 | ret = 1; | ||
174 | if (!priv->hw_bufs_used) | ||
175 | wake_up(&priv->bh_evt_wq); | ||
176 | return ret; | ||
177 | } | ||
178 | |||
179 | static int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv, | ||
180 | u16 *ctrl_reg) | ||
181 | { | ||
182 | int ret; | ||
183 | |||
184 | ret = cw1200_reg_read_16(priv, | ||
185 | ST90TDS_CONTROL_REG_ID, ctrl_reg); | ||
186 | if (ret) { | ||
187 | ret = cw1200_reg_read_16(priv, | ||
188 | ST90TDS_CONTROL_REG_ID, ctrl_reg); | ||
189 | if (ret) | ||
190 | pr_err("[BH] Failed to read control register.\n"); | ||
191 | } | ||
192 | |||
193 | return ret; | ||
194 | } | ||
195 | |||
196 | static int cw1200_device_wakeup(struct cw1200_common *priv) | ||
197 | { | ||
198 | u16 ctrl_reg; | ||
199 | int ret; | ||
200 | |||
201 | pr_debug("[BH] Device wakeup.\n"); | ||
202 | |||
203 | /* First, set the dpll register */ | ||
204 | ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID, | ||
205 | cw1200_dpll_from_clk(priv->hw_refclk)); | ||
206 | if (WARN_ON(ret)) | ||
207 | return ret; | ||
208 | |||
209 | /* To force the device to be always-on, the host sets WLAN_UP to 1 */ | ||
210 | ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, | ||
211 | ST90TDS_CONT_WUP_BIT); | ||
212 | if (WARN_ON(ret)) | ||
213 | return ret; | ||
214 | |||
215 | ret = cw1200_bh_read_ctrl_reg(priv, &ctrl_reg); | ||
216 | if (WARN_ON(ret)) | ||
217 | return ret; | ||
218 | |||
219 | /* If the device returns WLAN_RDY as 1, the device is active and will | ||
220 | * remain active. */ | ||
221 | if (ctrl_reg & ST90TDS_CONT_RDY_BIT) { | ||
222 | pr_debug("[BH] Device awake.\n"); | ||
223 | return 1; | ||
224 | } | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | /* Must be called from BH thraed. */ | ||
230 | void cw1200_enable_powersave(struct cw1200_common *priv, | ||
231 | bool enable) | ||
232 | { | ||
233 | pr_debug("[BH] Powerave is %s.\n", | ||
234 | enable ? "enabled" : "disabled"); | ||
235 | priv->powersave_enabled = enable; | ||
236 | } | ||
237 | |||
238 | static int cw1200_bh_rx_helper(struct cw1200_common *priv, | ||
239 | uint16_t *ctrl_reg, | ||
240 | int *tx) | ||
241 | { | ||
242 | size_t read_len = 0; | ||
243 | struct sk_buff *skb_rx = NULL; | ||
244 | struct wsm_hdr *wsm; | ||
245 | size_t wsm_len; | ||
246 | u16 wsm_id; | ||
247 | u8 wsm_seq; | ||
248 | int rx_resync = 1; | ||
249 | |||
250 | size_t alloc_len; | ||
251 | u8 *data; | ||
252 | |||
253 | read_len = (*ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2; | ||
254 | if (!read_len) | ||
255 | return 0; /* No more work */ | ||
256 | |||
257 | if (WARN_ON((read_len < sizeof(struct wsm_hdr)) || | ||
258 | (read_len > EFFECTIVE_BUF_SIZE))) { | ||
259 | pr_debug("Invalid read len: %zu (%04x)", | ||
260 | read_len, *ctrl_reg); | ||
261 | goto err; | ||
262 | } | ||
263 | |||
264 | /* Add SIZE of PIGGYBACK reg (CONTROL Reg) | ||
265 | * to the NEXT Message length + 2 Bytes for SKB */ | ||
266 | read_len = read_len + 2; | ||
267 | |||
268 | alloc_len = priv->sbus_ops->align_size( | ||
269 | priv->sbus_priv, read_len); | ||
270 | |||
271 | /* Check if not exceeding CW1200 capabilities */ | ||
272 | if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) { | ||
273 | pr_debug("Read aligned len: %zu\n", | ||
274 | alloc_len); | ||
275 | } | ||
276 | |||
277 | skb_rx = dev_alloc_skb(alloc_len); | ||
278 | if (WARN_ON(!skb_rx)) | ||
279 | goto err; | ||
280 | |||
281 | skb_trim(skb_rx, 0); | ||
282 | skb_put(skb_rx, read_len); | ||
283 | data = skb_rx->data; | ||
284 | if (WARN_ON(!data)) | ||
285 | goto err; | ||
286 | |||
287 | if (WARN_ON(cw1200_data_read(priv, data, alloc_len))) { | ||
288 | pr_err("rx blew up, len %zu\n", alloc_len); | ||
289 | goto err; | ||
290 | } | ||
291 | |||
292 | /* Piggyback */ | ||
293 | *ctrl_reg = __le16_to_cpu( | ||
294 | ((__le16 *)data)[alloc_len / 2 - 1]); | ||
295 | |||
296 | wsm = (struct wsm_hdr *)data; | ||
297 | wsm_len = __le16_to_cpu(wsm->len); | ||
298 | if (WARN_ON(wsm_len > read_len)) | ||
299 | goto err; | ||
300 | |||
301 | if (priv->wsm_enable_wsm_dumps) | ||
302 | print_hex_dump_bytes("<-- ", | ||
303 | DUMP_PREFIX_NONE, | ||
304 | data, wsm_len); | ||
305 | |||
306 | wsm_id = __le16_to_cpu(wsm->id) & 0xFFF; | ||
307 | wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7; | ||
308 | |||
309 | skb_trim(skb_rx, wsm_len); | ||
310 | |||
311 | if (wsm_id == 0x0800) { | ||
312 | wsm_handle_exception(priv, | ||
313 | &data[sizeof(*wsm)], | ||
314 | wsm_len - sizeof(*wsm)); | ||
315 | goto err; | ||
316 | } else if (!rx_resync) { | ||
317 | if (WARN_ON(wsm_seq != priv->wsm_rx_seq)) | ||
318 | goto err; | ||
319 | } | ||
320 | priv->wsm_rx_seq = (wsm_seq + 1) & 7; | ||
321 | rx_resync = 0; | ||
322 | |||
323 | if (wsm_id & 0x0400) { | ||
324 | int rc = wsm_release_tx_buffer(priv, 1); | ||
325 | if (WARN_ON(rc < 0)) | ||
326 | return rc; | ||
327 | else if (rc > 0) | ||
328 | *tx = 1; | ||
329 | } | ||
330 | |||
331 | /* cw1200_wsm_rx takes care on SKB livetime */ | ||
332 | if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb_rx))) | ||
333 | goto err; | ||
334 | |||
335 | if (skb_rx) { | ||
336 | dev_kfree_skb(skb_rx); | ||
337 | skb_rx = NULL; | ||
338 | } | ||
339 | |||
340 | return 0; | ||
341 | |||
342 | err: | ||
343 | if (skb_rx) { | ||
344 | dev_kfree_skb(skb_rx); | ||
345 | skb_rx = NULL; | ||
346 | } | ||
347 | return -1; | ||
348 | } | ||
349 | |||
350 | static int cw1200_bh_tx_helper(struct cw1200_common *priv, | ||
351 | int *pending_tx, | ||
352 | int *tx_burst) | ||
353 | { | ||
354 | size_t tx_len; | ||
355 | u8 *data; | ||
356 | int ret; | ||
357 | struct wsm_hdr *wsm; | ||
358 | |||
359 | if (priv->device_can_sleep) { | ||
360 | ret = cw1200_device_wakeup(priv); | ||
361 | if (WARN_ON(ret < 0)) { /* Error in wakeup */ | ||
362 | *pending_tx = 1; | ||
363 | return 0; | ||
364 | } else if (ret) { /* Woke up */ | ||
365 | priv->device_can_sleep = false; | ||
366 | } else { /* Did not awake */ | ||
367 | *pending_tx = 1; | ||
368 | return 0; | ||
369 | } | ||
370 | } | ||
371 | |||
372 | wsm_alloc_tx_buffer(priv); | ||
373 | ret = wsm_get_tx(priv, &data, &tx_len, tx_burst); | ||
374 | if (ret <= 0) { | ||
375 | wsm_release_tx_buffer(priv, 1); | ||
376 | if (WARN_ON(ret < 0)) | ||
377 | return ret; /* Error */ | ||
378 | return 0; /* No work */ | ||
379 | } | ||
380 | |||
381 | wsm = (struct wsm_hdr *)data; | ||
382 | BUG_ON(tx_len < sizeof(*wsm)); | ||
383 | BUG_ON(__le16_to_cpu(wsm->len) != tx_len); | ||
384 | |||
385 | atomic_add(1, &priv->bh_tx); | ||
386 | |||
387 | tx_len = priv->sbus_ops->align_size( | ||
388 | priv->sbus_priv, tx_len); | ||
389 | |||
390 | /* Check if not exceeding CW1200 capabilities */ | ||
391 | if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE)) | ||
392 | pr_debug("Write aligned len: %zu\n", tx_len); | ||
393 | |||
394 | wsm->id &= __cpu_to_le16(0xffff ^ WSM_TX_SEQ(WSM_TX_SEQ_MAX)); | ||
395 | wsm->id |= __cpu_to_le16(WSM_TX_SEQ(priv->wsm_tx_seq)); | ||
396 | |||
397 | if (WARN_ON(cw1200_data_write(priv, data, tx_len))) { | ||
398 | pr_err("tx blew up, len %zu\n", tx_len); | ||
399 | wsm_release_tx_buffer(priv, 1); | ||
400 | return -1; /* Error */ | ||
401 | } | ||
402 | |||
403 | if (priv->wsm_enable_wsm_dumps) | ||
404 | print_hex_dump_bytes("--> ", | ||
405 | DUMP_PREFIX_NONE, | ||
406 | data, | ||
407 | __le16_to_cpu(wsm->len)); | ||
408 | |||
409 | wsm_txed(priv, data); | ||
410 | priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX; | ||
411 | |||
412 | if (*tx_burst > 1) { | ||
413 | cw1200_debug_tx_burst(priv); | ||
414 | return 1; /* Work remains */ | ||
415 | } | ||
416 | |||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | static int cw1200_bh(void *arg) | ||
421 | { | ||
422 | struct cw1200_common *priv = arg; | ||
423 | int rx, tx, term, suspend; | ||
424 | u16 ctrl_reg = 0; | ||
425 | int tx_allowed; | ||
426 | int pending_tx = 0; | ||
427 | int tx_burst; | ||
428 | long status; | ||
429 | u32 dummy; | ||
430 | int ret; | ||
431 | |||
432 | for (;;) { | ||
433 | if (!priv->hw_bufs_used && | ||
434 | priv->powersave_enabled && | ||
435 | !priv->device_can_sleep && | ||
436 | !atomic_read(&priv->recent_scan)) { | ||
437 | status = 1 * HZ; | ||
438 | pr_debug("[BH] Device wakedown. No data.\n"); | ||
439 | cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0); | ||
440 | priv->device_can_sleep = true; | ||
441 | } else if (priv->hw_bufs_used) { | ||
442 | /* Interrupt loss detection */ | ||
443 | status = 1 * HZ; | ||
444 | } else { | ||
445 | status = MAX_SCHEDULE_TIMEOUT; | ||
446 | } | ||
447 | |||
448 | /* Dummy Read for SDIO retry mechanism*/ | ||
449 | if ((priv->hw_type != -1) && | ||
450 | (atomic_read(&priv->bh_rx) == 0) && | ||
451 | (atomic_read(&priv->bh_tx) == 0)) | ||
452 | cw1200_reg_read(priv, ST90TDS_CONFIG_REG_ID, | ||
453 | &dummy, sizeof(dummy)); | ||
454 | |||
455 | pr_debug("[BH] waiting ...\n"); | ||
456 | status = wait_event_interruptible_timeout(priv->bh_wq, ({ | ||
457 | rx = atomic_xchg(&priv->bh_rx, 0); | ||
458 | tx = atomic_xchg(&priv->bh_tx, 0); | ||
459 | term = atomic_xchg(&priv->bh_term, 0); | ||
460 | suspend = pending_tx ? | ||
461 | 0 : atomic_read(&priv->bh_suspend); | ||
462 | (rx || tx || term || suspend || priv->bh_error); | ||
463 | }), status); | ||
464 | |||
465 | pr_debug("[BH] - rx: %d, tx: %d, term: %d, suspend: %d, status: %ld\n", | ||
466 | rx, tx, term, suspend, status); | ||
467 | |||
468 | /* Did an error occur? */ | ||
469 | if ((status < 0 && status != -ERESTARTSYS) || | ||
470 | term || priv->bh_error) { | ||
471 | break; | ||
472 | } | ||
473 | if (!status) { /* wait_event timed out */ | ||
474 | unsigned long timestamp = jiffies; | ||
475 | long timeout; | ||
476 | int pending = 0; | ||
477 | int i; | ||
478 | |||
479 | /* Check to see if we have any outstanding frames */ | ||
480 | if (priv->hw_bufs_used && (!rx || !tx)) { | ||
481 | wiphy_warn(priv->hw->wiphy, | ||
482 | "Missed interrupt? (%d frames outstanding)\n", | ||
483 | priv->hw_bufs_used); | ||
484 | rx = 1; | ||
485 | |||
486 | /* Get a timestamp of "oldest" frame */ | ||
487 | for (i = 0; i < 4; ++i) | ||
488 | pending += cw1200_queue_get_xmit_timestamp( | ||
489 | &priv->tx_queue[i], | ||
490 | ×tamp, | ||
491 | priv->pending_frame_id); | ||
492 | |||
493 | /* Check if frame transmission is timed out. | ||
494 | * Add an extra second with respect to possible | ||
495 | * interrupt loss. | ||
496 | */ | ||
497 | timeout = timestamp + | ||
498 | WSM_CMD_LAST_CHANCE_TIMEOUT + | ||
499 | 1 * HZ - | ||
500 | jiffies; | ||
501 | |||
502 | /* And terminate BH thread if the frame is "stuck" */ | ||
503 | if (pending && timeout < 0) { | ||
504 | wiphy_warn(priv->hw->wiphy, | ||
505 | "Timeout waiting for TX confirm (%d/%d pending, %ld vs %lu).\n", | ||
506 | priv->hw_bufs_used, pending, | ||
507 | timestamp, jiffies); | ||
508 | break; | ||
509 | } | ||
510 | } else if (!priv->device_can_sleep && | ||
511 | !atomic_read(&priv->recent_scan)) { | ||
512 | pr_debug("[BH] Device wakedown. Timeout.\n"); | ||
513 | cw1200_reg_write_16(priv, | ||
514 | ST90TDS_CONTROL_REG_ID, 0); | ||
515 | priv->device_can_sleep = true; | ||
516 | } | ||
517 | goto done; | ||
518 | } else if (suspend) { | ||
519 | pr_debug("[BH] Device suspend.\n"); | ||
520 | if (priv->powersave_enabled) { | ||
521 | pr_debug("[BH] Device wakedown. Suspend.\n"); | ||
522 | cw1200_reg_write_16(priv, | ||
523 | ST90TDS_CONTROL_REG_ID, 0); | ||
524 | priv->device_can_sleep = true; | ||
525 | } | ||
526 | |||
527 | atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED); | ||
528 | wake_up(&priv->bh_evt_wq); | ||
529 | status = wait_event_interruptible(priv->bh_wq, | ||
530 | CW1200_BH_RESUME == atomic_read(&priv->bh_suspend)); | ||
531 | if (status < 0) { | ||
532 | wiphy_err(priv->hw->wiphy, | ||
533 | "Failed to wait for resume: %ld.\n", | ||
534 | status); | ||
535 | break; | ||
536 | } | ||
537 | pr_debug("[BH] Device resume.\n"); | ||
538 | atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED); | ||
539 | wake_up(&priv->bh_evt_wq); | ||
540 | atomic_add(1, &priv->bh_rx); | ||
541 | goto done; | ||
542 | } | ||
543 | |||
544 | rx: | ||
545 | tx += pending_tx; | ||
546 | pending_tx = 0; | ||
547 | |||
548 | if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg)) | ||
549 | break; | ||
550 | |||
551 | /* Don't bother trying to rx unless we have data to read */ | ||
552 | if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) { | ||
553 | ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx); | ||
554 | if (ret < 0) | ||
555 | break; | ||
556 | /* Double up here if there's more data.. */ | ||
557 | if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) { | ||
558 | ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx); | ||
559 | if (ret < 0) | ||
560 | break; | ||
561 | } | ||
562 | } | ||
563 | |||
564 | tx: | ||
565 | if (tx) { | ||
566 | tx = 0; | ||
567 | |||
568 | BUG_ON(priv->hw_bufs_used > priv->wsm_caps.input_buffers); | ||
569 | tx_burst = priv->wsm_caps.input_buffers - priv->hw_bufs_used; | ||
570 | tx_allowed = tx_burst > 0; | ||
571 | |||
572 | if (!tx_allowed) { | ||
573 | /* Buffers full. Ensure we process tx | ||
574 | * after we handle rx.. | ||
575 | */ | ||
576 | pending_tx = tx; | ||
577 | goto done_rx; | ||
578 | } | ||
579 | ret = cw1200_bh_tx_helper(priv, &pending_tx, &tx_burst); | ||
580 | if (ret < 0) | ||
581 | break; | ||
582 | if (ret > 0) /* More to transmit */ | ||
583 | tx = ret; | ||
584 | |||
585 | /* Re-read ctrl reg */ | ||
586 | if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg)) | ||
587 | break; | ||
588 | } | ||
589 | |||
590 | done_rx: | ||
591 | if (priv->bh_error) | ||
592 | break; | ||
593 | if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) | ||
594 | goto rx; | ||
595 | if (tx) | ||
596 | goto tx; | ||
597 | |||
598 | done: | ||
599 | /* Re-enable device interrupts */ | ||
600 | priv->sbus_ops->lock(priv->sbus_priv); | ||
601 | __cw1200_irq_enable(priv, 1); | ||
602 | priv->sbus_ops->unlock(priv->sbus_priv); | ||
603 | } | ||
604 | |||
605 | /* Explicitly disable device interrupts */ | ||
606 | priv->sbus_ops->lock(priv->sbus_priv); | ||
607 | __cw1200_irq_enable(priv, 0); | ||
608 | priv->sbus_ops->unlock(priv->sbus_priv); | ||
609 | |||
610 | if (!term) { | ||
611 | pr_err("[BH] Fatal error, exiting.\n"); | ||
612 | priv->bh_error = 1; | ||
613 | /* TODO: schedule_work(recovery) */ | ||
614 | } | ||
615 | return 0; | ||
616 | } | ||
diff --git a/drivers/net/wireless/cw1200/bh.h b/drivers/net/wireless/cw1200/bh.h new file mode 100644 index 000000000000..af6a4853728f --- /dev/null +++ b/drivers/net/wireless/cw1200/bh.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * Device handling thread interface for mac80211 ST-Ericsson CW1200 drivers | ||
3 | * | ||
4 | * Copyright (c) 2010, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef CW1200_BH_H | ||
13 | #define CW1200_BH_H | ||
14 | |||
15 | /* extern */ struct cw1200_common; | ||
16 | |||
17 | int cw1200_register_bh(struct cw1200_common *priv); | ||
18 | void cw1200_unregister_bh(struct cw1200_common *priv); | ||
19 | void cw1200_irq_handler(struct cw1200_common *priv); | ||
20 | void cw1200_bh_wakeup(struct cw1200_common *priv); | ||
21 | int cw1200_bh_suspend(struct cw1200_common *priv); | ||
22 | int cw1200_bh_resume(struct cw1200_common *priv); | ||
23 | /* Must be called from BH thread. */ | ||
24 | void cw1200_enable_powersave(struct cw1200_common *priv, | ||
25 | bool enable); | ||
26 | int wsm_release_tx_buffer(struct cw1200_common *priv, int count); | ||
27 | |||
28 | #endif /* CW1200_BH_H */ | ||
diff --git a/drivers/net/wireless/cw1200/cw1200.h b/drivers/net/wireless/cw1200/cw1200.h new file mode 100644 index 000000000000..2aa17ca60ba8 --- /dev/null +++ b/drivers/net/wireless/cw1200/cw1200.h | |||
@@ -0,0 +1,332 @@ | |||
1 | /* | ||
2 | * Common private data for ST-Ericsson CW1200 drivers | ||
3 | * | ||
4 | * Copyright (c) 2010, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * Based on the mac80211 Prism54 code, which is | ||
8 | * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> | ||
9 | * | ||
10 | * Based on the islsm (softmac prism54) driver, which is: | ||
11 | * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License version 2 as | ||
15 | * published by the Free Software Foundation. | ||
16 | */ | ||
17 | |||
18 | #ifndef CW1200_H | ||
19 | #define CW1200_H | ||
20 | |||
21 | #include <linux/wait.h> | ||
22 | #include <linux/version.h> | ||
23 | #include <linux/mutex.h> | ||
24 | #include <linux/workqueue.h> | ||
25 | #include <net/mac80211.h> | ||
26 | |||
27 | #include "queue.h" | ||
28 | #include "wsm.h" | ||
29 | #include "scan.h" | ||
30 | #include "txrx.h" | ||
31 | #include "pm.h" | ||
32 | |||
33 | /* Forward declarations */ | ||
34 | struct sbus_ops; | ||
35 | struct task_struct; | ||
36 | struct cw1200_debug_priv; | ||
37 | struct firmware; | ||
38 | |||
39 | #ifdef CONFIG_CW1200_ETF | ||
40 | extern int etf_mode; | ||
41 | extern char *etf_firmware; | ||
42 | #endif | ||
43 | |||
44 | #define CW1200_MAX_CTRL_FRAME_LEN (0x1000) | ||
45 | |||
46 | #define CW1200_MAX_STA_IN_AP_MODE (5) | ||
47 | #define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1) | ||
48 | #define CW1200_LINK_ID_UAPSD (CW1200_MAX_STA_IN_AP_MODE + 2) | ||
49 | #define CW1200_LINK_ID_MAX (CW1200_MAX_STA_IN_AP_MODE + 3) | ||
50 | #define CW1200_MAX_REQUEUE_ATTEMPTS (5) | ||
51 | |||
52 | #define CW1200_MAX_TID (8) | ||
53 | |||
54 | #define CW1200_BLOCK_ACK_CNT (30) | ||
55 | #define CW1200_BLOCK_ACK_THLD (800) | ||
56 | #define CW1200_BLOCK_ACK_HIST (3) | ||
57 | #define CW1200_BLOCK_ACK_INTERVAL (1 * HZ / CW1200_BLOCK_ACK_HIST) | ||
58 | |||
59 | #define CW1200_JOIN_TIMEOUT (1 * HZ) | ||
60 | #define CW1200_AUTH_TIMEOUT (5 * HZ) | ||
61 | |||
62 | struct cw1200_ht_info { | ||
63 | struct ieee80211_sta_ht_cap ht_cap; | ||
64 | enum nl80211_channel_type channel_type; | ||
65 | u16 operation_mode; | ||
66 | }; | ||
67 | |||
68 | /* Please keep order */ | ||
69 | enum cw1200_join_status { | ||
70 | CW1200_JOIN_STATUS_PASSIVE = 0, | ||
71 | CW1200_JOIN_STATUS_MONITOR, | ||
72 | CW1200_JOIN_STATUS_JOINING, | ||
73 | CW1200_JOIN_STATUS_PRE_STA, | ||
74 | CW1200_JOIN_STATUS_STA, | ||
75 | CW1200_JOIN_STATUS_IBSS, | ||
76 | CW1200_JOIN_STATUS_AP, | ||
77 | }; | ||
78 | |||
79 | enum cw1200_link_status { | ||
80 | CW1200_LINK_OFF, | ||
81 | CW1200_LINK_RESERVE, | ||
82 | CW1200_LINK_SOFT, | ||
83 | CW1200_LINK_HARD, | ||
84 | CW1200_LINK_RESET, | ||
85 | CW1200_LINK_RESET_REMAP, | ||
86 | }; | ||
87 | |||
88 | extern int cw1200_power_mode; | ||
89 | extern const char * const cw1200_fw_types[]; | ||
90 | |||
91 | struct cw1200_link_entry { | ||
92 | unsigned long timestamp; | ||
93 | enum cw1200_link_status status; | ||
94 | enum cw1200_link_status prev_status; | ||
95 | u8 mac[ETH_ALEN]; | ||
96 | u8 buffered[CW1200_MAX_TID]; | ||
97 | struct sk_buff_head rx_queue; | ||
98 | }; | ||
99 | |||
100 | struct cw1200_common { | ||
101 | /* interfaces to the rest of the stack */ | ||
102 | struct ieee80211_hw *hw; | ||
103 | struct ieee80211_vif *vif; | ||
104 | struct device *pdev; | ||
105 | |||
106 | /* Statistics */ | ||
107 | struct ieee80211_low_level_stats stats; | ||
108 | |||
109 | /* Our macaddr */ | ||
110 | u8 mac_addr[ETH_ALEN]; | ||
111 | |||
112 | /* Hardware interface */ | ||
113 | const struct sbus_ops *sbus_ops; | ||
114 | struct sbus_priv *sbus_priv; | ||
115 | |||
116 | /* Hardware information */ | ||
117 | enum { | ||
118 | HIF_9000_SILICON_VERSATILE = 0, | ||
119 | HIF_8601_VERSATILE, | ||
120 | HIF_8601_SILICON, | ||
121 | } hw_type; | ||
122 | enum { | ||
123 | CW1200_HW_REV_CUT10 = 10, | ||
124 | CW1200_HW_REV_CUT11 = 11, | ||
125 | CW1200_HW_REV_CUT20 = 20, | ||
126 | CW1200_HW_REV_CUT22 = 22, | ||
127 | CW1X60_HW_REV = 40, | ||
128 | } hw_revision; | ||
129 | int hw_refclk; | ||
130 | bool hw_have_5ghz; | ||
131 | const struct firmware *sdd; | ||
132 | char *sdd_path; | ||
133 | |||
134 | struct cw1200_debug_priv *debug; | ||
135 | |||
136 | struct workqueue_struct *workqueue; | ||
137 | struct mutex conf_mutex; | ||
138 | |||
139 | struct cw1200_queue tx_queue[4]; | ||
140 | struct cw1200_queue_stats tx_queue_stats; | ||
141 | int tx_burst_idx; | ||
142 | |||
143 | /* firmware/hardware info */ | ||
144 | unsigned int tx_hdr_len; | ||
145 | |||
146 | /* Radio data */ | ||
147 | int output_power; | ||
148 | |||
149 | /* BBP/MAC state */ | ||
150 | struct ieee80211_rate *rates; | ||
151 | struct ieee80211_rate *mcs_rates; | ||
152 | struct ieee80211_channel *channel; | ||
153 | struct wsm_edca_params edca; | ||
154 | struct wsm_tx_queue_params tx_queue_params; | ||
155 | struct wsm_mib_association_mode association_mode; | ||
156 | struct wsm_set_bss_params bss_params; | ||
157 | struct cw1200_ht_info ht_info; | ||
158 | struct wsm_set_pm powersave_mode; | ||
159 | struct wsm_set_pm firmware_ps_mode; | ||
160 | int cqm_rssi_thold; | ||
161 | unsigned cqm_rssi_hyst; | ||
162 | bool cqm_use_rssi; | ||
163 | int cqm_beacon_loss_count; | ||
164 | int channel_switch_in_progress; | ||
165 | wait_queue_head_t channel_switch_done; | ||
166 | u8 long_frame_max_tx_count; | ||
167 | u8 short_frame_max_tx_count; | ||
168 | int mode; | ||
169 | bool enable_beacon; | ||
170 | int beacon_int; | ||
171 | bool listening; | ||
172 | struct wsm_rx_filter rx_filter; | ||
173 | struct wsm_mib_multicast_filter multicast_filter; | ||
174 | bool has_multicast_subscription; | ||
175 | bool disable_beacon_filter; | ||
176 | struct work_struct update_filtering_work; | ||
177 | struct work_struct set_beacon_wakeup_period_work; | ||
178 | |||
179 | u8 ba_rx_tid_mask; | ||
180 | u8 ba_tx_tid_mask; | ||
181 | |||
182 | struct cw1200_pm_state pm_state; | ||
183 | |||
184 | struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; | ||
185 | struct wsm_uapsd_info uapsd_info; | ||
186 | bool setbssparams_done; | ||
187 | bool bt_present; | ||
188 | u8 conf_listen_interval; | ||
189 | u32 listen_interval; | ||
190 | u32 erp_info; | ||
191 | u32 rts_threshold; | ||
192 | |||
193 | /* BH */ | ||
194 | atomic_t bh_rx; | ||
195 | atomic_t bh_tx; | ||
196 | atomic_t bh_term; | ||
197 | atomic_t bh_suspend; | ||
198 | |||
199 | struct workqueue_struct *bh_workqueue; | ||
200 | struct work_struct bh_work; | ||
201 | |||
202 | int bh_error; | ||
203 | wait_queue_head_t bh_wq; | ||
204 | wait_queue_head_t bh_evt_wq; | ||
205 | u8 buf_id_tx; | ||
206 | u8 buf_id_rx; | ||
207 | u8 wsm_rx_seq; | ||
208 | u8 wsm_tx_seq; | ||
209 | int hw_bufs_used; | ||
210 | bool powersave_enabled; | ||
211 | bool device_can_sleep; | ||
212 | |||
213 | /* Scan status */ | ||
214 | struct cw1200_scan scan; | ||
215 | /* Keep cw1200 awake (WUP = 1) 1 second after each scan to avoid | ||
216 | * FW issue with sleeping/waking up. */ | ||
217 | atomic_t recent_scan; | ||
218 | struct delayed_work clear_recent_scan_work; | ||
219 | |||
220 | /* WSM */ | ||
221 | struct wsm_startup_ind wsm_caps; | ||
222 | struct mutex wsm_cmd_mux; | ||
223 | struct wsm_buf wsm_cmd_buf; | ||
224 | struct wsm_cmd wsm_cmd; | ||
225 | wait_queue_head_t wsm_cmd_wq; | ||
226 | wait_queue_head_t wsm_startup_done; | ||
227 | int firmware_ready; | ||
228 | atomic_t tx_lock; | ||
229 | |||
230 | /* WSM debug */ | ||
231 | int wsm_enable_wsm_dumps; | ||
232 | |||
233 | /* WSM Join */ | ||
234 | enum cw1200_join_status join_status; | ||
235 | u32 pending_frame_id; | ||
236 | bool join_pending; | ||
237 | struct delayed_work join_timeout; | ||
238 | struct work_struct unjoin_work; | ||
239 | struct work_struct join_complete_work; | ||
240 | int join_complete_status; | ||
241 | int join_dtim_period; | ||
242 | bool delayed_unjoin; | ||
243 | |||
244 | /* TX/RX and security */ | ||
245 | s8 wep_default_key_id; | ||
246 | struct work_struct wep_key_work; | ||
247 | u32 key_map; | ||
248 | struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1]; | ||
249 | |||
250 | /* AP powersave */ | ||
251 | u32 link_id_map; | ||
252 | struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE]; | ||
253 | struct work_struct link_id_work; | ||
254 | struct delayed_work link_id_gc_work; | ||
255 | u32 sta_asleep_mask; | ||
256 | u32 pspoll_mask; | ||
257 | bool aid0_bit_set; | ||
258 | spinlock_t ps_state_lock; /* Protect power save state */ | ||
259 | bool buffered_multicasts; | ||
260 | bool tx_multicast; | ||
261 | struct work_struct set_tim_work; | ||
262 | struct work_struct set_cts_work; | ||
263 | struct work_struct multicast_start_work; | ||
264 | struct work_struct multicast_stop_work; | ||
265 | struct timer_list mcast_timeout; | ||
266 | |||
267 | /* WSM events and CQM implementation */ | ||
268 | spinlock_t event_queue_lock; /* Protect event queue */ | ||
269 | struct list_head event_queue; | ||
270 | struct work_struct event_handler; | ||
271 | |||
272 | struct delayed_work bss_loss_work; | ||
273 | spinlock_t bss_loss_lock; /* Protect BSS loss state */ | ||
274 | int bss_loss_state; | ||
275 | int bss_loss_confirm_id; | ||
276 | int delayed_link_loss; | ||
277 | struct work_struct bss_params_work; | ||
278 | |||
279 | /* TX rate policy cache */ | ||
280 | struct tx_policy_cache tx_policy_cache; | ||
281 | struct work_struct tx_policy_upload_work; | ||
282 | |||
283 | /* legacy PS mode switch in suspend */ | ||
284 | int ps_mode_switch_in_progress; | ||
285 | wait_queue_head_t ps_mode_switch_done; | ||
286 | |||
287 | /* Workaround for WFD testcase 6.1.10*/ | ||
288 | struct work_struct linkid_reset_work; | ||
289 | u8 action_frame_sa[ETH_ALEN]; | ||
290 | u8 action_linkid; | ||
291 | |||
292 | #ifdef CONFIG_CW1200_ETF | ||
293 | struct sk_buff_head etf_q; | ||
294 | #endif | ||
295 | }; | ||
296 | |||
297 | struct cw1200_sta_priv { | ||
298 | int link_id; | ||
299 | }; | ||
300 | |||
301 | /* interfaces for the drivers */ | ||
302 | int cw1200_core_probe(const struct sbus_ops *sbus_ops, | ||
303 | struct sbus_priv *sbus, | ||
304 | struct device *pdev, | ||
305 | struct cw1200_common **pself, | ||
306 | int ref_clk, const u8 *macaddr, | ||
307 | const char *sdd_path, bool have_5ghz); | ||
308 | void cw1200_core_release(struct cw1200_common *self); | ||
309 | |||
310 | #define FWLOAD_BLOCK_SIZE (1024) | ||
311 | |||
312 | static inline int cw1200_is_ht(const struct cw1200_ht_info *ht_info) | ||
313 | { | ||
314 | return ht_info->channel_type != NL80211_CHAN_NO_HT; | ||
315 | } | ||
316 | |||
317 | static inline int cw1200_ht_greenfield(const struct cw1200_ht_info *ht_info) | ||
318 | { | ||
319 | return cw1200_is_ht(ht_info) && | ||
320 | (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && | ||
321 | !(ht_info->operation_mode & | ||
322 | IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); | ||
323 | } | ||
324 | |||
325 | static inline int cw1200_ht_ampdu_density(const struct cw1200_ht_info *ht_info) | ||
326 | { | ||
327 | if (!cw1200_is_ht(ht_info)) | ||
328 | return 0; | ||
329 | return ht_info->ht_cap.ampdu_density; | ||
330 | } | ||
331 | |||
332 | #endif /* CW1200_H */ | ||
diff --git a/drivers/net/wireless/cw1200/cw1200_sagrad.c b/drivers/net/wireless/cw1200/cw1200_sagrad.c new file mode 100644 index 000000000000..a5ada0eda1dd --- /dev/null +++ b/drivers/net/wireless/cw1200/cw1200_sagrad.c | |||
@@ -0,0 +1,145 @@ | |||
1 | /* | ||
2 | * Platform glue data for ST-Ericsson CW1200 driver | ||
3 | * | ||
4 | * Copyright (c) 2013, Sagrad, Inc | ||
5 | * Author: Solomon Peachy <speachy@sagrad.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/cw1200_platform.h> | ||
14 | |||
15 | MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>"); | ||
16 | MODULE_DESCRIPTION("ST-Ericsson CW1200 Platform glue driver"); | ||
17 | MODULE_LICENSE("GPL"); | ||
18 | |||
19 | /* Define just one of these. Feel free to customize as needed */ | ||
20 | #define SAGRAD_1091_1098_EVK_SDIO | ||
21 | /* #define SAGRAD_1091_1098_EVK_SPI */ | ||
22 | |||
23 | #ifdef SAGRAD_1091_1098_EVK_SDIO | ||
24 | #if 0 | ||
25 | static struct resource cw1200_href_resources[] = { | ||
26 | { | ||
27 | .start = 215, /* fix me as appropriate */ | ||
28 | .end = 215, /* ditto */ | ||
29 | .flags = IORESOURCE_IO, | ||
30 | .name = "cw1200_wlan_reset", | ||
31 | }, | ||
32 | { | ||
33 | .start = 216, /* fix me as appropriate */ | ||
34 | .end = 216, /* ditto */ | ||
35 | .flags = IORESOURCE_IO, | ||
36 | .name = "cw1200_wlan_powerup", | ||
37 | }, | ||
38 | { | ||
39 | .start = NOMADIK_GPIO_TO_IRQ(216), /* fix me as appropriate */ | ||
40 | .end = NOMADIK_GPIO_TO_IRQ(216), /* ditto */ | ||
41 | .flags = IORESOURCE_IRQ, | ||
42 | .name = "cw1200_wlan_irq", | ||
43 | }, | ||
44 | }; | ||
45 | #endif | ||
46 | |||
47 | static int cw1200_power_ctrl(const struct cw1200_platform_data_sdio *pdata, | ||
48 | bool enable) | ||
49 | { | ||
50 | /* Control 3v3 and 1v8 to hardware as appropriate */ | ||
51 | /* Note this is not needed if it's controlled elsewhere or always on */ | ||
52 | |||
53 | /* May require delay for power to stabilize */ | ||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | static int cw1200_clk_ctrl(const struct cw1200_platform_data_sdio *pdata, | ||
58 | bool enable) | ||
59 | { | ||
60 | /* Turn CLK_32K off and on as appropriate. */ | ||
61 | /* Note this is not needed if it's always on */ | ||
62 | |||
63 | /* May require delay for clock to stabilize */ | ||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | static struct cw1200_platform_data_sdio cw1200_platform_data = { | ||
68 | .ref_clk = 38400, | ||
69 | .have_5ghz = false, | ||
70 | #if 0 | ||
71 | .reset = &cw1200_href_resources[0], | ||
72 | .powerup = &cw1200_href_resources[1], | ||
73 | .irq = &cw1200_href_resources[2], | ||
74 | #endif | ||
75 | .power_ctrl = cw1200_power_ctrl, | ||
76 | .clk_ctrl = cw1200_clk_ctrl, | ||
77 | /* .macaddr = ??? */ | ||
78 | .sdd_file = "sdd_sagrad_1091_1098.bin", | ||
79 | }; | ||
80 | #endif | ||
81 | |||
82 | #ifdef SAGRAD_1091_1098_EVK_SPI | ||
83 | /* Note that this is an example of integrating into your board support file */ | ||
84 | static struct resource cw1200_href_resources[] = { | ||
85 | { | ||
86 | .start = GPIO_RF_RESET, | ||
87 | .end = GPIO_RF_RESET, | ||
88 | .flags = IORESOURCE_IO, | ||
89 | .name = "cw1200_wlan_reset", | ||
90 | }, | ||
91 | { | ||
92 | .start = GPIO_RF_POWERUP, | ||
93 | .end = GPIO_RF_POWERUP, | ||
94 | .flags = IORESOURCE_IO, | ||
95 | .name = "cw1200_wlan_powerup", | ||
96 | }, | ||
97 | }; | ||
98 | |||
99 | static int cw1200_power_ctrl(const struct cw1200_platform_data_spi *pdata, | ||
100 | bool enable) | ||
101 | { | ||
102 | /* Control 3v3 and 1v8 to hardware as appropriate */ | ||
103 | /* Note this is not needed if it's controlled elsewhere or always on */ | ||
104 | |||
105 | /* May require delay for power to stabilize */ | ||
106 | return 0; | ||
107 | } | ||
108 | static int cw1200_clk_ctrl(const struct cw1200_platform_data_spi *pdata, | ||
109 | bool enable) | ||
110 | { | ||
111 | /* Turn CLK_32K off and on as appropriate. */ | ||
112 | /* Note this is not needed if it's always on */ | ||
113 | |||
114 | /* May require delay for clock to stabilize */ | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static struct cw1200_platform_data_spi cw1200_platform_data = { | ||
119 | .ref_clk = 38400, | ||
120 | .spi_bits_per_word = 16, | ||
121 | .reset = &cw1200_href_resources[0], | ||
122 | .powerup = &cw1200_href_resources[1], | ||
123 | .power_ctrl = cw1200_power_ctrl, | ||
124 | .clk_ctrl = cw1200_clk_ctrl, | ||
125 | /* .macaddr = ??? */ | ||
126 | .sdd_file = "sdd_sagrad_1091_1098.bin", | ||
127 | }; | ||
128 | static struct spi_board_info myboard_spi_devices[] __initdata = { | ||
129 | { | ||
130 | .modalias = "cw1200_wlan_spi", | ||
131 | .max_speed_hz = 10000000, /* 52MHz Max */ | ||
132 | .bus_num = 0, | ||
133 | .irq = WIFI_IRQ, | ||
134 | .platform_data = &cw1200_platform_data, | ||
135 | .chip_select = 0, | ||
136 | }, | ||
137 | }; | ||
138 | #endif | ||
139 | |||
140 | |||
141 | const void *cw1200_get_platform_data(void) | ||
142 | { | ||
143 | return &cw1200_platform_data; | ||
144 | } | ||
145 | EXPORT_SYMBOL_GPL(cw1200_get_platform_data); | ||
diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c new file mode 100644 index 000000000000..f6e22192d80a --- /dev/null +++ b/drivers/net/wireless/cw1200/cw1200_sdio.c | |||
@@ -0,0 +1,409 @@ | |||
1 | /* | ||
2 | * Mac80211 SDIO driver for ST-Ericsson CW1200 device | ||
3 | * | ||
4 | * Copyright (c) 2010, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/version.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/gpio.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/mmc/host.h> | ||
17 | #include <linux/mmc/sdio_func.h> | ||
18 | #include <linux/mmc/card.h> | ||
19 | #include <linux/mmc/sdio.h> | ||
20 | #include <net/mac80211.h> | ||
21 | |||
22 | #include "cw1200.h" | ||
23 | #include "sbus.h" | ||
24 | #include <linux/cw1200_platform.h> | ||
25 | #include "hwio.h" | ||
26 | |||
27 | MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>"); | ||
28 | MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver"); | ||
29 | MODULE_LICENSE("GPL"); | ||
30 | |||
31 | #define SDIO_BLOCK_SIZE (512) | ||
32 | |||
33 | struct sbus_priv { | ||
34 | struct sdio_func *func; | ||
35 | struct cw1200_common *core; | ||
36 | const struct cw1200_platform_data_sdio *pdata; | ||
37 | }; | ||
38 | |||
39 | #ifndef SDIO_VENDOR_ID_STE | ||
40 | #define SDIO_VENDOR_ID_STE 0x0020 | ||
41 | #endif | ||
42 | |||
43 | #ifndef SDIO_DEVICE_ID_STE_CW1200 | ||
44 | #define SDIO_DEVICE_ID_STE_CW1200 0x2280 | ||
45 | #endif | ||
46 | |||
47 | static const struct sdio_device_id cw1200_sdio_ids[] = { | ||
48 | { SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) }, | ||
49 | { /* end: all zeroes */ }, | ||
50 | }; | ||
51 | |||
52 | /* sbus_ops implemetation */ | ||
53 | |||
54 | static int cw1200_sdio_memcpy_fromio(struct sbus_priv *self, | ||
55 | unsigned int addr, | ||
56 | void *dst, int count) | ||
57 | { | ||
58 | return sdio_memcpy_fromio(self->func, dst, addr, count); | ||
59 | } | ||
60 | |||
61 | static int cw1200_sdio_memcpy_toio(struct sbus_priv *self, | ||
62 | unsigned int addr, | ||
63 | const void *src, int count) | ||
64 | { | ||
65 | return sdio_memcpy_toio(self->func, addr, (void *)src, count); | ||
66 | } | ||
67 | |||
68 | static void cw1200_sdio_lock(struct sbus_priv *self) | ||
69 | { | ||
70 | sdio_claim_host(self->func); | ||
71 | } | ||
72 | |||
73 | static void cw1200_sdio_unlock(struct sbus_priv *self) | ||
74 | { | ||
75 | sdio_release_host(self->func); | ||
76 | } | ||
77 | |||
78 | static void cw1200_sdio_irq_handler(struct sdio_func *func) | ||
79 | { | ||
80 | struct sbus_priv *self = sdio_get_drvdata(func); | ||
81 | |||
82 | /* note: sdio_host already claimed here. */ | ||
83 | if (self->core) | ||
84 | cw1200_irq_handler(self->core); | ||
85 | } | ||
86 | |||
87 | static irqreturn_t cw1200_gpio_hardirq(int irq, void *dev_id) | ||
88 | { | ||
89 | return IRQ_WAKE_THREAD; | ||
90 | } | ||
91 | |||
92 | static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id) | ||
93 | { | ||
94 | struct sbus_priv *self = dev_id; | ||
95 | |||
96 | if (self->core) { | ||
97 | sdio_claim_host(self->func); | ||
98 | cw1200_irq_handler(self->core); | ||
99 | sdio_release_host(self->func); | ||
100 | return IRQ_HANDLED; | ||
101 | } else { | ||
102 | return IRQ_NONE; | ||
103 | } | ||
104 | } | ||
105 | |||
106 | static int cw1200_request_irq(struct sbus_priv *self) | ||
107 | { | ||
108 | int ret; | ||
109 | const struct resource *irq = self->pdata->irq; | ||
110 | u8 cccr; | ||
111 | |||
112 | cccr = sdio_f0_readb(self->func, SDIO_CCCR_IENx, &ret); | ||
113 | if (WARN_ON(ret)) | ||
114 | goto err; | ||
115 | |||
116 | /* Master interrupt enable ... */ | ||
117 | cccr |= BIT(0); | ||
118 | |||
119 | /* ... for our function */ | ||
120 | cccr |= BIT(self->func->num); | ||
121 | |||
122 | sdio_f0_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret); | ||
123 | if (WARN_ON(ret)) | ||
124 | goto err; | ||
125 | |||
126 | ret = enable_irq_wake(irq->start); | ||
127 | if (WARN_ON(ret)) | ||
128 | goto err; | ||
129 | |||
130 | /* Request the IRQ */ | ||
131 | ret = request_threaded_irq(irq->start, cw1200_gpio_hardirq, | ||
132 | cw1200_gpio_irq, | ||
133 | IRQF_TRIGGER_HIGH | IRQF_ONESHOT, | ||
134 | irq->name, self); | ||
135 | if (WARN_ON(ret)) | ||
136 | goto err; | ||
137 | |||
138 | return 0; | ||
139 | |||
140 | err: | ||
141 | return ret; | ||
142 | } | ||
143 | |||
144 | static int cw1200_sdio_irq_subscribe(struct sbus_priv *self) | ||
145 | { | ||
146 | int ret = 0; | ||
147 | |||
148 | pr_debug("SW IRQ subscribe\n"); | ||
149 | sdio_claim_host(self->func); | ||
150 | if (self->pdata->irq) | ||
151 | ret = cw1200_request_irq(self); | ||
152 | else | ||
153 | ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler); | ||
154 | |||
155 | sdio_release_host(self->func); | ||
156 | return ret; | ||
157 | } | ||
158 | |||
159 | static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self) | ||
160 | { | ||
161 | int ret = 0; | ||
162 | |||
163 | pr_debug("SW IRQ unsubscribe\n"); | ||
164 | |||
165 | if (self->pdata->irq) { | ||
166 | disable_irq_wake(self->pdata->irq->start); | ||
167 | free_irq(self->pdata->irq->start, self); | ||
168 | } else { | ||
169 | sdio_claim_host(self->func); | ||
170 | ret = sdio_release_irq(self->func); | ||
171 | sdio_release_host(self->func); | ||
172 | } | ||
173 | return ret; | ||
174 | } | ||
175 | |||
176 | static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata) | ||
177 | { | ||
178 | const struct resource *reset = pdata->reset; | ||
179 | |||
180 | if (reset) { | ||
181 | gpio_set_value(reset->start, 0); | ||
182 | msleep(30); /* Min is 2 * CLK32K cycles */ | ||
183 | gpio_free(reset->start); | ||
184 | } | ||
185 | |||
186 | if (pdata->power_ctrl) | ||
187 | pdata->power_ctrl(pdata, false); | ||
188 | if (pdata->clk_ctrl) | ||
189 | pdata->clk_ctrl(pdata, false); | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata) | ||
195 | { | ||
196 | const struct resource *reset = pdata->reset; | ||
197 | const struct resource *powerup = pdata->reset; | ||
198 | |||
199 | /* Ensure I/Os are pulled low */ | ||
200 | if (reset) { | ||
201 | gpio_request(reset->start, reset->name); | ||
202 | gpio_direction_output(reset->start, 0); | ||
203 | } | ||
204 | if (powerup) { | ||
205 | gpio_request(powerup->start, powerup->name); | ||
206 | gpio_direction_output(powerup->start, 0); | ||
207 | } | ||
208 | if (reset || powerup) | ||
209 | msleep(50); /* Settle time */ | ||
210 | |||
211 | /* Enable 3v3 and 1v8 to hardware */ | ||
212 | if (pdata->power_ctrl) { | ||
213 | if (pdata->power_ctrl(pdata, true)) { | ||
214 | pr_err("power_ctrl() failed!\n"); | ||
215 | return -1; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | /* Enable CLK32K */ | ||
220 | if (pdata->clk_ctrl) { | ||
221 | if (pdata->clk_ctrl(pdata, true)) { | ||
222 | pr_err("clk_ctrl() failed!\n"); | ||
223 | return -1; | ||
224 | } | ||
225 | msleep(10); /* Delay until clock is stable for 2 cycles */ | ||
226 | } | ||
227 | |||
228 | /* Enable POWERUP signal */ | ||
229 | if (powerup) { | ||
230 | gpio_set_value(powerup->start, 1); | ||
231 | msleep(250); /* or more..? */ | ||
232 | } | ||
233 | /* Enable RSTn signal */ | ||
234 | if (reset) { | ||
235 | gpio_set_value(reset->start, 1); | ||
236 | msleep(50); /* Or more..? */ | ||
237 | } | ||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | static size_t cw1200_sdio_align_size(struct sbus_priv *self, size_t size) | ||
242 | { | ||
243 | if (self->pdata->no_nptb) | ||
244 | size = round_up(size, SDIO_BLOCK_SIZE); | ||
245 | else | ||
246 | size = sdio_align_size(self->func, size); | ||
247 | |||
248 | return size; | ||
249 | } | ||
250 | |||
251 | static int cw1200_sdio_pm(struct sbus_priv *self, bool suspend) | ||
252 | { | ||
253 | int ret = 0; | ||
254 | |||
255 | if (self->pdata->irq) | ||
256 | ret = irq_set_irq_wake(self->pdata->irq->start, suspend); | ||
257 | return ret; | ||
258 | } | ||
259 | |||
260 | static struct sbus_ops cw1200_sdio_sbus_ops = { | ||
261 | .sbus_memcpy_fromio = cw1200_sdio_memcpy_fromio, | ||
262 | .sbus_memcpy_toio = cw1200_sdio_memcpy_toio, | ||
263 | .lock = cw1200_sdio_lock, | ||
264 | .unlock = cw1200_sdio_unlock, | ||
265 | .align_size = cw1200_sdio_align_size, | ||
266 | .power_mgmt = cw1200_sdio_pm, | ||
267 | }; | ||
268 | |||
269 | /* Probe Function to be called by SDIO stack when device is discovered */ | ||
270 | static int cw1200_sdio_probe(struct sdio_func *func, | ||
271 | const struct sdio_device_id *id) | ||
272 | { | ||
273 | struct sbus_priv *self; | ||
274 | int status; | ||
275 | |||
276 | pr_info("cw1200_wlan_sdio: Probe called\n"); | ||
277 | |||
278 | /* We are only able to handle the wlan function */ | ||
279 | if (func->num != 0x01) | ||
280 | return -ENODEV; | ||
281 | |||
282 | self = kzalloc(sizeof(*self), GFP_KERNEL); | ||
283 | if (!self) { | ||
284 | pr_err("Can't allocate SDIO sbus_priv.\n"); | ||
285 | return -ENOMEM; | ||
286 | } | ||
287 | |||
288 | func->card->quirks |= MMC_QUIRK_LENIENT_FN0; | ||
289 | |||
290 | self->pdata = cw1200_get_platform_data(); | ||
291 | self->func = func; | ||
292 | sdio_set_drvdata(func, self); | ||
293 | sdio_claim_host(func); | ||
294 | sdio_enable_func(func); | ||
295 | sdio_release_host(func); | ||
296 | |||
297 | status = cw1200_sdio_irq_subscribe(self); | ||
298 | |||
299 | status = cw1200_core_probe(&cw1200_sdio_sbus_ops, | ||
300 | self, &func->dev, &self->core, | ||
301 | self->pdata->ref_clk, | ||
302 | self->pdata->macaddr, | ||
303 | self->pdata->sdd_file, | ||
304 | self->pdata->have_5ghz); | ||
305 | if (status) { | ||
306 | cw1200_sdio_irq_unsubscribe(self); | ||
307 | sdio_claim_host(func); | ||
308 | sdio_disable_func(func); | ||
309 | sdio_release_host(func); | ||
310 | sdio_set_drvdata(func, NULL); | ||
311 | kfree(self); | ||
312 | } | ||
313 | |||
314 | return status; | ||
315 | } | ||
316 | |||
317 | /* Disconnect Function to be called by SDIO stack when | ||
318 | * device is disconnected */ | ||
319 | static void cw1200_sdio_disconnect(struct sdio_func *func) | ||
320 | { | ||
321 | struct sbus_priv *self = sdio_get_drvdata(func); | ||
322 | |||
323 | if (self) { | ||
324 | cw1200_sdio_irq_unsubscribe(self); | ||
325 | if (self->core) { | ||
326 | cw1200_core_release(self->core); | ||
327 | self->core = NULL; | ||
328 | } | ||
329 | sdio_claim_host(func); | ||
330 | sdio_disable_func(func); | ||
331 | sdio_release_host(func); | ||
332 | sdio_set_drvdata(func, NULL); | ||
333 | kfree(self); | ||
334 | } | ||
335 | } | ||
336 | |||
337 | static int cw1200_sdio_suspend(struct device *dev) | ||
338 | { | ||
339 | int ret; | ||
340 | struct sdio_func *func = dev_to_sdio_func(dev); | ||
341 | struct sbus_priv *self = sdio_get_drvdata(func); | ||
342 | |||
343 | if (!cw1200_can_suspend(self->core)) | ||
344 | return -EAGAIN; | ||
345 | |||
346 | /* Notify SDIO that CW1200 will remain powered during suspend */ | ||
347 | ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); | ||
348 | if (ret) | ||
349 | pr_err("Error setting SDIO pm flags: %i\n", ret); | ||
350 | |||
351 | return ret; | ||
352 | } | ||
353 | |||
354 | static int cw1200_sdio_resume(struct device *dev) | ||
355 | { | ||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | static const struct dev_pm_ops cw1200_pm_ops = { | ||
360 | .suspend = cw1200_sdio_suspend, | ||
361 | .resume = cw1200_sdio_resume, | ||
362 | }; | ||
363 | |||
364 | static struct sdio_driver sdio_driver = { | ||
365 | .name = "cw1200_wlan_sdio", | ||
366 | .id_table = cw1200_sdio_ids, | ||
367 | .probe = cw1200_sdio_probe, | ||
368 | .remove = cw1200_sdio_disconnect, | ||
369 | .drv = { | ||
370 | .pm = &cw1200_pm_ops, | ||
371 | } | ||
372 | }; | ||
373 | |||
374 | /* Init Module function -> Called by insmod */ | ||
375 | static int __init cw1200_sdio_init(void) | ||
376 | { | ||
377 | const struct cw1200_platform_data_sdio *pdata; | ||
378 | int ret; | ||
379 | |||
380 | pdata = cw1200_get_platform_data(); | ||
381 | |||
382 | if (cw1200_sdio_on(pdata)) { | ||
383 | ret = -1; | ||
384 | goto err; | ||
385 | } | ||
386 | |||
387 | ret = sdio_register_driver(&sdio_driver); | ||
388 | if (ret) | ||
389 | goto err; | ||
390 | |||
391 | return 0; | ||
392 | |||
393 | err: | ||
394 | cw1200_sdio_off(pdata); | ||
395 | return ret; | ||
396 | } | ||
397 | |||
398 | /* Called at Driver Unloading */ | ||
399 | static void __exit cw1200_sdio_exit(void) | ||
400 | { | ||
401 | const struct cw1200_platform_data_sdio *pdata; | ||
402 | pdata = cw1200_get_platform_data(); | ||
403 | sdio_unregister_driver(&sdio_driver); | ||
404 | cw1200_sdio_off(pdata); | ||
405 | } | ||
406 | |||
407 | |||
408 | module_init(cw1200_sdio_init); | ||
409 | module_exit(cw1200_sdio_exit); | ||
diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c new file mode 100644 index 000000000000..04af68534854 --- /dev/null +++ b/drivers/net/wireless/cw1200/cw1200_spi.c | |||
@@ -0,0 +1,480 @@ | |||
1 | /* | ||
2 | * Mac80211 SPI driver for ST-Ericsson CW1200 device | ||
3 | * | ||
4 | * Copyright (c) 2011, Sagrad Inc. | ||
5 | * Author: Solomon Peachy <speachy@sagrad.com> | ||
6 | * | ||
7 | * Based on cw1200_sdio.c | ||
8 | * Copyright (c) 2010, ST-Ericsson | ||
9 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | */ | ||
15 | |||
16 | #include <linux/version.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/gpio.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/spinlock.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <net/mac80211.h> | ||
23 | |||
24 | #include <linux/spi/spi.h> | ||
25 | #include <linux/device.h> | ||
26 | |||
27 | #include "cw1200.h" | ||
28 | #include "sbus.h" | ||
29 | #include <linux/cw1200_platform.h> | ||
30 | #include "hwio.h" | ||
31 | |||
32 | MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>"); | ||
33 | MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver"); | ||
34 | MODULE_LICENSE("GPL"); | ||
35 | MODULE_ALIAS("spi:cw1200_wlan_spi"); | ||
36 | |||
37 | /* #define SPI_DEBUG */ | ||
38 | |||
39 | struct sbus_priv { | ||
40 | struct spi_device *func; | ||
41 | struct cw1200_common *core; | ||
42 | const struct cw1200_platform_data_spi *pdata; | ||
43 | spinlock_t lock; /* Serialize all bus operations */ | ||
44 | int claimed; | ||
45 | }; | ||
46 | |||
47 | #define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2) | ||
48 | #define SET_WRITE 0x7FFF /* usage: and operation */ | ||
49 | #define SET_READ 0x8000 /* usage: or operation */ | ||
50 | |||
51 | /* | ||
52 | Notes on byte ordering: | ||
53 | LE: B0 B1 B2 B3 | ||
54 | BE: B3 B2 B1 B0 | ||
55 | |||
56 | Hardware expects 32-bit data to be written as 16-bit BE words: | ||
57 | |||
58 | B1 B0 B3 B2 | ||
59 | |||
60 | */ | ||
61 | |||
62 | static int cw1200_spi_memcpy_fromio(struct sbus_priv *self, | ||
63 | unsigned int addr, | ||
64 | void *dst, int count) | ||
65 | { | ||
66 | int ret, i; | ||
67 | uint16_t regaddr; | ||
68 | struct spi_message m; | ||
69 | |||
70 | struct spi_transfer t_addr = { | ||
71 | .tx_buf = ®addr, | ||
72 | .len = sizeof(regaddr), | ||
73 | }; | ||
74 | struct spi_transfer t_msg = { | ||
75 | .rx_buf = dst, | ||
76 | .len = count, | ||
77 | }; | ||
78 | |||
79 | regaddr = (SDIO_TO_SPI_ADDR(addr))<<12; | ||
80 | regaddr |= SET_READ; | ||
81 | regaddr |= (count>>1); | ||
82 | regaddr = cpu_to_le16(regaddr); | ||
83 | |||
84 | #ifdef SPI_DEBUG | ||
85 | pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr, | ||
86 | le16_to_cpu(regaddr)); | ||
87 | #endif | ||
88 | |||
89 | #if defined(__LITTLE_ENDIAN) | ||
90 | /* We have to byteswap if the SPI bus is limited to 8b operation */ | ||
91 | if (self->func->bits_per_word == 8) | ||
92 | #endif | ||
93 | regaddr = swab16(regaddr); | ||
94 | |||
95 | spi_message_init(&m); | ||
96 | spi_message_add_tail(&t_addr, &m); | ||
97 | spi_message_add_tail(&t_msg, &m); | ||
98 | ret = spi_sync(self->func, &m); | ||
99 | |||
100 | #ifdef SPI_DEBUG | ||
101 | pr_info("READ : "); | ||
102 | for (i = 0; i < t_addr.len; i++) | ||
103 | printk("%02x ", ((u8 *)t_addr.tx_buf)[i]); | ||
104 | printk(" : "); | ||
105 | for (i = 0; i < t_msg.len; i++) | ||
106 | printk("%02x ", ((u8 *)t_msg.rx_buf)[i]); | ||
107 | printk("\n"); | ||
108 | #endif | ||
109 | |||
110 | #if defined(__LITTLE_ENDIAN) | ||
111 | /* We have to byteswap if the SPI bus is limited to 8b operation */ | ||
112 | if (self->func->bits_per_word == 8) | ||
113 | #endif | ||
114 | { | ||
115 | uint16_t *buf = (uint16_t *)dst; | ||
116 | for (i = 0; i < ((count + 1) >> 1); i++) | ||
117 | buf[i] = swab16(buf[i]); | ||
118 | } | ||
119 | |||
120 | return ret; | ||
121 | } | ||
122 | |||
123 | static int cw1200_spi_memcpy_toio(struct sbus_priv *self, | ||
124 | unsigned int addr, | ||
125 | const void *src, int count) | ||
126 | { | ||
127 | int rval, i; | ||
128 | uint16_t regaddr; | ||
129 | struct spi_transfer t_addr = { | ||
130 | .tx_buf = ®addr, | ||
131 | .len = sizeof(regaddr), | ||
132 | }; | ||
133 | struct spi_transfer t_msg = { | ||
134 | .tx_buf = src, | ||
135 | .len = count, | ||
136 | }; | ||
137 | struct spi_message m; | ||
138 | |||
139 | regaddr = (SDIO_TO_SPI_ADDR(addr))<<12; | ||
140 | regaddr &= SET_WRITE; | ||
141 | regaddr |= (count>>1); | ||
142 | regaddr = cpu_to_le16(regaddr); | ||
143 | |||
144 | #ifdef SPI_DEBUG | ||
145 | pr_info("WRITE: %04d to 0x%02x (%04x)\n", count, addr, | ||
146 | le16_to_cpu(regaddr)); | ||
147 | #endif | ||
148 | |||
149 | #if defined(__LITTLE_ENDIAN) | ||
150 | /* We have to byteswap if the SPI bus is limited to 8b operation */ | ||
151 | if (self->func->bits_per_word == 8) | ||
152 | #endif | ||
153 | { | ||
154 | uint16_t *buf = (uint16_t *)src; | ||
155 | regaddr = swab16(regaddr); | ||
156 | for (i = 0; i < ((count + 1) >> 1); i++) | ||
157 | buf[i] = swab16(buf[i]); | ||
158 | } | ||
159 | |||
160 | #ifdef SPI_DEBUG | ||
161 | pr_info("WRITE: "); | ||
162 | for (i = 0; i < t_addr.len; i++) | ||
163 | printk("%02x ", ((u8 *)t_addr.tx_buf)[i]); | ||
164 | printk(" : "); | ||
165 | for (i = 0; i < t_msg.len; i++) | ||
166 | printk("%02x ", ((u8 *)t_msg.tx_buf)[i]); | ||
167 | printk("\n"); | ||
168 | #endif | ||
169 | |||
170 | spi_message_init(&m); | ||
171 | spi_message_add_tail(&t_addr, &m); | ||
172 | spi_message_add_tail(&t_msg, &m); | ||
173 | rval = spi_sync(self->func, &m); | ||
174 | |||
175 | #ifdef SPI_DEBUG | ||
176 | pr_info("WROTE: %d\n", m.actual_length); | ||
177 | #endif | ||
178 | |||
179 | #if defined(__LITTLE_ENDIAN) | ||
180 | /* We have to byteswap if the SPI bus is limited to 8b operation */ | ||
181 | if (self->func->bits_per_word == 8) | ||
182 | #endif | ||
183 | { | ||
184 | uint16_t *buf = (uint16_t *)src; | ||
185 | for (i = 0; i < ((count + 1) >> 1); i++) | ||
186 | buf[i] = swab16(buf[i]); | ||
187 | } | ||
188 | return rval; | ||
189 | } | ||
190 | |||
191 | static void cw1200_spi_lock(struct sbus_priv *self) | ||
192 | { | ||
193 | unsigned long flags; | ||
194 | |||
195 | might_sleep(); | ||
196 | |||
197 | spin_lock_irqsave(&self->lock, flags); | ||
198 | while (1) { | ||
199 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
200 | if (!self->claimed) | ||
201 | break; | ||
202 | spin_unlock_irqrestore(&self->lock, flags); | ||
203 | schedule(); | ||
204 | spin_lock_irqsave(&self->lock, flags); | ||
205 | } | ||
206 | set_current_state(TASK_RUNNING); | ||
207 | self->claimed = 1; | ||
208 | spin_unlock_irqrestore(&self->lock, flags); | ||
209 | |||
210 | return; | ||
211 | } | ||
212 | |||
213 | static void cw1200_spi_unlock(struct sbus_priv *self) | ||
214 | { | ||
215 | unsigned long flags; | ||
216 | |||
217 | spin_lock_irqsave(&self->lock, flags); | ||
218 | self->claimed = 0; | ||
219 | spin_unlock_irqrestore(&self->lock, flags); | ||
220 | return; | ||
221 | } | ||
222 | |||
223 | static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id) | ||
224 | { | ||
225 | struct sbus_priv *self = dev_id; | ||
226 | |||
227 | if (self->core) { | ||
228 | cw1200_irq_handler(self->core); | ||
229 | return IRQ_HANDLED; | ||
230 | } else { | ||
231 | return IRQ_NONE; | ||
232 | } | ||
233 | } | ||
234 | |||
235 | static int cw1200_spi_irq_subscribe(struct sbus_priv *self) | ||
236 | { | ||
237 | int ret; | ||
238 | |||
239 | pr_debug("SW IRQ subscribe\n"); | ||
240 | |||
241 | ret = request_any_context_irq(self->func->irq, cw1200_spi_irq_handler, | ||
242 | IRQF_TRIGGER_HIGH, | ||
243 | "cw1200_wlan_irq", self); | ||
244 | if (WARN_ON(ret < 0)) | ||
245 | goto exit; | ||
246 | |||
247 | ret = enable_irq_wake(self->func->irq); | ||
248 | if (WARN_ON(ret)) | ||
249 | goto free_irq; | ||
250 | |||
251 | return 0; | ||
252 | |||
253 | free_irq: | ||
254 | free_irq(self->func->irq, self); | ||
255 | exit: | ||
256 | return ret; | ||
257 | } | ||
258 | |||
259 | static int cw1200_spi_irq_unsubscribe(struct sbus_priv *self) | ||
260 | { | ||
261 | int ret = 0; | ||
262 | |||
263 | pr_debug("SW IRQ unsubscribe\n"); | ||
264 | disable_irq_wake(self->func->irq); | ||
265 | free_irq(self->func->irq, self); | ||
266 | |||
267 | return ret; | ||
268 | } | ||
269 | |||
270 | static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata) | ||
271 | { | ||
272 | const struct resource *reset = pdata->reset; | ||
273 | |||
274 | if (reset) { | ||
275 | gpio_set_value(reset->start, 0); | ||
276 | msleep(30); /* Min is 2 * CLK32K cycles */ | ||
277 | gpio_free(reset->start); | ||
278 | } | ||
279 | |||
280 | if (pdata->power_ctrl) | ||
281 | pdata->power_ctrl(pdata, false); | ||
282 | if (pdata->clk_ctrl) | ||
283 | pdata->clk_ctrl(pdata, false); | ||
284 | |||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata) | ||
289 | { | ||
290 | const struct resource *reset = pdata->reset; | ||
291 | const struct resource *powerup = pdata->reset; | ||
292 | |||
293 | /* Ensure I/Os are pulled low */ | ||
294 | if (reset) { | ||
295 | gpio_request(reset->start, reset->name); | ||
296 | gpio_direction_output(reset->start, 0); | ||
297 | } | ||
298 | if (powerup) { | ||
299 | gpio_request(powerup->start, powerup->name); | ||
300 | gpio_direction_output(powerup->start, 0); | ||
301 | } | ||
302 | if (reset || powerup) | ||
303 | msleep(10); /* Settle time? */ | ||
304 | |||
305 | /* Enable 3v3 and 1v8 to hardware */ | ||
306 | if (pdata->power_ctrl) { | ||
307 | if (pdata->power_ctrl(pdata, true)) { | ||
308 | pr_err("power_ctrl() failed!\n"); | ||
309 | return -1; | ||
310 | } | ||
311 | } | ||
312 | |||
313 | /* Enable CLK32K */ | ||
314 | if (pdata->clk_ctrl) { | ||
315 | if (pdata->clk_ctrl(pdata, true)) { | ||
316 | pr_err("clk_ctrl() failed!\n"); | ||
317 | return -1; | ||
318 | } | ||
319 | msleep(10); /* Delay until clock is stable for 2 cycles */ | ||
320 | } | ||
321 | |||
322 | /* Enable POWERUP signal */ | ||
323 | if (powerup) { | ||
324 | gpio_set_value(powerup->start, 1); | ||
325 | msleep(250); /* or more..? */ | ||
326 | } | ||
327 | /* Enable RSTn signal */ | ||
328 | if (reset) { | ||
329 | gpio_set_value(reset->start, 1); | ||
330 | msleep(50); /* Or more..? */ | ||
331 | } | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | static size_t cw1200_spi_align_size(struct sbus_priv *self, size_t size) | ||
336 | { | ||
337 | return size & 1 ? size + 1 : size; | ||
338 | } | ||
339 | |||
340 | static int cw1200_spi_pm(struct sbus_priv *self, bool suspend) | ||
341 | { | ||
342 | return irq_set_irq_wake(self->func->irq, suspend); | ||
343 | } | ||
344 | |||
345 | static struct sbus_ops cw1200_spi_sbus_ops = { | ||
346 | .sbus_memcpy_fromio = cw1200_spi_memcpy_fromio, | ||
347 | .sbus_memcpy_toio = cw1200_spi_memcpy_toio, | ||
348 | .lock = cw1200_spi_lock, | ||
349 | .unlock = cw1200_spi_unlock, | ||
350 | .align_size = cw1200_spi_align_size, | ||
351 | .power_mgmt = cw1200_spi_pm, | ||
352 | }; | ||
353 | |||
354 | /* Probe Function to be called by SPI stack when device is discovered */ | ||
355 | static int cw1200_spi_probe(struct spi_device *func) | ||
356 | { | ||
357 | const struct cw1200_platform_data_spi *plat_data = | ||
358 | func->dev.platform_data; | ||
359 | struct sbus_priv *self; | ||
360 | int status; | ||
361 | |||
362 | /* Sanity check speed */ | ||
363 | if (func->max_speed_hz > 52000000) | ||
364 | func->max_speed_hz = 52000000; | ||
365 | if (func->max_speed_hz < 1000000) | ||
366 | func->max_speed_hz = 1000000; | ||
367 | |||
368 | /* Fix up transfer size */ | ||
369 | if (plat_data->spi_bits_per_word) | ||
370 | func->bits_per_word = plat_data->spi_bits_per_word; | ||
371 | if (!func->bits_per_word) | ||
372 | func->bits_per_word = 16; | ||
373 | |||
374 | /* And finally.. */ | ||
375 | func->mode = SPI_MODE_0; | ||
376 | |||
377 | pr_info("cw1200_wlan_spi: Probe called (CS %d M %d BPW %d CLK %d)\n", | ||
378 | func->chip_select, func->mode, func->bits_per_word, | ||
379 | func->max_speed_hz); | ||
380 | |||
381 | if (cw1200_spi_on(plat_data)) { | ||
382 | pr_err("spi_on() failed!\n"); | ||
383 | return -1; | ||
384 | } | ||
385 | |||
386 | if (spi_setup(func)) { | ||
387 | pr_err("spi_setup() failed!\n"); | ||
388 | return -1; | ||
389 | } | ||
390 | |||
391 | self = kzalloc(sizeof(*self), GFP_KERNEL); | ||
392 | if (!self) { | ||
393 | pr_err("Can't allocate SPI sbus_priv."); | ||
394 | return -ENOMEM; | ||
395 | } | ||
396 | |||
397 | self->pdata = plat_data; | ||
398 | self->func = func; | ||
399 | spin_lock_init(&self->lock); | ||
400 | |||
401 | spi_set_drvdata(func, self); | ||
402 | |||
403 | status = cw1200_spi_irq_subscribe(self); | ||
404 | |||
405 | status = cw1200_core_probe(&cw1200_spi_sbus_ops, | ||
406 | self, &func->dev, &self->core, | ||
407 | self->pdata->ref_clk, | ||
408 | self->pdata->macaddr, | ||
409 | self->pdata->sdd_file, | ||
410 | self->pdata->have_5ghz); | ||
411 | |||
412 | if (status) { | ||
413 | cw1200_spi_irq_unsubscribe(self); | ||
414 | cw1200_spi_off(plat_data); | ||
415 | kfree(self); | ||
416 | } | ||
417 | |||
418 | return status; | ||
419 | } | ||
420 | |||
421 | /* Disconnect Function to be called by SPI stack when device is disconnected */ | ||
422 | static int cw1200_spi_disconnect(struct spi_device *func) | ||
423 | { | ||
424 | struct sbus_priv *self = spi_get_drvdata(func); | ||
425 | |||
426 | if (self) { | ||
427 | cw1200_spi_irq_unsubscribe(self); | ||
428 | if (self->core) { | ||
429 | cw1200_core_release(self->core); | ||
430 | self->core = NULL; | ||
431 | } | ||
432 | kfree(self); | ||
433 | } | ||
434 | cw1200_spi_off(func->dev.platform_data); | ||
435 | |||
436 | return 0; | ||
437 | } | ||
438 | |||
439 | static int cw1200_spi_suspend(struct device *dev, pm_message_t state) | ||
440 | { | ||
441 | struct sbus_priv *self = spi_get_drvdata(to_spi_device(dev)); | ||
442 | |||
443 | if (!cw1200_can_suspend(self->core)) | ||
444 | return -EAGAIN; | ||
445 | |||
446 | /* XXX notify host that we have to keep CW1200 powered on? */ | ||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | static int cw1200_spi_resume(struct device *dev) | ||
451 | { | ||
452 | return 0; | ||
453 | } | ||
454 | |||
455 | static struct spi_driver spi_driver = { | ||
456 | .probe = cw1200_spi_probe, | ||
457 | .remove = cw1200_spi_disconnect, | ||
458 | .driver = { | ||
459 | .name = "cw1200_wlan_spi", | ||
460 | .bus = &spi_bus_type, | ||
461 | .owner = THIS_MODULE, | ||
462 | .suspend = cw1200_spi_suspend, | ||
463 | .resume = cw1200_spi_resume, | ||
464 | }, | ||
465 | }; | ||
466 | |||
467 | /* Init Module function -> Called by insmod */ | ||
468 | static int __init cw1200_spi_init(void) | ||
469 | { | ||
470 | return spi_register_driver(&spi_driver); | ||
471 | } | ||
472 | |||
473 | /* Called at Driver Unloading */ | ||
474 | static void __exit cw1200_spi_exit(void) | ||
475 | { | ||
476 | spi_unregister_driver(&spi_driver); | ||
477 | } | ||
478 | |||
479 | module_init(cw1200_spi_init); | ||
480 | module_exit(cw1200_spi_exit); | ||
diff --git a/drivers/net/wireless/cw1200/debug.c b/drivers/net/wireless/cw1200/debug.c new file mode 100644 index 000000000000..b815181802e0 --- /dev/null +++ b/drivers/net/wireless/cw1200/debug.c | |||
@@ -0,0 +1,664 @@ | |||
1 | /* | ||
2 | * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers | ||
3 | * DebugFS code | ||
4 | * | ||
5 | * Copyright (c) 2010, ST-Ericsson | ||
6 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/debugfs.h> | ||
15 | #include <linux/seq_file.h> | ||
16 | #include "cw1200.h" | ||
17 | #include "debug.h" | ||
18 | #include "fwio.h" | ||
19 | |||
20 | /* join_status */ | ||
21 | static const char * const cw1200_debug_join_status[] = { | ||
22 | "passive", | ||
23 | "monitor", | ||
24 | "station (joining)", | ||
25 | "station (not authenticated yet)", | ||
26 | "station", | ||
27 | "adhoc", | ||
28 | "access point", | ||
29 | }; | ||
30 | |||
31 | /* WSM_JOIN_PREAMBLE_... */ | ||
32 | static const char * const cw1200_debug_preamble[] = { | ||
33 | "long", | ||
34 | "short", | ||
35 | "long on 1 and 2 Mbps", | ||
36 | }; | ||
37 | |||
38 | |||
39 | static const char * const cw1200_debug_link_id[] = { | ||
40 | "OFF", | ||
41 | "REQ", | ||
42 | "SOFT", | ||
43 | "HARD", | ||
44 | }; | ||
45 | |||
46 | static const char *cw1200_debug_mode(int mode) | ||
47 | { | ||
48 | switch (mode) { | ||
49 | case NL80211_IFTYPE_UNSPECIFIED: | ||
50 | return "unspecified"; | ||
51 | case NL80211_IFTYPE_MONITOR: | ||
52 | return "monitor"; | ||
53 | case NL80211_IFTYPE_STATION: | ||
54 | return "station"; | ||
55 | case NL80211_IFTYPE_ADHOC: | ||
56 | return "adhoc"; | ||
57 | case NL80211_IFTYPE_MESH_POINT: | ||
58 | return "mesh point"; | ||
59 | case NL80211_IFTYPE_AP: | ||
60 | return "access point"; | ||
61 | case NL80211_IFTYPE_P2P_CLIENT: | ||
62 | return "p2p client"; | ||
63 | case NL80211_IFTYPE_P2P_GO: | ||
64 | return "p2p go"; | ||
65 | default: | ||
66 | return "unsupported"; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | static void cw1200_queue_status_show(struct seq_file *seq, | ||
71 | struct cw1200_queue *q) | ||
72 | { | ||
73 | int i; | ||
74 | seq_printf(seq, "Queue %d:\n", q->queue_id); | ||
75 | seq_printf(seq, " capacity: %zu\n", q->capacity); | ||
76 | seq_printf(seq, " queued: %zu\n", q->num_queued); | ||
77 | seq_printf(seq, " pending: %zu\n", q->num_pending); | ||
78 | seq_printf(seq, " sent: %zu\n", q->num_sent); | ||
79 | seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no"); | ||
80 | seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no"); | ||
81 | seq_puts(seq, " link map: 0-> "); | ||
82 | for (i = 0; i < q->stats->map_capacity; ++i) | ||
83 | seq_printf(seq, "%.2d ", q->link_map_cache[i]); | ||
84 | seq_printf(seq, "<-%zu\n", q->stats->map_capacity); | ||
85 | } | ||
86 | |||
87 | static void cw1200_debug_print_map(struct seq_file *seq, | ||
88 | struct cw1200_common *priv, | ||
89 | const char *label, | ||
90 | u32 map) | ||
91 | { | ||
92 | int i; | ||
93 | seq_printf(seq, "%s0-> ", label); | ||
94 | for (i = 0; i < priv->tx_queue_stats.map_capacity; ++i) | ||
95 | seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : ".."); | ||
96 | seq_printf(seq, "<-%zu\n", priv->tx_queue_stats.map_capacity - 1); | ||
97 | } | ||
98 | |||
99 | static int cw1200_status_show(struct seq_file *seq, void *v) | ||
100 | { | ||
101 | int i; | ||
102 | struct list_head *item; | ||
103 | struct cw1200_common *priv = seq->private; | ||
104 | struct cw1200_debug_priv *d = priv->debug; | ||
105 | |||
106 | seq_puts(seq, "CW1200 Wireless LAN driver status\n"); | ||
107 | seq_printf(seq, "Hardware: %d.%d\n", | ||
108 | priv->wsm_caps.hw_id, | ||
109 | priv->wsm_caps.hw_subid); | ||
110 | seq_printf(seq, "Firmware: %s %d.%d\n", | ||
111 | cw1200_fw_types[priv->wsm_caps.fw_type], | ||
112 | priv->wsm_caps.fw_ver, | ||
113 | priv->wsm_caps.fw_build); | ||
114 | seq_printf(seq, "FW API: %d\n", | ||
115 | priv->wsm_caps.fw_api); | ||
116 | seq_printf(seq, "FW caps: 0x%.4X\n", | ||
117 | priv->wsm_caps.fw_cap); | ||
118 | seq_printf(seq, "FW label: '%s'\n", | ||
119 | priv->wsm_caps.fw_label); | ||
120 | seq_printf(seq, "Mode: %s%s\n", | ||
121 | cw1200_debug_mode(priv->mode), | ||
122 | priv->listening ? " (listening)" : ""); | ||
123 | seq_printf(seq, "Join state: %s\n", | ||
124 | cw1200_debug_join_status[priv->join_status]); | ||
125 | if (priv->channel) | ||
126 | seq_printf(seq, "Channel: %d%s\n", | ||
127 | priv->channel->hw_value, | ||
128 | priv->channel_switch_in_progress ? | ||
129 | " (switching)" : ""); | ||
130 | if (priv->rx_filter.promiscuous) | ||
131 | seq_puts(seq, "Filter: promisc\n"); | ||
132 | else if (priv->rx_filter.fcs) | ||
133 | seq_puts(seq, "Filter: fcs\n"); | ||
134 | if (priv->rx_filter.bssid) | ||
135 | seq_puts(seq, "Filter: bssid\n"); | ||
136 | if (!priv->disable_beacon_filter) | ||
137 | seq_puts(seq, "Filter: beacons\n"); | ||
138 | |||
139 | if (priv->enable_beacon || | ||
140 | priv->mode == NL80211_IFTYPE_AP || | ||
141 | priv->mode == NL80211_IFTYPE_ADHOC || | ||
142 | priv->mode == NL80211_IFTYPE_MESH_POINT || | ||
143 | priv->mode == NL80211_IFTYPE_P2P_GO) | ||
144 | seq_printf(seq, "Beaconing: %s\n", | ||
145 | priv->enable_beacon ? | ||
146 | "enabled" : "disabled"); | ||
147 | |||
148 | for (i = 0; i < 4; ++i) | ||
149 | seq_printf(seq, "EDCA(%d): %d, %d, %d, %d, %d\n", i, | ||
150 | priv->edca.params[i].cwmin, | ||
151 | priv->edca.params[i].cwmax, | ||
152 | priv->edca.params[i].aifns, | ||
153 | priv->edca.params[i].txop_limit, | ||
154 | priv->edca.params[i].max_rx_lifetime); | ||
155 | |||
156 | if (priv->join_status == CW1200_JOIN_STATUS_STA) { | ||
157 | static const char *pm_mode = "unknown"; | ||
158 | switch (priv->powersave_mode.mode) { | ||
159 | case WSM_PSM_ACTIVE: | ||
160 | pm_mode = "off"; | ||
161 | break; | ||
162 | case WSM_PSM_PS: | ||
163 | pm_mode = "on"; | ||
164 | break; | ||
165 | case WSM_PSM_FAST_PS: | ||
166 | pm_mode = "dynamic"; | ||
167 | break; | ||
168 | } | ||
169 | seq_printf(seq, "Preamble: %s\n", | ||
170 | cw1200_debug_preamble[priv->association_mode.preamble]); | ||
171 | seq_printf(seq, "AMPDU spcn: %d\n", | ||
172 | priv->association_mode.mpdu_start_spacing); | ||
173 | seq_printf(seq, "Basic rate: 0x%.8X\n", | ||
174 | le32_to_cpu(priv->association_mode.basic_rate_set)); | ||
175 | seq_printf(seq, "Bss lost: %d beacons\n", | ||
176 | priv->bss_params.beacon_lost_count); | ||
177 | seq_printf(seq, "AID: %d\n", | ||
178 | priv->bss_params.aid); | ||
179 | seq_printf(seq, "Rates: 0x%.8X\n", | ||
180 | priv->bss_params.operational_rate_set); | ||
181 | seq_printf(seq, "Powersave: %s\n", pm_mode); | ||
182 | } | ||
183 | seq_printf(seq, "HT: %s\n", | ||
184 | cw1200_is_ht(&priv->ht_info) ? "on" : "off"); | ||
185 | if (cw1200_is_ht(&priv->ht_info)) { | ||
186 | seq_printf(seq, "Greenfield: %s\n", | ||
187 | cw1200_ht_greenfield(&priv->ht_info) ? "yes" : "no"); | ||
188 | seq_printf(seq, "AMPDU dens: %d\n", | ||
189 | cw1200_ht_ampdu_density(&priv->ht_info)); | ||
190 | } | ||
191 | seq_printf(seq, "RSSI thold: %d\n", | ||
192 | priv->cqm_rssi_thold); | ||
193 | seq_printf(seq, "RSSI hyst: %d\n", | ||
194 | priv->cqm_rssi_hyst); | ||
195 | seq_printf(seq, "Long retr: %d\n", | ||
196 | priv->long_frame_max_tx_count); | ||
197 | seq_printf(seq, "Short retr: %d\n", | ||
198 | priv->short_frame_max_tx_count); | ||
199 | spin_lock_bh(&priv->tx_policy_cache.lock); | ||
200 | i = 0; | ||
201 | list_for_each(item, &priv->tx_policy_cache.used) | ||
202 | ++i; | ||
203 | spin_unlock_bh(&priv->tx_policy_cache.lock); | ||
204 | seq_printf(seq, "RC in use: %d\n", i); | ||
205 | |||
206 | seq_puts(seq, "\n"); | ||
207 | for (i = 0; i < 4; ++i) { | ||
208 | cw1200_queue_status_show(seq, &priv->tx_queue[i]); | ||
209 | seq_puts(seq, "\n"); | ||
210 | } | ||
211 | |||
212 | cw1200_debug_print_map(seq, priv, "Link map: ", | ||
213 | priv->link_id_map); | ||
214 | cw1200_debug_print_map(seq, priv, "Asleep map: ", | ||
215 | priv->sta_asleep_mask); | ||
216 | cw1200_debug_print_map(seq, priv, "PSPOLL map: ", | ||
217 | priv->pspoll_mask); | ||
218 | |||
219 | seq_puts(seq, "\n"); | ||
220 | |||
221 | for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { | ||
222 | if (priv->link_id_db[i].status) { | ||
223 | seq_printf(seq, "Link %d: %s, %pM\n", | ||
224 | i + 1, | ||
225 | cw1200_debug_link_id[priv->link_id_db[i].status], | ||
226 | priv->link_id_db[i].mac); | ||
227 | } | ||
228 | } | ||
229 | |||
230 | seq_puts(seq, "\n"); | ||
231 | |||
232 | seq_printf(seq, "BH status: %s\n", | ||
233 | atomic_read(&priv->bh_term) ? "terminated" : "alive"); | ||
234 | seq_printf(seq, "Pending RX: %d\n", | ||
235 | atomic_read(&priv->bh_rx)); | ||
236 | seq_printf(seq, "Pending TX: %d\n", | ||
237 | atomic_read(&priv->bh_tx)); | ||
238 | if (priv->bh_error) | ||
239 | seq_printf(seq, "BH errcode: %d\n", | ||
240 | priv->bh_error); | ||
241 | seq_printf(seq, "TX bufs: %d x %d bytes\n", | ||
242 | priv->wsm_caps.input_buffers, | ||
243 | priv->wsm_caps.input_buffer_size); | ||
244 | seq_printf(seq, "Used bufs: %d\n", | ||
245 | priv->hw_bufs_used); | ||
246 | seq_printf(seq, "Powermgmt: %s\n", | ||
247 | priv->powersave_enabled ? "on" : "off"); | ||
248 | seq_printf(seq, "Device: %s\n", | ||
249 | priv->device_can_sleep ? "asleep" : "awake"); | ||
250 | |||
251 | spin_lock(&priv->wsm_cmd.lock); | ||
252 | seq_printf(seq, "WSM status: %s\n", | ||
253 | priv->wsm_cmd.done ? "idle" : "active"); | ||
254 | seq_printf(seq, "WSM cmd: 0x%.4X (%td bytes)\n", | ||
255 | priv->wsm_cmd.cmd, priv->wsm_cmd.len); | ||
256 | seq_printf(seq, "WSM retval: %d\n", | ||
257 | priv->wsm_cmd.ret); | ||
258 | spin_unlock(&priv->wsm_cmd.lock); | ||
259 | |||
260 | seq_printf(seq, "Datapath: %s\n", | ||
261 | atomic_read(&priv->tx_lock) ? "locked" : "unlocked"); | ||
262 | if (atomic_read(&priv->tx_lock)) | ||
263 | seq_printf(seq, "TXlock cnt: %d\n", | ||
264 | atomic_read(&priv->tx_lock)); | ||
265 | |||
266 | seq_printf(seq, "TXed: %d\n", | ||
267 | d->tx); | ||
268 | seq_printf(seq, "AGG TXed: %d\n", | ||
269 | d->tx_agg); | ||
270 | seq_printf(seq, "MULTI TXed: %d (%d)\n", | ||
271 | d->tx_multi, d->tx_multi_frames); | ||
272 | seq_printf(seq, "RXed: %d\n", | ||
273 | d->rx); | ||
274 | seq_printf(seq, "AGG RXed: %d\n", | ||
275 | d->rx_agg); | ||
276 | seq_printf(seq, "TX miss: %d\n", | ||
277 | d->tx_cache_miss); | ||
278 | seq_printf(seq, "TX align: %d\n", | ||
279 | d->tx_align); | ||
280 | seq_printf(seq, "TX burst: %d\n", | ||
281 | d->tx_burst); | ||
282 | seq_printf(seq, "TX TTL: %d\n", | ||
283 | d->tx_ttl); | ||
284 | seq_printf(seq, "Scan: %s\n", | ||
285 | atomic_read(&priv->scan.in_progress) ? "active" : "idle"); | ||
286 | |||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | static int cw1200_status_open(struct inode *inode, struct file *file) | ||
291 | { | ||
292 | return single_open(file, &cw1200_status_show, | ||
293 | inode->i_private); | ||
294 | } | ||
295 | |||
296 | static const struct file_operations fops_status = { | ||
297 | .open = cw1200_status_open, | ||
298 | .read = seq_read, | ||
299 | .llseek = seq_lseek, | ||
300 | .release = single_release, | ||
301 | .owner = THIS_MODULE, | ||
302 | }; | ||
303 | |||
304 | static int cw1200_counters_show(struct seq_file *seq, void *v) | ||
305 | { | ||
306 | int ret; | ||
307 | struct cw1200_common *priv = seq->private; | ||
308 | struct wsm_mib_counters_table counters; | ||
309 | |||
310 | ret = wsm_get_counters_table(priv, &counters); | ||
311 | if (ret) | ||
312 | return ret; | ||
313 | |||
314 | #define PUT_COUNTER(tab, name) \ | ||
315 | seq_printf(seq, "%s:" tab "%d\n", #name, \ | ||
316 | __le32_to_cpu(counters.name)) | ||
317 | |||
318 | PUT_COUNTER("\t\t", plcp_errors); | ||
319 | PUT_COUNTER("\t\t", fcs_errors); | ||
320 | PUT_COUNTER("\t\t", tx_packets); | ||
321 | PUT_COUNTER("\t\t", rx_packets); | ||
322 | PUT_COUNTER("\t\t", rx_packet_errors); | ||
323 | PUT_COUNTER("\t", rx_decryption_failures); | ||
324 | PUT_COUNTER("\t\t", rx_mic_failures); | ||
325 | PUT_COUNTER("\t", rx_no_key_failures); | ||
326 | PUT_COUNTER("\t", tx_multicast_frames); | ||
327 | PUT_COUNTER("\t", tx_frames_success); | ||
328 | PUT_COUNTER("\t", tx_frame_failures); | ||
329 | PUT_COUNTER("\t", tx_frames_retried); | ||
330 | PUT_COUNTER("\t", tx_frames_multi_retried); | ||
331 | PUT_COUNTER("\t", rx_frame_duplicates); | ||
332 | PUT_COUNTER("\t\t", rts_success); | ||
333 | PUT_COUNTER("\t\t", rts_failures); | ||
334 | PUT_COUNTER("\t\t", ack_failures); | ||
335 | PUT_COUNTER("\t", rx_multicast_frames); | ||
336 | PUT_COUNTER("\t", rx_frames_success); | ||
337 | PUT_COUNTER("\t", rx_cmac_icv_errors); | ||
338 | PUT_COUNTER("\t\t", rx_cmac_replays); | ||
339 | PUT_COUNTER("\t", rx_mgmt_ccmp_replays); | ||
340 | |||
341 | #undef PUT_COUNTER | ||
342 | |||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | static int cw1200_counters_open(struct inode *inode, struct file *file) | ||
347 | { | ||
348 | return single_open(file, &cw1200_counters_show, | ||
349 | inode->i_private); | ||
350 | } | ||
351 | |||
352 | static const struct file_operations fops_counters = { | ||
353 | .open = cw1200_counters_open, | ||
354 | .read = seq_read, | ||
355 | .llseek = seq_lseek, | ||
356 | .release = single_release, | ||
357 | .owner = THIS_MODULE, | ||
358 | }; | ||
359 | |||
360 | static int cw1200_generic_open(struct inode *inode, struct file *file) | ||
361 | { | ||
362 | file->private_data = inode->i_private; | ||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | #ifdef CONFIG_CW1200_ETF | ||
367 | static int cw1200_etf_out_show(struct seq_file *seq, void *v) | ||
368 | { | ||
369 | struct cw1200_common *priv = seq->private; | ||
370 | struct sk_buff *skb; | ||
371 | u32 len = 0; | ||
372 | |||
373 | skb = skb_dequeue(&priv->etf_q); | ||
374 | |||
375 | if (skb) | ||
376 | len = skb->len; | ||
377 | |||
378 | seq_write(seq, &len, sizeof(len)); | ||
379 | |||
380 | if (skb) { | ||
381 | seq_write(seq, skb->data, len); | ||
382 | kfree_skb(skb); | ||
383 | } | ||
384 | |||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static int cw1200_etf_out_open(struct inode *inode, struct file *file) | ||
389 | { | ||
390 | return single_open(file, &cw1200_etf_out_show, | ||
391 | inode->i_private); | ||
392 | } | ||
393 | |||
394 | static const struct file_operations fops_etf_out = { | ||
395 | .open = cw1200_etf_out_open, | ||
396 | .read = seq_read, | ||
397 | .llseek = seq_lseek, | ||
398 | .release = single_release, | ||
399 | .owner = THIS_MODULE, | ||
400 | }; | ||
401 | |||
402 | struct etf_req_msg; | ||
403 | static int etf_request(struct cw1200_common *priv, | ||
404 | struct etf_req_msg *msg, u32 len); | ||
405 | |||
406 | #define MAX_RX_SZE 2600 | ||
407 | |||
408 | struct etf_in_state { | ||
409 | struct cw1200_common *priv; | ||
410 | u32 total_len; | ||
411 | u8 buf[MAX_RX_SZE]; | ||
412 | u32 written; | ||
413 | }; | ||
414 | |||
415 | static int cw1200_etf_in_open(struct inode *inode, struct file *file) | ||
416 | { | ||
417 | struct etf_in_state *etf = kmalloc(sizeof(struct etf_in_state), | ||
418 | GFP_KERNEL); | ||
419 | |||
420 | if (!etf) | ||
421 | return -ENOMEM; | ||
422 | |||
423 | etf->written = 0; | ||
424 | etf->total_len = 0; | ||
425 | etf->priv = inode->i_private; | ||
426 | |||
427 | file->private_data = etf; | ||
428 | |||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | static int cw1200_etf_in_release(struct inode *inode, struct file *file) | ||
433 | { | ||
434 | kfree(file->private_data); | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | static ssize_t cw1200_etf_in_write(struct file *file, | ||
439 | const char __user *user_buf, size_t count, loff_t *ppos) | ||
440 | { | ||
441 | struct etf_in_state *etf = file->private_data; | ||
442 | |||
443 | ssize_t written = 0; | ||
444 | |||
445 | if (!etf->total_len) { | ||
446 | if (count < sizeof(etf->total_len)) { | ||
447 | pr_err("count < sizeof(total_len)\n"); | ||
448 | return -EINVAL; | ||
449 | } | ||
450 | |||
451 | if (copy_from_user(&etf->total_len, user_buf, | ||
452 | sizeof(etf->total_len))) { | ||
453 | pr_err("copy_from_user (len) failed\n"); | ||
454 | return -EFAULT; | ||
455 | } | ||
456 | |||
457 | written += sizeof(etf->total_len); | ||
458 | count -= sizeof(etf->total_len); | ||
459 | } | ||
460 | |||
461 | if (!count) | ||
462 | goto done; | ||
463 | |||
464 | if (copy_from_user(etf->buf + etf->written, user_buf + written, | ||
465 | count)) { | ||
466 | pr_err("copy_from_user (payload %zu) failed\n", count); | ||
467 | return -EFAULT; | ||
468 | } | ||
469 | |||
470 | written += count; | ||
471 | etf->written += count; | ||
472 | |||
473 | if (etf->written >= etf->total_len) { | ||
474 | if (etf_request(etf->priv, (struct etf_req_msg *)etf->buf, | ||
475 | etf->total_len)) { | ||
476 | pr_err("etf_request failed\n"); | ||
477 | return -EIO; | ||
478 | } | ||
479 | } | ||
480 | |||
481 | done: | ||
482 | return written; | ||
483 | } | ||
484 | |||
485 | static const struct file_operations fops_etf_in = { | ||
486 | .open = cw1200_etf_in_open, | ||
487 | .release = cw1200_etf_in_release, | ||
488 | .write = cw1200_etf_in_write, | ||
489 | .llseek = default_llseek, | ||
490 | .owner = THIS_MODULE, | ||
491 | }; | ||
492 | #endif /* CONFIG_CW1200_ETF */ | ||
493 | |||
494 | static ssize_t cw1200_wsm_dumps(struct file *file, | ||
495 | const char __user *user_buf, size_t count, loff_t *ppos) | ||
496 | { | ||
497 | struct cw1200_common *priv = file->private_data; | ||
498 | char buf[1]; | ||
499 | |||
500 | if (!count) | ||
501 | return -EINVAL; | ||
502 | if (copy_from_user(buf, user_buf, 1)) | ||
503 | return -EFAULT; | ||
504 | |||
505 | if (buf[0] == '1') | ||
506 | priv->wsm_enable_wsm_dumps = 1; | ||
507 | else | ||
508 | priv->wsm_enable_wsm_dumps = 0; | ||
509 | |||
510 | return count; | ||
511 | } | ||
512 | |||
513 | static const struct file_operations fops_wsm_dumps = { | ||
514 | .open = cw1200_generic_open, | ||
515 | .write = cw1200_wsm_dumps, | ||
516 | .llseek = default_llseek, | ||
517 | }; | ||
518 | |||
519 | int cw1200_debug_init(struct cw1200_common *priv) | ||
520 | { | ||
521 | int ret = -ENOMEM; | ||
522 | struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv), | ||
523 | GFP_KERNEL); | ||
524 | priv->debug = d; | ||
525 | if (!d) | ||
526 | return ret; | ||
527 | |||
528 | d->debugfs_phy = debugfs_create_dir("cw1200", | ||
529 | priv->hw->wiphy->debugfsdir); | ||
530 | if (!d->debugfs_phy) | ||
531 | goto err; | ||
532 | |||
533 | if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy, | ||
534 | priv, &fops_status)) | ||
535 | goto err; | ||
536 | |||
537 | if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy, | ||
538 | priv, &fops_counters)) | ||
539 | goto err; | ||
540 | |||
541 | #ifdef CONFIG_CW1200_ETF | ||
542 | if (etf_mode) { | ||
543 | skb_queue_head_init(&priv->etf_q); | ||
544 | |||
545 | if (!debugfs_create_file("etf_out", S_IRUSR, d->debugfs_phy, | ||
546 | priv, &fops_etf_out)) | ||
547 | goto err; | ||
548 | if (!debugfs_create_file("etf_in", S_IWUSR, d->debugfs_phy, | ||
549 | priv, &fops_etf_in)) | ||
550 | goto err; | ||
551 | } | ||
552 | #endif /* CONFIG_CW1200_ETF */ | ||
553 | |||
554 | if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy, | ||
555 | priv, &fops_wsm_dumps)) | ||
556 | goto err; | ||
557 | |||
558 | ret = cw1200_itp_init(priv); | ||
559 | if (ret) | ||
560 | goto err; | ||
561 | |||
562 | return 0; | ||
563 | |||
564 | err: | ||
565 | priv->debug = NULL; | ||
566 | debugfs_remove_recursive(d->debugfs_phy); | ||
567 | kfree(d); | ||
568 | return ret; | ||
569 | } | ||
570 | |||
571 | void cw1200_debug_release(struct cw1200_common *priv) | ||
572 | { | ||
573 | struct cw1200_debug_priv *d = priv->debug; | ||
574 | if (d) { | ||
575 | cw1200_itp_release(priv); | ||
576 | priv->debug = NULL; | ||
577 | kfree(d); | ||
578 | } | ||
579 | } | ||
580 | |||
581 | #ifdef CONFIG_CW1200_ETF | ||
582 | struct cw1200_sdd { | ||
583 | u8 id; | ||
584 | u8 len; | ||
585 | u8 data[]; | ||
586 | }; | ||
587 | |||
588 | struct etf_req_msg { | ||
589 | u32 id; | ||
590 | u32 len; | ||
591 | u8 data[]; | ||
592 | }; | ||
593 | |||
594 | static int parse_sdd_file(struct cw1200_common *priv, u8 *data, u32 length) | ||
595 | { | ||
596 | struct cw1200_sdd *ie; | ||
597 | |||
598 | while (length > 0) { | ||
599 | ie = (struct cw1200_sdd *)data; | ||
600 | if (ie->id == SDD_REFERENCE_FREQUENCY_ELT_ID) { | ||
601 | priv->hw_refclk = cpu_to_le16(*((u16 *)ie->data)); | ||
602 | pr_info("Using Reference clock frequency %d KHz\n", | ||
603 | priv->hw_refclk); | ||
604 | break; | ||
605 | } | ||
606 | |||
607 | length -= ie->len + sizeof(*ie); | ||
608 | data += ie->len + sizeof(*ie); | ||
609 | } | ||
610 | return 0; | ||
611 | } | ||
612 | |||
613 | char *etf_firmware; | ||
614 | |||
615 | #define ST90TDS_START_ADAPTER 0x09 /* Loads firmware too */ | ||
616 | #define ST90TDS_STOP_ADAPTER 0x0A | ||
617 | #define ST90TDS_CONFIG_ADAPTER 0x0E /* Send configuration params */ | ||
618 | #define ST90TDS_SBUS_READ 0x13 | ||
619 | #define ST90TDS_SBUS_WRITE 0x14 | ||
620 | #define ST90TDS_GET_DEVICE_OPTION 0x19 | ||
621 | #define ST90TDS_SET_DEVICE_OPTION 0x1A | ||
622 | #define ST90TDS_SEND_SDD 0x1D /* SDD File used to find DPLL */ | ||
623 | |||
624 | #include "fwio.h" | ||
625 | |||
626 | static int etf_request(struct cw1200_common *priv, | ||
627 | struct etf_req_msg *msg, | ||
628 | u32 len) | ||
629 | { | ||
630 | int rval = -1; | ||
631 | switch (msg->id) { | ||
632 | case ST90TDS_START_ADAPTER: | ||
633 | etf_firmware = "cw1200_etf.bin"; | ||
634 | pr_info("ETF_START (len %d, '%s')\n", len, etf_firmware); | ||
635 | rval = cw1200_load_firmware(priv); | ||
636 | break; | ||
637 | case ST90TDS_STOP_ADAPTER: | ||
638 | pr_info("ETF_STOP (unhandled)\n"); | ||
639 | break; | ||
640 | case ST90TDS_SEND_SDD: | ||
641 | pr_info("ETF_SDD\n"); | ||
642 | rval = parse_sdd_file(priv, msg->data, msg->len); | ||
643 | break; | ||
644 | case ST90TDS_CONFIG_ADAPTER: | ||
645 | pr_info("ETF_CONFIG_ADAP (unhandled)\n"); | ||
646 | break; | ||
647 | case ST90TDS_SBUS_READ: | ||
648 | pr_info("ETF_SBUS_READ (unhandled)\n"); | ||
649 | break; | ||
650 | case ST90TDS_SBUS_WRITE: | ||
651 | pr_info("ETF_SBUS_WRITE (unhandled)\n"); | ||
652 | break; | ||
653 | case ST90TDS_SET_DEVICE_OPTION: | ||
654 | pr_info("ETF_SET_DEV_OPT (unhandled)\n"); | ||
655 | break; | ||
656 | default: | ||
657 | pr_info("ETF_PASSTHRU (0x%08x)\n", msg->id); | ||
658 | rval = wsm_raw_cmd(priv, (u8 *)msg, len); | ||
659 | break; | ||
660 | } | ||
661 | |||
662 | return rval; | ||
663 | } | ||
664 | #endif /* CONFIG_CW1200_ETF */ | ||
diff --git a/drivers/net/wireless/cw1200/debug.h b/drivers/net/wireless/cw1200/debug.h new file mode 100644 index 000000000000..1fea5b29a819 --- /dev/null +++ b/drivers/net/wireless/cw1200/debug.h | |||
@@ -0,0 +1,98 @@ | |||
1 | /* | ||
2 | * DebugFS code for ST-Ericsson CW1200 mac80211 driver | ||
3 | * | ||
4 | * Copyright (c) 2011, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef CW1200_DEBUG_H_INCLUDED | ||
13 | #define CW1200_DEBUG_H_INCLUDED | ||
14 | |||
15 | #include "itp.h" | ||
16 | |||
17 | struct cw1200_debug_priv { | ||
18 | struct dentry *debugfs_phy; | ||
19 | int tx; | ||
20 | int tx_agg; | ||
21 | int rx; | ||
22 | int rx_agg; | ||
23 | int tx_multi; | ||
24 | int tx_multi_frames; | ||
25 | int tx_cache_miss; | ||
26 | int tx_align; | ||
27 | int tx_ttl; | ||
28 | int tx_burst; | ||
29 | int ba_cnt; | ||
30 | int ba_acc; | ||
31 | int ba_cnt_rx; | ||
32 | int ba_acc_rx; | ||
33 | #ifdef CONFIG_CW1200_ITP | ||
34 | struct cw1200_itp itp; | ||
35 | #endif /* CONFIG_CW1200_ITP */ | ||
36 | }; | ||
37 | |||
38 | int cw1200_debug_init(struct cw1200_common *priv); | ||
39 | void cw1200_debug_release(struct cw1200_common *priv); | ||
40 | |||
41 | static inline void cw1200_debug_txed(struct cw1200_common *priv) | ||
42 | { | ||
43 | ++priv->debug->tx; | ||
44 | } | ||
45 | |||
46 | static inline void cw1200_debug_txed_agg(struct cw1200_common *priv) | ||
47 | { | ||
48 | ++priv->debug->tx_agg; | ||
49 | } | ||
50 | |||
51 | static inline void cw1200_debug_txed_multi(struct cw1200_common *priv, | ||
52 | int count) | ||
53 | { | ||
54 | ++priv->debug->tx_multi; | ||
55 | priv->debug->tx_multi_frames += count; | ||
56 | } | ||
57 | |||
58 | static inline void cw1200_debug_rxed(struct cw1200_common *priv) | ||
59 | { | ||
60 | ++priv->debug->rx; | ||
61 | } | ||
62 | |||
63 | static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv) | ||
64 | { | ||
65 | ++priv->debug->rx_agg; | ||
66 | } | ||
67 | |||
68 | static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv) | ||
69 | { | ||
70 | ++priv->debug->tx_cache_miss; | ||
71 | } | ||
72 | |||
73 | static inline void cw1200_debug_tx_align(struct cw1200_common *priv) | ||
74 | { | ||
75 | ++priv->debug->tx_align; | ||
76 | } | ||
77 | |||
78 | static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv) | ||
79 | { | ||
80 | ++priv->debug->tx_ttl; | ||
81 | } | ||
82 | |||
83 | static inline void cw1200_debug_tx_burst(struct cw1200_common *priv) | ||
84 | { | ||
85 | ++priv->debug->tx_burst; | ||
86 | } | ||
87 | |||
88 | static inline void cw1200_debug_ba(struct cw1200_common *priv, | ||
89 | int ba_cnt, int ba_acc, | ||
90 | int ba_cnt_rx, int ba_acc_rx) | ||
91 | { | ||
92 | priv->debug->ba_cnt = ba_cnt; | ||
93 | priv->debug->ba_acc = ba_acc; | ||
94 | priv->debug->ba_cnt_rx = ba_cnt_rx; | ||
95 | priv->debug->ba_acc_rx = ba_acc_rx; | ||
96 | } | ||
97 | |||
98 | #endif /* CW1200_DEBUG_H_INCLUDED */ | ||
diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/cw1200/fwio.c new file mode 100644 index 000000000000..ad01cd2a59ec --- /dev/null +++ b/drivers/net/wireless/cw1200/fwio.c | |||
@@ -0,0 +1,525 @@ | |||
1 | /* | ||
2 | * Firmware I/O 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 | * ST-Ericsson UMAC CW1200 driver which is | ||
9 | * Copyright (c) 2010, ST-Ericsson | ||
10 | * Author: Ajitpal Singh <ajitpal.singh@stericsson.com> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | */ | ||
16 | |||
17 | #include <linux/init.h> | ||
18 | #include <linux/vmalloc.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/firmware.h> | ||
21 | |||
22 | #include "cw1200.h" | ||
23 | #include "fwio.h" | ||
24 | #include "hwio.h" | ||
25 | #include "sbus.h" | ||
26 | #include "bh.h" | ||
27 | |||
28 | static int cw1200_get_hw_type(u32 config_reg_val, int *major_revision) | ||
29 | { | ||
30 | int hw_type = -1; | ||
31 | u32 silicon_type = (config_reg_val >> 24) & 0x7; | ||
32 | u32 silicon_vers = (config_reg_val >> 31) & 0x1; | ||
33 | |||
34 | switch (silicon_type) { | ||
35 | case 0x00: | ||
36 | *major_revision = 1; | ||
37 | hw_type = HIF_9000_SILICON_VERSATILE; | ||
38 | break; | ||
39 | case 0x01: | ||
40 | case 0x02: /* CW1x00 */ | ||
41 | case 0x04: /* CW1x60 */ | ||
42 | *major_revision = silicon_type; | ||
43 | if (silicon_vers) | ||
44 | hw_type = HIF_8601_VERSATILE; | ||
45 | else | ||
46 | hw_type = HIF_8601_SILICON; | ||
47 | break; | ||
48 | default: | ||
49 | break; | ||
50 | } | ||
51 | |||
52 | return hw_type; | ||
53 | } | ||
54 | |||
55 | static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) | ||
56 | { | ||
57 | int ret, block, num_blocks; | ||
58 | unsigned i; | ||
59 | u32 val32; | ||
60 | u32 put = 0, get = 0; | ||
61 | u8 *buf = NULL; | ||
62 | const char *fw_path; | ||
63 | const struct firmware *firmware = NULL; | ||
64 | |||
65 | /* Macroses are local. */ | ||
66 | #define APB_WRITE(reg, val) \ | ||
67 | do { \ | ||
68 | ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \ | ||
69 | if (ret < 0) \ | ||
70 | goto error; \ | ||
71 | } while (0) | ||
72 | #define APB_READ(reg, val) \ | ||
73 | do { \ | ||
74 | ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \ | ||
75 | if (ret < 0) \ | ||
76 | goto error; \ | ||
77 | } while (0) | ||
78 | #define REG_WRITE(reg, val) \ | ||
79 | do { \ | ||
80 | ret = cw1200_reg_write_32(priv, (reg), (val)); \ | ||
81 | if (ret < 0) \ | ||
82 | goto error; \ | ||
83 | } while (0) | ||
84 | #define REG_READ(reg, val) \ | ||
85 | do { \ | ||
86 | ret = cw1200_reg_read_32(priv, (reg), &(val)); \ | ||
87 | if (ret < 0) \ | ||
88 | goto error; \ | ||
89 | } while (0) | ||
90 | |||
91 | switch (priv->hw_revision) { | ||
92 | case CW1200_HW_REV_CUT10: | ||
93 | fw_path = FIRMWARE_CUT10; | ||
94 | if (!priv->sdd_path) | ||
95 | priv->sdd_path = SDD_FILE_10; | ||
96 | break; | ||
97 | case CW1200_HW_REV_CUT11: | ||
98 | fw_path = FIRMWARE_CUT11; | ||
99 | if (!priv->sdd_path) | ||
100 | priv->sdd_path = SDD_FILE_11; | ||
101 | break; | ||
102 | case CW1200_HW_REV_CUT20: | ||
103 | fw_path = FIRMWARE_CUT20; | ||
104 | if (!priv->sdd_path) | ||
105 | priv->sdd_path = SDD_FILE_20; | ||
106 | break; | ||
107 | case CW1200_HW_REV_CUT22: | ||
108 | fw_path = FIRMWARE_CUT22; | ||
109 | if (!priv->sdd_path) | ||
110 | priv->sdd_path = SDD_FILE_22; | ||
111 | break; | ||
112 | case CW1X60_HW_REV: | ||
113 | fw_path = FIRMWARE_CW1X60; | ||
114 | if (!priv->sdd_path) | ||
115 | priv->sdd_path = SDD_FILE_CW1X60; | ||
116 | break; | ||
117 | default: | ||
118 | pr_err("Invalid silicon revision %d.\n", priv->hw_revision); | ||
119 | return -EINVAL; | ||
120 | } | ||
121 | |||
122 | /* Initialize common registers */ | ||
123 | APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE); | ||
124 | APB_WRITE(DOWNLOAD_PUT_REG, 0); | ||
125 | APB_WRITE(DOWNLOAD_GET_REG, 0); | ||
126 | APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING); | ||
127 | APB_WRITE(DOWNLOAD_FLAGS_REG, 0); | ||
128 | |||
129 | /* Write the NOP Instruction */ | ||
130 | REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000); | ||
131 | REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE); | ||
132 | |||
133 | /* Release CPU from RESET */ | ||
134 | REG_READ(ST90TDS_CONFIG_REG_ID, val32); | ||
135 | val32 &= ~ST90TDS_CONFIG_CPU_RESET_BIT; | ||
136 | REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); | ||
137 | |||
138 | /* Enable Clock */ | ||
139 | val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT; | ||
140 | REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); | ||
141 | |||
142 | #ifdef CONFIG_CW1200_ETF | ||
143 | if (etf_mode) | ||
144 | fw_path = etf_firmware; | ||
145 | #endif | ||
146 | |||
147 | /* Load a firmware file */ | ||
148 | ret = request_firmware(&firmware, fw_path, priv->pdev); | ||
149 | if (ret) { | ||
150 | pr_err("Can't load firmware file %s.\n", fw_path); | ||
151 | goto error; | ||
152 | } | ||
153 | |||
154 | buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); | ||
155 | if (!buf) { | ||
156 | pr_err("Can't allocate firmware load buffer.\n"); | ||
157 | ret = -ENOMEM; | ||
158 | goto error; | ||
159 | } | ||
160 | |||
161 | /* Check if the bootloader is ready */ | ||
162 | for (i = 0; i < 100; i += 1 + i / 2) { | ||
163 | APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32); | ||
164 | if (val32 == DOWNLOAD_I_AM_HERE) | ||
165 | break; | ||
166 | mdelay(i); | ||
167 | } /* End of for loop */ | ||
168 | |||
169 | if (val32 != DOWNLOAD_I_AM_HERE) { | ||
170 | pr_err("Bootloader is not ready.\n"); | ||
171 | ret = -ETIMEDOUT; | ||
172 | goto error; | ||
173 | } | ||
174 | |||
175 | /* Calculcate number of download blocks */ | ||
176 | num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1; | ||
177 | |||
178 | /* Updating the length in Download Ctrl Area */ | ||
179 | val32 = firmware->size; /* Explicit cast from size_t to u32 */ | ||
180 | APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32); | ||
181 | |||
182 | /* Firmware downloading loop */ | ||
183 | for (block = 0; block < num_blocks; block++) { | ||
184 | size_t tx_size; | ||
185 | size_t block_size; | ||
186 | |||
187 | /* check the download status */ | ||
188 | APB_READ(DOWNLOAD_STATUS_REG, val32); | ||
189 | if (val32 != DOWNLOAD_PENDING) { | ||
190 | pr_err("Bootloader reported error %d.\n", val32); | ||
191 | ret = -EIO; | ||
192 | goto error; | ||
193 | } | ||
194 | |||
195 | /* loop until put - get <= 24K */ | ||
196 | for (i = 0; i < 100; i++) { | ||
197 | APB_READ(DOWNLOAD_GET_REG, get); | ||
198 | if ((put - get) <= | ||
199 | (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) | ||
200 | break; | ||
201 | mdelay(i); | ||
202 | } | ||
203 | |||
204 | if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) { | ||
205 | pr_err("Timeout waiting for FIFO.\n"); | ||
206 | ret = -ETIMEDOUT; | ||
207 | goto error; | ||
208 | } | ||
209 | |||
210 | /* calculate the block size */ | ||
211 | tx_size = block_size = min((size_t)(firmware->size - put), | ||
212 | (size_t)DOWNLOAD_BLOCK_SIZE); | ||
213 | |||
214 | memcpy(buf, &firmware->data[put], block_size); | ||
215 | if (block_size < DOWNLOAD_BLOCK_SIZE) { | ||
216 | memset(&buf[block_size], 0, | ||
217 | DOWNLOAD_BLOCK_SIZE - block_size); | ||
218 | tx_size = DOWNLOAD_BLOCK_SIZE; | ||
219 | } | ||
220 | |||
221 | /* send the block to sram */ | ||
222 | ret = cw1200_apb_write(priv, | ||
223 | CW1200_APB(DOWNLOAD_FIFO_OFFSET + | ||
224 | (put & (DOWNLOAD_FIFO_SIZE - 1))), | ||
225 | buf, tx_size); | ||
226 | if (ret < 0) { | ||
227 | pr_err("Can't write firmware block @ %d!\n", | ||
228 | put & (DOWNLOAD_FIFO_SIZE - 1)); | ||
229 | goto error; | ||
230 | } | ||
231 | |||
232 | /* update the put register */ | ||
233 | put += block_size; | ||
234 | APB_WRITE(DOWNLOAD_PUT_REG, put); | ||
235 | } /* End of firmware download loop */ | ||
236 | |||
237 | /* Wait for the download completion */ | ||
238 | for (i = 0; i < 300; i += 1 + i / 2) { | ||
239 | APB_READ(DOWNLOAD_STATUS_REG, val32); | ||
240 | if (val32 != DOWNLOAD_PENDING) | ||
241 | break; | ||
242 | mdelay(i); | ||
243 | } | ||
244 | if (val32 != DOWNLOAD_SUCCESS) { | ||
245 | pr_err("Wait for download completion failed: 0x%.8X\n", val32); | ||
246 | ret = -ETIMEDOUT; | ||
247 | goto error; | ||
248 | } else { | ||
249 | pr_info("Firmware download completed.\n"); | ||
250 | ret = 0; | ||
251 | } | ||
252 | |||
253 | error: | ||
254 | kfree(buf); | ||
255 | if (firmware) | ||
256 | release_firmware(firmware); | ||
257 | return ret; | ||
258 | |||
259 | #undef APB_WRITE | ||
260 | #undef APB_READ | ||
261 | #undef REG_WRITE | ||
262 | #undef REG_READ | ||
263 | } | ||
264 | |||
265 | |||
266 | static int config_reg_read(struct cw1200_common *priv, u32 *val) | ||
267 | { | ||
268 | switch (priv->hw_type) { | ||
269 | case HIF_9000_SILICON_VERSATILE: { | ||
270 | u16 val16; | ||
271 | int ret = cw1200_reg_read_16(priv, | ||
272 | ST90TDS_CONFIG_REG_ID, | ||
273 | &val16); | ||
274 | if (ret < 0) | ||
275 | return ret; | ||
276 | *val = val16; | ||
277 | return 0; | ||
278 | } | ||
279 | case HIF_8601_VERSATILE: | ||
280 | case HIF_8601_SILICON: | ||
281 | default: | ||
282 | cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, val); | ||
283 | break; | ||
284 | } | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static int config_reg_write(struct cw1200_common *priv, u32 val) | ||
289 | { | ||
290 | switch (priv->hw_type) { | ||
291 | case HIF_9000_SILICON_VERSATILE: | ||
292 | return cw1200_reg_write_16(priv, | ||
293 | ST90TDS_CONFIG_REG_ID, | ||
294 | (u16)val); | ||
295 | case HIF_8601_VERSATILE: | ||
296 | case HIF_8601_SILICON: | ||
297 | default: | ||
298 | return cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val); | ||
299 | break; | ||
300 | } | ||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | int cw1200_load_firmware(struct cw1200_common *priv) | ||
305 | { | ||
306 | int ret; | ||
307 | int i; | ||
308 | u32 val32; | ||
309 | u16 val16; | ||
310 | int major_revision = -1; | ||
311 | |||
312 | /* Read CONFIG Register */ | ||
313 | ret = cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); | ||
314 | if (ret < 0) { | ||
315 | pr_err("Can't read config register.\n"); | ||
316 | goto out; | ||
317 | } | ||
318 | |||
319 | if (val32 == 0 || val32 == 0xffffffff) { | ||
320 | pr_err("Bad config register value (0x%08x)\n", val32); | ||
321 | ret = -EIO; | ||
322 | goto out; | ||
323 | } | ||
324 | |||
325 | priv->hw_type = cw1200_get_hw_type(val32, &major_revision); | ||
326 | if (priv->hw_type < 0) { | ||
327 | pr_err("Can't deduce hardware type.\n"); | ||
328 | ret = -ENOTSUPP; | ||
329 | goto out; | ||
330 | } | ||
331 | |||
332 | /* Set DPLL Reg value, and read back to confirm writes work */ | ||
333 | ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID, | ||
334 | cw1200_dpll_from_clk(priv->hw_refclk)); | ||
335 | if (ret < 0) { | ||
336 | pr_err("Can't write DPLL register.\n"); | ||
337 | goto out; | ||
338 | } | ||
339 | |||
340 | msleep(20); | ||
341 | |||
342 | ret = cw1200_reg_read_32(priv, | ||
343 | ST90TDS_TSET_GEN_R_W_REG_ID, &val32); | ||
344 | if (ret < 0) { | ||
345 | pr_err("Can't read DPLL register.\n"); | ||
346 | goto out; | ||
347 | } | ||
348 | |||
349 | if (val32 != cw1200_dpll_from_clk(priv->hw_refclk)) { | ||
350 | pr_err("Unable to initialise DPLL register. Wrote 0x%.8X, Read 0x%.8X.\n", | ||
351 | cw1200_dpll_from_clk(priv->hw_refclk), val32); | ||
352 | ret = -EIO; | ||
353 | goto out; | ||
354 | } | ||
355 | |||
356 | /* Set wakeup bit in device */ | ||
357 | ret = cw1200_reg_read_16(priv, ST90TDS_CONTROL_REG_ID, &val16); | ||
358 | if (ret < 0) { | ||
359 | pr_err("set_wakeup: can't read control register.\n"); | ||
360 | goto out; | ||
361 | } | ||
362 | |||
363 | ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, | ||
364 | val16 | ST90TDS_CONT_WUP_BIT); | ||
365 | if (ret < 0) { | ||
366 | pr_err("set_wakeup: can't write control register.\n"); | ||
367 | goto out; | ||
368 | } | ||
369 | |||
370 | /* Wait for wakeup */ | ||
371 | for (i = 0; i < 300; i += (1 + i / 2)) { | ||
372 | ret = cw1200_reg_read_16(priv, | ||
373 | ST90TDS_CONTROL_REG_ID, &val16); | ||
374 | if (ret < 0) { | ||
375 | pr_err("wait_for_wakeup: can't read control register.\n"); | ||
376 | goto out; | ||
377 | } | ||
378 | |||
379 | if (val16 & ST90TDS_CONT_RDY_BIT) | ||
380 | break; | ||
381 | |||
382 | msleep(i); | ||
383 | } | ||
384 | |||
385 | if ((val16 & ST90TDS_CONT_RDY_BIT) == 0) { | ||
386 | pr_err("wait_for_wakeup: device is not responding.\n"); | ||
387 | ret = -ETIMEDOUT; | ||
388 | goto out; | ||
389 | } | ||
390 | |||
391 | switch (major_revision) { | ||
392 | case 1: | ||
393 | /* CW1200 Hardware detection logic : Check for CUT1.1 */ | ||
394 | ret = cw1200_ahb_read_32(priv, CW1200_CUT_ID_ADDR, &val32); | ||
395 | if (ret) { | ||
396 | pr_err("HW detection: can't read CUT ID.\n"); | ||
397 | goto out; | ||
398 | } | ||
399 | |||
400 | switch (val32) { | ||
401 | case CW1200_CUT_11_ID_STR: | ||
402 | pr_info("CW1x00 Cut 1.1 silicon detected.\n"); | ||
403 | priv->hw_revision = CW1200_HW_REV_CUT11; | ||
404 | break; | ||
405 | default: | ||
406 | pr_info("CW1x00 Cut 1.0 silicon detected.\n"); | ||
407 | priv->hw_revision = CW1200_HW_REV_CUT10; | ||
408 | break; | ||
409 | } | ||
410 | |||
411 | /* According to ST-E, CUT<2.0 has busted BA TID0-3. | ||
412 | Just disable it entirely... | ||
413 | */ | ||
414 | priv->ba_rx_tid_mask = 0; | ||
415 | priv->ba_tx_tid_mask = 0; | ||
416 | break; | ||
417 | case 2: { | ||
418 | u32 ar1, ar2, ar3; | ||
419 | ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR, &ar1); | ||
420 | if (ret) { | ||
421 | pr_err("(1) HW detection: can't read CUT ID\n"); | ||
422 | goto out; | ||
423 | } | ||
424 | ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2); | ||
425 | if (ret) { | ||
426 | pr_err("(2) HW detection: can't read CUT ID.\n"); | ||
427 | goto out; | ||
428 | } | ||
429 | |||
430 | ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3); | ||
431 | if (ret) { | ||
432 | pr_err("(3) HW detection: can't read CUT ID.\n"); | ||
433 | goto out; | ||
434 | } | ||
435 | |||
436 | if (ar1 == CW1200_CUT_22_ID_STR1 && | ||
437 | ar2 == CW1200_CUT_22_ID_STR2 && | ||
438 | ar3 == CW1200_CUT_22_ID_STR3) { | ||
439 | pr_info("CW1x00 Cut 2.2 silicon detected.\n"); | ||
440 | priv->hw_revision = CW1200_HW_REV_CUT22; | ||
441 | } else { | ||
442 | pr_info("CW1x00 Cut 2.0 silicon detected.\n"); | ||
443 | priv->hw_revision = CW1200_HW_REV_CUT20; | ||
444 | } | ||
445 | break; | ||
446 | } | ||
447 | case 4: | ||
448 | pr_info("CW1x60 silicon detected.\n"); | ||
449 | priv->hw_revision = CW1X60_HW_REV; | ||
450 | break; | ||
451 | default: | ||
452 | pr_err("Unsupported silicon major revision %d.\n", | ||
453 | major_revision); | ||
454 | ret = -ENOTSUPP; | ||
455 | goto out; | ||
456 | } | ||
457 | |||
458 | /* Checking for access mode */ | ||
459 | ret = config_reg_read(priv, &val32); | ||
460 | if (ret < 0) { | ||
461 | pr_err("Can't read config register.\n"); | ||
462 | goto out; | ||
463 | } | ||
464 | |||
465 | if (!(val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT)) { | ||
466 | pr_err("Device is already in QUEUE mode!\n"); | ||
467 | ret = -EINVAL; | ||
468 | goto out; | ||
469 | } | ||
470 | |||
471 | switch (priv->hw_type) { | ||
472 | case HIF_8601_SILICON: | ||
473 | if (priv->hw_revision == CW1X60_HW_REV) { | ||
474 | pr_err("Can't handle CW1160/1260 firmware load yet.\n"); | ||
475 | ret = -ENOTSUPP; | ||
476 | goto out; | ||
477 | } | ||
478 | ret = cw1200_load_firmware_cw1200(priv); | ||
479 | break; | ||
480 | default: | ||
481 | pr_err("Can't perform firmware load for hw type %d.\n", | ||
482 | priv->hw_type); | ||
483 | ret = -ENOTSUPP; | ||
484 | goto out; | ||
485 | } | ||
486 | if (ret < 0) { | ||
487 | pr_err("Firmware load error.\n"); | ||
488 | goto out; | ||
489 | } | ||
490 | |||
491 | /* Enable interrupt signalling */ | ||
492 | priv->sbus_ops->lock(priv->sbus_priv); | ||
493 | ret = __cw1200_irq_enable(priv, 1); | ||
494 | priv->sbus_ops->unlock(priv->sbus_priv); | ||
495 | if (ret < 0) | ||
496 | goto unsubscribe; | ||
497 | |||
498 | /* Configure device for MESSSAGE MODE */ | ||
499 | ret = config_reg_read(priv, &val32); | ||
500 | if (ret < 0) { | ||
501 | pr_err("Can't read config register.\n"); | ||
502 | goto unsubscribe; | ||
503 | } | ||
504 | ret = config_reg_write(priv, val32 & ~ST90TDS_CONFIG_ACCESS_MODE_BIT); | ||
505 | if (ret < 0) { | ||
506 | pr_err("Can't write config register.\n"); | ||
507 | goto unsubscribe; | ||
508 | } | ||
509 | |||
510 | /* Unless we read the CONFIG Register we are | ||
511 | * not able to get an interrupt | ||
512 | */ | ||
513 | mdelay(10); | ||
514 | config_reg_read(priv, &val32); | ||
515 | |||
516 | out: | ||
517 | return ret; | ||
518 | |||
519 | unsubscribe: | ||
520 | /* Disable interrupt signalling */ | ||
521 | priv->sbus_ops->lock(priv->sbus_priv); | ||
522 | ret = __cw1200_irq_enable(priv, 0); | ||
523 | priv->sbus_ops->unlock(priv->sbus_priv); | ||
524 | return ret; | ||
525 | } | ||
diff --git a/drivers/net/wireless/cw1200/fwio.h b/drivers/net/wireless/cw1200/fwio.h new file mode 100644 index 000000000000..ea3099362cdf --- /dev/null +++ b/drivers/net/wireless/cw1200/fwio.h | |||
@@ -0,0 +1,39 @@ | |||
1 | /* | ||
2 | * Firmware API 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 | * ST-Ericsson UMAC CW1200 driver which is | ||
9 | * Copyright (c) 2010, ST-Ericsson | ||
10 | * Author: Ajitpal Singh <ajitpal.singh@stericsson.com> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | */ | ||
16 | |||
17 | #ifndef FWIO_H_INCLUDED | ||
18 | #define FWIO_H_INCLUDED | ||
19 | |||
20 | #define BOOTLOADER_CW1X60 "boot_cw1x60.bin" | ||
21 | #define FIRMWARE_CW1X60 "wsm_cw1x60.bin" | ||
22 | #define FIRMWARE_CUT22 "wsm_22.bin" | ||
23 | #define FIRMWARE_CUT20 "wsm_20.bin" | ||
24 | #define FIRMWARE_CUT11 "wsm_11.bin" | ||
25 | #define FIRMWARE_CUT10 "wsm_10.bin" | ||
26 | #define SDD_FILE_CW1X60 "sdd_cw1x60.bin" | ||
27 | #define SDD_FILE_22 "sdd_22.bin" | ||
28 | #define SDD_FILE_20 "sdd_20.bin" | ||
29 | #define SDD_FILE_11 "sdd_11.bin" | ||
30 | #define SDD_FILE_10 "sdd_10.bin" | ||
31 | |||
32 | int cw1200_load_firmware(struct cw1200_common *priv); | ||
33 | |||
34 | /* SDD definitions */ | ||
35 | #define SDD_PTA_CFG_ELT_ID 0xEB | ||
36 | #define SDD_REFERENCE_FREQUENCY_ELT_ID 0xc5 | ||
37 | u32 cw1200_dpll_from_clk(u16 clk); | ||
38 | |||
39 | #endif | ||
diff --git a/drivers/net/wireless/cw1200/hwio.c b/drivers/net/wireless/cw1200/hwio.c new file mode 100644 index 000000000000..1af7b3d421b2 --- /dev/null +++ b/drivers/net/wireless/cw1200/hwio.c | |||
@@ -0,0 +1,311 @@ | |||
1 | /* | ||
2 | * Low-level device IO routines for ST-Ericsson CW1200 drivers | ||
3 | * | ||
4 | * Copyright (c) 2010, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * Based on: | ||
8 | * ST-Ericsson UMAC CW1200 driver, which is | ||
9 | * Copyright (c) 2010, ST-Ericsson | ||
10 | * Author: Ajitpal Singh <ajitpal.singh@lockless.no> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | */ | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | |||
19 | #include "cw1200.h" | ||
20 | #include "hwio.h" | ||
21 | #include "sbus.h" | ||
22 | |||
23 | /* Sdio addr is 4*spi_addr */ | ||
24 | #define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2) | ||
25 | #define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \ | ||
26 | ((((buf_id) & 0x1F) << 7) \ | ||
27 | | (((mpf) & 1) << 6) \ | ||
28 | | (((rfu) & 1) << 5) \ | ||
29 | | (((reg_id_ofs) & 0x1F) << 0)) | ||
30 | #define MAX_RETRY 3 | ||
31 | |||
32 | |||
33 | static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr, | ||
34 | void *buf, size_t buf_len, int buf_id) | ||
35 | { | ||
36 | u16 addr_sdio; | ||
37 | u32 sdio_reg_addr_17bit; | ||
38 | |||
39 | /* Check if buffer is aligned to 4 byte boundary */ | ||
40 | if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) { | ||
41 | pr_err("buffer is not aligned.\n"); | ||
42 | return -EINVAL; | ||
43 | } | ||
44 | |||
45 | /* Convert to SDIO Register Address */ | ||
46 | addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); | ||
47 | sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); | ||
48 | |||
49 | return priv->sbus_ops->sbus_memcpy_fromio(priv->sbus_priv, | ||
50 | sdio_reg_addr_17bit, | ||
51 | buf, buf_len); | ||
52 | } | ||
53 | |||
54 | static int __cw1200_reg_write(struct cw1200_common *priv, u16 addr, | ||
55 | const void *buf, size_t buf_len, int buf_id) | ||
56 | { | ||
57 | u16 addr_sdio; | ||
58 | u32 sdio_reg_addr_17bit; | ||
59 | |||
60 | /* Convert to SDIO Register Address */ | ||
61 | addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); | ||
62 | sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); | ||
63 | |||
64 | return priv->sbus_ops->sbus_memcpy_toio(priv->sbus_priv, | ||
65 | sdio_reg_addr_17bit, | ||
66 | buf, buf_len); | ||
67 | } | ||
68 | |||
69 | static inline int __cw1200_reg_read_32(struct cw1200_common *priv, | ||
70 | u16 addr, u32 *val) | ||
71 | { | ||
72 | int i = __cw1200_reg_read(priv, addr, val, sizeof(*val), 0); | ||
73 | *val = le32_to_cpu(*val); | ||
74 | return i; | ||
75 | } | ||
76 | |||
77 | static inline int __cw1200_reg_write_32(struct cw1200_common *priv, | ||
78 | u16 addr, u32 val) | ||
79 | { | ||
80 | val = cpu_to_le32(val); | ||
81 | return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0); | ||
82 | } | ||
83 | |||
84 | static inline int __cw1200_reg_read_16(struct cw1200_common *priv, | ||
85 | u16 addr, u16 *val) | ||
86 | { | ||
87 | int i = __cw1200_reg_read(priv, addr, val, sizeof(*val), 0); | ||
88 | *val = le16_to_cpu(*val); | ||
89 | return i; | ||
90 | } | ||
91 | |||
92 | static inline int __cw1200_reg_write_16(struct cw1200_common *priv, | ||
93 | u16 addr, u16 val) | ||
94 | { | ||
95 | val = cpu_to_le16(val); | ||
96 | return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0); | ||
97 | } | ||
98 | |||
99 | int cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf, | ||
100 | size_t buf_len) | ||
101 | { | ||
102 | int ret; | ||
103 | priv->sbus_ops->lock(priv->sbus_priv); | ||
104 | ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0); | ||
105 | priv->sbus_ops->unlock(priv->sbus_priv); | ||
106 | return ret; | ||
107 | } | ||
108 | |||
109 | int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf, | ||
110 | size_t buf_len) | ||
111 | { | ||
112 | int ret; | ||
113 | priv->sbus_ops->lock(priv->sbus_priv); | ||
114 | ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0); | ||
115 | priv->sbus_ops->unlock(priv->sbus_priv); | ||
116 | return ret; | ||
117 | } | ||
118 | |||
119 | int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len) | ||
120 | { | ||
121 | int ret, retry = 1; | ||
122 | int buf_id_rx = priv->buf_id_rx; | ||
123 | |||
124 | priv->sbus_ops->lock(priv->sbus_priv); | ||
125 | |||
126 | while (retry <= MAX_RETRY) { | ||
127 | ret = __cw1200_reg_read(priv, | ||
128 | ST90TDS_IN_OUT_QUEUE_REG_ID, buf, | ||
129 | buf_len, buf_id_rx + 1); | ||
130 | if (!ret) { | ||
131 | buf_id_rx = (buf_id_rx + 1) & 3; | ||
132 | priv->buf_id_rx = buf_id_rx; | ||
133 | break; | ||
134 | } else { | ||
135 | retry++; | ||
136 | mdelay(1); | ||
137 | pr_err("error :[%d]\n", ret); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | priv->sbus_ops->unlock(priv->sbus_priv); | ||
142 | return ret; | ||
143 | } | ||
144 | |||
145 | int cw1200_data_write(struct cw1200_common *priv, const void *buf, | ||
146 | size_t buf_len) | ||
147 | { | ||
148 | int ret, retry = 1; | ||
149 | int buf_id_tx = priv->buf_id_tx; | ||
150 | |||
151 | priv->sbus_ops->lock(priv->sbus_priv); | ||
152 | |||
153 | while (retry <= MAX_RETRY) { | ||
154 | ret = __cw1200_reg_write(priv, | ||
155 | ST90TDS_IN_OUT_QUEUE_REG_ID, buf, | ||
156 | buf_len, buf_id_tx); | ||
157 | if (!ret) { | ||
158 | buf_id_tx = (buf_id_tx + 1) & 31; | ||
159 | priv->buf_id_tx = buf_id_tx; | ||
160 | break; | ||
161 | } else { | ||
162 | retry++; | ||
163 | mdelay(1); | ||
164 | pr_err("error :[%d]\n", ret); | ||
165 | } | ||
166 | } | ||
167 | |||
168 | priv->sbus_ops->unlock(priv->sbus_priv); | ||
169 | return ret; | ||
170 | } | ||
171 | |||
172 | int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, | ||
173 | size_t buf_len, u32 prefetch, u16 port_addr) | ||
174 | { | ||
175 | u32 val32 = 0; | ||
176 | int i, ret; | ||
177 | |||
178 | if ((buf_len / 2) >= 0x1000) { | ||
179 | pr_err("Can't read more than 0xfff words.\n"); | ||
180 | return -EINVAL; | ||
181 | goto out; | ||
182 | } | ||
183 | |||
184 | priv->sbus_ops->lock(priv->sbus_priv); | ||
185 | /* Write address */ | ||
186 | ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); | ||
187 | if (ret < 0) { | ||
188 | pr_err("Can't write address register.\n"); | ||
189 | goto out; | ||
190 | } | ||
191 | |||
192 | /* Read CONFIG Register Value - We will read 32 bits */ | ||
193 | ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); | ||
194 | if (ret < 0) { | ||
195 | pr_err("Can't read config register.\n"); | ||
196 | goto out; | ||
197 | } | ||
198 | |||
199 | /* Set PREFETCH bit */ | ||
200 | ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, | ||
201 | val32 | prefetch); | ||
202 | if (ret < 0) { | ||
203 | pr_err("Can't write prefetch bit.\n"); | ||
204 | goto out; | ||
205 | } | ||
206 | |||
207 | /* Check for PRE-FETCH bit to be cleared */ | ||
208 | for (i = 0; i < 20; i++) { | ||
209 | ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); | ||
210 | if (ret < 0) { | ||
211 | pr_err("Can't check prefetch bit.\n"); | ||
212 | goto out; | ||
213 | } | ||
214 | if (!(val32 & prefetch)) | ||
215 | break; | ||
216 | |||
217 | mdelay(i); | ||
218 | } | ||
219 | |||
220 | if (val32 & prefetch) { | ||
221 | pr_err("Prefetch bit is not cleared.\n"); | ||
222 | goto out; | ||
223 | } | ||
224 | |||
225 | /* Read data port */ | ||
226 | ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0); | ||
227 | if (ret < 0) { | ||
228 | pr_err("Can't read data port.\n"); | ||
229 | goto out; | ||
230 | } | ||
231 | |||
232 | out: | ||
233 | priv->sbus_ops->unlock(priv->sbus_priv); | ||
234 | return ret; | ||
235 | } | ||
236 | |||
237 | int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf, | ||
238 | size_t buf_len) | ||
239 | { | ||
240 | int ret; | ||
241 | |||
242 | if ((buf_len / 2) >= 0x1000) { | ||
243 | pr_err("Can't write more than 0xfff words.\n"); | ||
244 | return -EINVAL; | ||
245 | } | ||
246 | |||
247 | priv->sbus_ops->lock(priv->sbus_priv); | ||
248 | |||
249 | /* Write address */ | ||
250 | ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); | ||
251 | if (ret < 0) { | ||
252 | pr_err("Can't write address register.\n"); | ||
253 | goto out; | ||
254 | } | ||
255 | |||
256 | /* Write data port */ | ||
257 | ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID, | ||
258 | buf, buf_len, 0); | ||
259 | if (ret < 0) { | ||
260 | pr_err("Can't write data port.\n"); | ||
261 | goto out; | ||
262 | } | ||
263 | |||
264 | out: | ||
265 | priv->sbus_ops->unlock(priv->sbus_priv); | ||
266 | return ret; | ||
267 | } | ||
268 | |||
269 | int __cw1200_irq_enable(struct cw1200_common *priv, int enable) | ||
270 | { | ||
271 | u32 val32; | ||
272 | u16 val16; | ||
273 | int ret; | ||
274 | |||
275 | if (HIF_8601_SILICON == priv->hw_type) { | ||
276 | ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); | ||
277 | if (ret < 0) { | ||
278 | pr_err("Can't read config register.\n"); | ||
279 | return ret; | ||
280 | } | ||
281 | |||
282 | if (enable) | ||
283 | val32 |= ST90TDS_CONF_IRQ_RDY_ENABLE; | ||
284 | else | ||
285 | val32 &= ~ST90TDS_CONF_IRQ_RDY_ENABLE; | ||
286 | |||
287 | ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val32); | ||
288 | if (ret < 0) { | ||
289 | pr_err("Can't write config register.\n"); | ||
290 | return ret; | ||
291 | } | ||
292 | } else { | ||
293 | ret = __cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16); | ||
294 | if (ret < 0) { | ||
295 | pr_err("Can't read control register.\n"); | ||
296 | return ret; | ||
297 | } | ||
298 | |||
299 | if (enable) | ||
300 | val16 |= ST90TDS_CONT_IRQ_RDY_ENABLE; | ||
301 | else | ||
302 | val16 &= ~ST90TDS_CONT_IRQ_RDY_ENABLE; | ||
303 | |||
304 | ret = __cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, val16); | ||
305 | if (ret < 0) { | ||
306 | pr_err("Can't write control register.\n"); | ||
307 | return ret; | ||
308 | } | ||
309 | } | ||
310 | return 0; | ||
311 | } | ||
diff --git a/drivers/net/wireless/cw1200/hwio.h b/drivers/net/wireless/cw1200/hwio.h new file mode 100644 index 000000000000..7ee73fe32ccf --- /dev/null +++ b/drivers/net/wireless/cw1200/hwio.h | |||
@@ -0,0 +1,247 @@ | |||
1 | /* | ||
2 | * Low-level API 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 | * ST-Ericsson UMAC CW1200 driver which is | ||
9 | * Copyright (c) 2010, ST-Ericsson | ||
10 | * Author: Ajitpal Singh <ajitpal.singh@stericsson.com> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | */ | ||
16 | |||
17 | #ifndef CW1200_HWIO_H_INCLUDED | ||
18 | #define CW1200_HWIO_H_INCLUDED | ||
19 | |||
20 | /* extern */ struct cw1200_common; | ||
21 | |||
22 | #define CW1200_CUT_11_ID_STR (0x302E3830) | ||
23 | #define CW1200_CUT_22_ID_STR1 (0x302e3132) | ||
24 | #define CW1200_CUT_22_ID_STR2 (0x32302e30) | ||
25 | #define CW1200_CUT_22_ID_STR3 (0x3335) | ||
26 | #define CW1200_CUT_ID_ADDR (0xFFF17F90) | ||
27 | #define CW1200_CUT2_ID_ADDR (0xFFF1FF90) | ||
28 | |||
29 | /* Download control area */ | ||
30 | /* boot loader start address in SRAM */ | ||
31 | #define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000) | ||
32 | /* 32K, 0x4000 to 0xDFFF */ | ||
33 | #define DOWNLOAD_FIFO_OFFSET (0x00004000) | ||
34 | /* 32K */ | ||
35 | #define DOWNLOAD_FIFO_SIZE (0x00008000) | ||
36 | /* 128 bytes, 0xFF80 to 0xFFFF */ | ||
37 | #define DOWNLOAD_CTRL_OFFSET (0x0000FF80) | ||
38 | #define DOWNLOAD_CTRL_DATA_DWORDS (32-6) | ||
39 | |||
40 | struct download_cntl_t { | ||
41 | /* size of whole firmware file (including Cheksum), host init */ | ||
42 | u32 image_size; | ||
43 | /* downloading flags */ | ||
44 | u32 flags; | ||
45 | /* No. of bytes put into the download, init & updated by host */ | ||
46 | u32 put; | ||
47 | /* last traced program counter, last ARM reg_pc */ | ||
48 | u32 trace_pc; | ||
49 | /* No. of bytes read from the download, host init, device updates */ | ||
50 | u32 get; | ||
51 | /* r0, boot losader status, host init to pending, device updates */ | ||
52 | u32 status; | ||
53 | /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */ | ||
54 | u32 debug_data[DOWNLOAD_CTRL_DATA_DWORDS]; | ||
55 | }; | ||
56 | |||
57 | #define DOWNLOAD_IMAGE_SIZE_REG \ | ||
58 | (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, image_size)) | ||
59 | #define DOWNLOAD_FLAGS_REG \ | ||
60 | (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, flags)) | ||
61 | #define DOWNLOAD_PUT_REG \ | ||
62 | (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, put)) | ||
63 | #define DOWNLOAD_TRACE_PC_REG \ | ||
64 | (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, trace_pc)) | ||
65 | #define DOWNLOAD_GET_REG \ | ||
66 | (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, get)) | ||
67 | #define DOWNLOAD_STATUS_REG \ | ||
68 | (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, status)) | ||
69 | #define DOWNLOAD_DEBUG_DATA_REG \ | ||
70 | (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, debug_data)) | ||
71 | #define DOWNLOAD_DEBUG_DATA_LEN (108) | ||
72 | |||
73 | #define DOWNLOAD_BLOCK_SIZE (1024) | ||
74 | |||
75 | /* For boot loader detection */ | ||
76 | #define DOWNLOAD_ARE_YOU_HERE (0x87654321) | ||
77 | #define DOWNLOAD_I_AM_HERE (0x12345678) | ||
78 | |||
79 | /* Download error code */ | ||
80 | #define DOWNLOAD_PENDING (0xFFFFFFFF) | ||
81 | #define DOWNLOAD_SUCCESS (0) | ||
82 | #define DOWNLOAD_EXCEPTION (1) | ||
83 | #define DOWNLOAD_ERR_MEM_1 (2) | ||
84 | #define DOWNLOAD_ERR_MEM_2 (3) | ||
85 | #define DOWNLOAD_ERR_SOFTWARE (4) | ||
86 | #define DOWNLOAD_ERR_FILE_SIZE (5) | ||
87 | #define DOWNLOAD_ERR_CHECKSUM (6) | ||
88 | #define DOWNLOAD_ERR_OVERFLOW (7) | ||
89 | #define DOWNLOAD_ERR_IMAGE (8) | ||
90 | #define DOWNLOAD_ERR_HOST (9) | ||
91 | #define DOWNLOAD_ERR_ABORT (10) | ||
92 | |||
93 | |||
94 | #define SYS_BASE_ADDR_SILICON (0) | ||
95 | #define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000) | ||
96 | #define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON) | ||
97 | |||
98 | #define CW1200_APB(addr) (PAC_SHARED_MEMORY_SILICON + (addr)) | ||
99 | |||
100 | /* *************************************************************** | ||
101 | *Device register definitions | ||
102 | *************************************************************** */ | ||
103 | /* WBF - SPI Register Addresses */ | ||
104 | #define ST90TDS_ADDR_ID_BASE (0x0000) | ||
105 | /* 16/32 bits */ | ||
106 | #define ST90TDS_CONFIG_REG_ID (0x0000) | ||
107 | /* 16/32 bits */ | ||
108 | #define ST90TDS_CONTROL_REG_ID (0x0001) | ||
109 | /* 16 bits, Q mode W/R */ | ||
110 | #define ST90TDS_IN_OUT_QUEUE_REG_ID (0x0002) | ||
111 | /* 32 bits, AHB bus R/W */ | ||
112 | #define ST90TDS_AHB_DPORT_REG_ID (0x0003) | ||
113 | /* 16/32 bits */ | ||
114 | #define ST90TDS_SRAM_BASE_ADDR_REG_ID (0x0004) | ||
115 | /* 32 bits, APB bus R/W */ | ||
116 | #define ST90TDS_SRAM_DPORT_REG_ID (0x0005) | ||
117 | /* 32 bits, t_settle/general */ | ||
118 | #define ST90TDS_TSET_GEN_R_W_REG_ID (0x0006) | ||
119 | /* 16 bits, Q mode read, no length */ | ||
120 | #define ST90TDS_FRAME_OUT_REG_ID (0x0007) | ||
121 | #define ST90TDS_ADDR_ID_MAX (ST90TDS_FRAME_OUT_REG_ID) | ||
122 | |||
123 | /* WBF - Control register bit set */ | ||
124 | /* next o/p length, bit 11 to 0 */ | ||
125 | #define ST90TDS_CONT_NEXT_LEN_MASK (0x0FFF) | ||
126 | #define ST90TDS_CONT_WUP_BIT (BIT(12)) | ||
127 | #define ST90TDS_CONT_RDY_BIT (BIT(13)) | ||
128 | #define ST90TDS_CONT_IRQ_ENABLE (BIT(14)) | ||
129 | #define ST90TDS_CONT_RDY_ENABLE (BIT(15)) | ||
130 | #define ST90TDS_CONT_IRQ_RDY_ENABLE (BIT(14)|BIT(15)) | ||
131 | |||
132 | /* SPI Config register bit set */ | ||
133 | #define ST90TDS_CONFIG_FRAME_BIT (BIT(2)) | ||
134 | #define ST90TDS_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4)) | ||
135 | #define ST90TDS_CONFIG_WORD_MODE_1 (BIT(3)) | ||
136 | #define ST90TDS_CONFIG_WORD_MODE_2 (BIT(4)) | ||
137 | #define ST90TDS_CONFIG_ERROR_0_BIT (BIT(5)) | ||
138 | #define ST90TDS_CONFIG_ERROR_1_BIT (BIT(6)) | ||
139 | #define ST90TDS_CONFIG_ERROR_2_BIT (BIT(7)) | ||
140 | /* TBD: Sure??? */ | ||
141 | #define ST90TDS_CONFIG_CSN_FRAME_BIT (BIT(7)) | ||
142 | #define ST90TDS_CONFIG_ERROR_3_BIT (BIT(8)) | ||
143 | #define ST90TDS_CONFIG_ERROR_4_BIT (BIT(9)) | ||
144 | /* QueueM */ | ||
145 | #define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10)) | ||
146 | /* AHB bus */ | ||
147 | #define ST90TDS_CONFIG_AHB_PRFETCH_BIT (BIT(11)) | ||
148 | #define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12)) | ||
149 | /* APB bus */ | ||
150 | #define ST90TDS_CONFIG_PRFETCH_BIT (BIT(13)) | ||
151 | /* cpu reset */ | ||
152 | #define ST90TDS_CONFIG_CPU_RESET_BIT (BIT(14)) | ||
153 | #define ST90TDS_CONFIG_CLEAR_INT_BIT (BIT(15)) | ||
154 | |||
155 | /* For CW1200 the IRQ Enable and Ready Bits are in CONFIG register */ | ||
156 | #define ST90TDS_CONF_IRQ_ENABLE (BIT(16)) | ||
157 | #define ST90TDS_CONF_RDY_ENABLE (BIT(17)) | ||
158 | #define ST90TDS_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17)) | ||
159 | |||
160 | int cw1200_data_read(struct cw1200_common *priv, | ||
161 | void *buf, size_t buf_len); | ||
162 | int cw1200_data_write(struct cw1200_common *priv, | ||
163 | const void *buf, size_t buf_len); | ||
164 | |||
165 | int cw1200_reg_read(struct cw1200_common *priv, u16 addr, | ||
166 | void *buf, size_t buf_len); | ||
167 | int cw1200_reg_write(struct cw1200_common *priv, u16 addr, | ||
168 | const void *buf, size_t buf_len); | ||
169 | |||
170 | static inline int cw1200_reg_read_16(struct cw1200_common *priv, | ||
171 | u16 addr, u16 *val) | ||
172 | { | ||
173 | u32 tmp; | ||
174 | int i; | ||
175 | i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp)); | ||
176 | tmp = le32_to_cpu(tmp); | ||
177 | *val = tmp & 0xffff; | ||
178 | return i; | ||
179 | } | ||
180 | |||
181 | static inline int cw1200_reg_write_16(struct cw1200_common *priv, | ||
182 | u16 addr, u16 val) | ||
183 | { | ||
184 | u32 tmp = val; | ||
185 | tmp = cpu_to_le32(tmp); | ||
186 | return cw1200_reg_write(priv, addr, &tmp, sizeof(tmp)); | ||
187 | } | ||
188 | |||
189 | static inline int cw1200_reg_read_32(struct cw1200_common *priv, | ||
190 | u16 addr, u32 *val) | ||
191 | { | ||
192 | int i = cw1200_reg_read(priv, addr, val, sizeof(*val)); | ||
193 | *val = le32_to_cpu(*val); | ||
194 | return i; | ||
195 | } | ||
196 | |||
197 | static inline int cw1200_reg_write_32(struct cw1200_common *priv, | ||
198 | u16 addr, u32 val) | ||
199 | { | ||
200 | val = cpu_to_le32(val); | ||
201 | return cw1200_reg_write(priv, addr, &val, sizeof(val)); | ||
202 | } | ||
203 | |||
204 | int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, | ||
205 | size_t buf_len, u32 prefetch, u16 port_addr); | ||
206 | int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf, | ||
207 | size_t buf_len); | ||
208 | |||
209 | static inline int cw1200_apb_read(struct cw1200_common *priv, u32 addr, | ||
210 | void *buf, size_t buf_len) | ||
211 | { | ||
212 | return cw1200_indirect_read(priv, addr, buf, buf_len, | ||
213 | ST90TDS_CONFIG_PRFETCH_BIT, | ||
214 | ST90TDS_SRAM_DPORT_REG_ID); | ||
215 | } | ||
216 | |||
217 | static inline int cw1200_ahb_read(struct cw1200_common *priv, u32 addr, | ||
218 | void *buf, size_t buf_len) | ||
219 | { | ||
220 | return cw1200_indirect_read(priv, addr, buf, buf_len, | ||
221 | ST90TDS_CONFIG_AHB_PRFETCH_BIT, | ||
222 | ST90TDS_AHB_DPORT_REG_ID); | ||
223 | } | ||
224 | |||
225 | static inline int cw1200_apb_read_32(struct cw1200_common *priv, | ||
226 | u32 addr, u32 *val) | ||
227 | { | ||
228 | int i = cw1200_apb_read(priv, addr, val, sizeof(*val)); | ||
229 | *val = le32_to_cpu(*val); | ||
230 | return i; | ||
231 | } | ||
232 | |||
233 | static inline int cw1200_apb_write_32(struct cw1200_common *priv, | ||
234 | u32 addr, u32 val) | ||
235 | { | ||
236 | val = cpu_to_le32(val); | ||
237 | return cw1200_apb_write(priv, addr, &val, sizeof(val)); | ||
238 | } | ||
239 | static inline int cw1200_ahb_read_32(struct cw1200_common *priv, | ||
240 | u32 addr, u32 *val) | ||
241 | { | ||
242 | int i = cw1200_ahb_read(priv, addr, val, sizeof(*val)); | ||
243 | *val = le32_to_cpu(*val); | ||
244 | return i; | ||
245 | } | ||
246 | |||
247 | #endif /* CW1200_HWIO_H_INCLUDED */ | ||
diff --git a/drivers/net/wireless/cw1200/itp.c b/drivers/net/wireless/cw1200/itp.c new file mode 100644 index 000000000000..c0730bb49b75 --- /dev/null +++ b/drivers/net/wireless/cw1200/itp.c | |||
@@ -0,0 +1,730 @@ | |||
1 | /* | ||
2 | * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers | ||
3 | * ITP code | ||
4 | * | ||
5 | * Copyright (c) 2010, ST-Ericsson | ||
6 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/debugfs.h> | ||
15 | #include <linux/poll.h> | ||
16 | #include <linux/time.h> | ||
17 | #include <linux/random.h> | ||
18 | #include <linux/kallsyms.h> | ||
19 | #include <net/mac80211.h> | ||
20 | #include "cw1200.h" | ||
21 | #include "debug.h" | ||
22 | #include "itp.h" | ||
23 | #include "sta.h" | ||
24 | |||
25 | static int __cw1200_itp_open(struct cw1200_common *priv); | ||
26 | static int __cw1200_itp_close(struct cw1200_common *priv); | ||
27 | static void cw1200_itp_rx_start(struct cw1200_common *priv); | ||
28 | static void cw1200_itp_rx_stop(struct cw1200_common *priv); | ||
29 | static void cw1200_itp_rx_stats(struct cw1200_common *priv); | ||
30 | static void cw1200_itp_rx_reset(struct cw1200_common *priv); | ||
31 | static void cw1200_itp_tx_stop(struct cw1200_common *priv); | ||
32 | static void cw1200_itp_handle(struct cw1200_common *priv, | ||
33 | struct sk_buff *skb); | ||
34 | static void cw1200_itp_err(struct cw1200_common *priv, | ||
35 | int err, | ||
36 | int arg); | ||
37 | static void __cw1200_itp_tx_stop(struct cw1200_common *priv); | ||
38 | |||
39 | static ssize_t cw1200_itp_read(struct file *file, | ||
40 | char __user *user_buf, size_t count, loff_t *ppos) | ||
41 | { | ||
42 | struct cw1200_common *priv = file->private_data; | ||
43 | struct cw1200_itp *itp = &priv->debug->itp; | ||
44 | struct sk_buff *skb; | ||
45 | int ret; | ||
46 | |||
47 | if (skb_queue_empty(&itp->log_queue)) | ||
48 | return 0; | ||
49 | |||
50 | skb = skb_dequeue(&itp->log_queue); | ||
51 | ret = copy_to_user(user_buf, skb->data, skb->len); | ||
52 | *ppos += skb->len; | ||
53 | skb->data[skb->len] = 0; | ||
54 | pr_debug("[ITP] >>> %s", skb->data); | ||
55 | consume_skb(skb); | ||
56 | |||
57 | return skb->len - ret; | ||
58 | } | ||
59 | |||
60 | static ssize_t cw1200_itp_write(struct file *file, | ||
61 | const char __user *user_buf, size_t count, loff_t *ppos) | ||
62 | { | ||
63 | struct cw1200_common *priv = file->private_data; | ||
64 | struct sk_buff *skb; | ||
65 | |||
66 | if (!count || count > 1024) | ||
67 | return -EINVAL; | ||
68 | skb = dev_alloc_skb(count + 1); | ||
69 | if (!skb) | ||
70 | return -ENOMEM; | ||
71 | skb_trim(skb, 0); | ||
72 | skb_put(skb, count + 1); | ||
73 | if (copy_from_user(skb->data, user_buf, count)) { | ||
74 | kfree_skb(skb); | ||
75 | return -EFAULT; | ||
76 | } | ||
77 | skb->data[count] = 0; | ||
78 | |||
79 | cw1200_itp_handle(priv, skb); | ||
80 | consume_skb(skb); | ||
81 | return count; | ||
82 | } | ||
83 | |||
84 | static unsigned int cw1200_itp_poll(struct file *file, poll_table *wait) | ||
85 | { | ||
86 | struct cw1200_common *priv = file->private_data; | ||
87 | struct cw1200_itp *itp = &priv->debug->itp; | ||
88 | unsigned int mask = 0; | ||
89 | |||
90 | poll_wait(file, &itp->read_wait, wait); | ||
91 | |||
92 | if (!skb_queue_empty(&itp->log_queue)) | ||
93 | mask |= POLLIN | POLLRDNORM; | ||
94 | |||
95 | mask |= POLLOUT | POLLWRNORM; | ||
96 | |||
97 | return mask; | ||
98 | } | ||
99 | |||
100 | static int cw1200_itp_open(struct inode *inode, struct file *file) | ||
101 | { | ||
102 | struct cw1200_common *priv = inode->i_private; | ||
103 | struct cw1200_itp *itp = &priv->debug->itp; | ||
104 | int ret = 0; | ||
105 | |||
106 | file->private_data = priv; | ||
107 | if (atomic_inc_return(&itp->open_count) == 1) { | ||
108 | ret = __cw1200_itp_open(priv); | ||
109 | if (ret && !atomic_dec_return(&itp->open_count)) | ||
110 | __cw1200_itp_close(priv); | ||
111 | } else { | ||
112 | atomic_dec(&itp->open_count); | ||
113 | ret = -EBUSY; | ||
114 | } | ||
115 | |||
116 | return ret; | ||
117 | } | ||
118 | |||
119 | static int cw1200_itp_close(struct inode *inode, struct file *file) | ||
120 | { | ||
121 | struct cw1200_common *priv = file->private_data; | ||
122 | struct cw1200_itp *itp = &priv->debug->itp; | ||
123 | if (!atomic_dec_return(&itp->open_count)) { | ||
124 | __cw1200_itp_close(priv); | ||
125 | wake_up(&itp->close_wait); | ||
126 | } | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static const struct file_operations fops_itp = { | ||
131 | .open = cw1200_itp_open, | ||
132 | .read = cw1200_itp_read, | ||
133 | .write = cw1200_itp_write, | ||
134 | .poll = cw1200_itp_poll, | ||
135 | .release = cw1200_itp_close, | ||
136 | .llseek = default_llseek, | ||
137 | .owner = THIS_MODULE, | ||
138 | }; | ||
139 | |||
140 | static void cw1200_itp_fill_pattern(u8 *data, int size, | ||
141 | enum cw1200_itp_data_modes mode) | ||
142 | { | ||
143 | if (size <= 0) | ||
144 | return; | ||
145 | |||
146 | switch (mode) { | ||
147 | default: | ||
148 | case ITP_DATA_ZEROS: | ||
149 | memset(data, 0x0, size); | ||
150 | break; | ||
151 | case ITP_DATA_ONES: | ||
152 | memset(data, 0xff, size); | ||
153 | break; | ||
154 | case ITP_DATA_ZERONES: | ||
155 | memset(data, 0x55, size); | ||
156 | break; | ||
157 | case ITP_DATA_RANDOM: | ||
158 | get_random_bytes(data, size); | ||
159 | break; | ||
160 | } | ||
161 | return; | ||
162 | } | ||
163 | |||
164 | static void cw1200_itp_tx_work(struct work_struct *work) | ||
165 | { | ||
166 | struct cw1200_itp *itp = container_of(work, struct cw1200_itp, | ||
167 | tx_work.work); | ||
168 | struct cw1200_common *priv = itp->priv; | ||
169 | atomic_set(&priv->bh_tx, 1); | ||
170 | wake_up(&priv->bh_wq); | ||
171 | } | ||
172 | |||
173 | static void cw1200_itp_tx_finish(struct work_struct *work) | ||
174 | { | ||
175 | struct cw1200_itp *itp = container_of(work, struct cw1200_itp, | ||
176 | tx_finish.work); | ||
177 | __cw1200_itp_tx_stop(itp->priv); | ||
178 | } | ||
179 | |||
180 | int cw1200_itp_init(struct cw1200_common *priv) | ||
181 | { | ||
182 | struct cw1200_itp *itp = &priv->debug->itp; | ||
183 | |||
184 | itp->priv = priv; | ||
185 | atomic_set(&itp->open_count, 0); | ||
186 | atomic_set(&itp->stop_tx, 0); | ||
187 | atomic_set(&itp->awaiting_confirm, 0); | ||
188 | skb_queue_head_init(&itp->log_queue); | ||
189 | spin_lock_init(&itp->tx_lock); | ||
190 | init_waitqueue_head(&itp->read_wait); | ||
191 | init_waitqueue_head(&itp->write_wait); | ||
192 | init_waitqueue_head(&itp->close_wait); | ||
193 | INIT_DELAYED_WORK(&itp->tx_work, cw1200_itp_tx_work); | ||
194 | INIT_DELAYED_WORK(&itp->tx_finish, cw1200_itp_tx_finish); | ||
195 | itp->data = NULL; | ||
196 | itp->hdr_len = WSM_TX_EXTRA_HEADROOM + | ||
197 | sizeof(struct ieee80211_hdr_3addr); | ||
198 | |||
199 | if (!debugfs_create_file("itp", S_IRUSR | S_IWUSR, | ||
200 | priv->debug->debugfs_phy, priv, &fops_itp)) | ||
201 | return -ENOMEM; | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | void cw1200_itp_release(struct cw1200_common *priv) | ||
207 | { | ||
208 | struct cw1200_itp *itp = &priv->debug->itp; | ||
209 | |||
210 | wait_event_interruptible(itp->close_wait, | ||
211 | !atomic_read(&itp->open_count)); | ||
212 | |||
213 | WARN_ON(atomic_read(&itp->open_count)); | ||
214 | |||
215 | skb_queue_purge(&itp->log_queue); | ||
216 | cw1200_itp_tx_stop(priv); | ||
217 | } | ||
218 | |||
219 | static int __cw1200_itp_open(struct cw1200_common *priv) | ||
220 | { | ||
221 | struct cw1200_itp *itp = &priv->debug->itp; | ||
222 | |||
223 | if (!priv->vif) | ||
224 | return -EINVAL; | ||
225 | if (priv->join_status) | ||
226 | return -EINVAL; | ||
227 | itp->saved_channel = priv->channel; | ||
228 | if (!priv->channel) | ||
229 | priv->channel = &priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; | ||
230 | wsm_set_bssid_filtering(priv, false); | ||
231 | cw1200_itp_rx_reset(priv); | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | static int __cw1200_itp_close(struct cw1200_common *priv) | ||
236 | { | ||
237 | struct cw1200_itp *itp = &priv->debug->itp; | ||
238 | if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) | ||
239 | cw1200_itp_rx_stop(priv); | ||
240 | cw1200_itp_tx_stop(priv); | ||
241 | cw1200_disable_listening(priv); | ||
242 | cw1200_update_filtering(priv); | ||
243 | priv->channel = itp->saved_channel; | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | bool cw1200_is_itp(struct cw1200_common *priv) | ||
248 | { | ||
249 | struct cw1200_itp *itp = &priv->debug->itp; | ||
250 | return atomic_read(&itp->open_count) != 0; | ||
251 | } | ||
252 | |||
253 | static void cw1200_itp_rx_reset(struct cw1200_common *priv) | ||
254 | { | ||
255 | struct cw1200_itp *itp = &priv->debug->itp; | ||
256 | itp->rx_cnt = 0; | ||
257 | itp->rx_rssi = 0; | ||
258 | itp->rx_rssi_max = -1000; | ||
259 | itp->rx_rssi_min = 1000; | ||
260 | } | ||
261 | |||
262 | static void cw1200_itp_rx_start(struct cw1200_common *priv) | ||
263 | { | ||
264 | struct cw1200_itp *itp = &priv->debug->itp; | ||
265 | |||
266 | pr_debug("[ITP] RX start, band = %d, ch = %d\n", | ||
267 | itp->band, itp->ch); | ||
268 | atomic_set(&itp->test_mode, TEST_MODE_RX_TEST); | ||
269 | cw1200_update_listening(priv, false); | ||
270 | priv->channel = &priv->hw-> | ||
271 | wiphy->bands[itp->band]->channels[itp->ch]; | ||
272 | cw1200_update_listening(priv, true); | ||
273 | wsm_set_bssid_filtering(priv, false); | ||
274 | } | ||
275 | |||
276 | static void cw1200_itp_rx_stop(struct cw1200_common *priv) | ||
277 | { | ||
278 | struct cw1200_itp *itp = &priv->debug->itp; | ||
279 | pr_debug("[ITP] RX stop\n"); | ||
280 | atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); | ||
281 | cw1200_itp_rx_reset(priv); | ||
282 | } | ||
283 | |||
284 | static void cw1200_itp_rx_stats(struct cw1200_common *priv) | ||
285 | { | ||
286 | struct cw1200_itp *itp = &priv->debug->itp; | ||
287 | struct sk_buff *skb; | ||
288 | char buf[128]; | ||
289 | int len, ret; | ||
290 | struct wsm_mib_counters_table counters; | ||
291 | |||
292 | ret = wsm_get_counters_table(priv, &counters); | ||
293 | |||
294 | if (ret) | ||
295 | cw1200_itp_err(priv, -EBUSY, 20); | ||
296 | |||
297 | if (!itp->rx_cnt) | ||
298 | len = snprintf(buf, sizeof(buf), "1,0,0,0,0,%d\n", | ||
299 | counters.rx_packet_errors); | ||
300 | else | ||
301 | len = snprintf(buf, sizeof(buf), "1,%d,%ld,%d,%d,%d\n", | ||
302 | itp->rx_cnt, | ||
303 | itp->rx_cnt ? itp->rx_rssi / itp->rx_cnt : 0, | ||
304 | itp->rx_rssi_min, itp->rx_rssi_max, | ||
305 | counters.rx_packet_errors); | ||
306 | |||
307 | if (len <= 0) { | ||
308 | cw1200_itp_err(priv, -EBUSY, 21); | ||
309 | return; | ||
310 | } | ||
311 | |||
312 | skb = dev_alloc_skb(len); | ||
313 | if (!skb) { | ||
314 | cw1200_itp_err(priv, -ENOMEM, 22); | ||
315 | return; | ||
316 | } | ||
317 | |||
318 | itp->rx_cnt = 0; | ||
319 | itp->rx_rssi = 0; | ||
320 | itp->rx_rssi_max = -1000; | ||
321 | itp->rx_rssi_min = 1000; | ||
322 | |||
323 | skb_trim(skb, 0); | ||
324 | skb_put(skb, len); | ||
325 | |||
326 | memcpy(skb->data, buf, len); | ||
327 | skb_queue_tail(&itp->log_queue, skb); | ||
328 | wake_up(&itp->read_wait); | ||
329 | } | ||
330 | |||
331 | static void cw1200_itp_tx_start(struct cw1200_common *priv) | ||
332 | { | ||
333 | struct wsm_tx *tx; | ||
334 | struct ieee80211_hdr_3addr *hdr; | ||
335 | struct cw1200_itp *itp = &priv->debug->itp; | ||
336 | struct wsm_mib_association_mode assoc_mode = { | ||
337 | .flags = WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE, | ||
338 | .preamble = itp->preamble, | ||
339 | }; | ||
340 | int len; | ||
341 | u8 da_addr[6] = ITP_DEFAULT_DA_ADDR; | ||
342 | |||
343 | /* Rates index 4 and 5 are not supported */ | ||
344 | if (itp->rate > 3) | ||
345 | itp->rate += 2; | ||
346 | |||
347 | pr_debug("[ITP] TX start: band = %d, ch = %d, rate = %d, preamble = %d, number = %d, data_mode = %d, interval = %d, power = %d, data_len = %d\n", | ||
348 | itp->band, itp->ch, itp->rate, itp->preamble, | ||
349 | itp->number, itp->data_mode, itp->interval_us, | ||
350 | itp->power, itp->data_len); | ||
351 | |||
352 | len = itp->hdr_len + itp->data_len; | ||
353 | |||
354 | itp->data = kmalloc(len, GFP_KERNEL); | ||
355 | tx = (struct wsm_tx *)itp->data; | ||
356 | tx->hdr.len = itp->data_len + itp->hdr_len; | ||
357 | tx->hdr.id = __cpu_to_le16(0x0004 | 1 << 6); | ||
358 | tx->max_tx_rate = itp->rate; | ||
359 | tx->queue_id = 3; | ||
360 | tx->more = 0; | ||
361 | tx->flags = 0xc; | ||
362 | tx->packet_id = 0x55ff55; | ||
363 | tx->reserved = 0; | ||
364 | tx->expire_time = 1; | ||
365 | |||
366 | if (itp->preamble == ITP_PREAMBLE_GREENFIELD) | ||
367 | tx->ht_tx_parameters = WSM_HT_TX_GREENFIELD; | ||
368 | else if (itp->preamble == ITP_PREAMBLE_MIXED) | ||
369 | tx->ht_tx_parameters = WSM_HT_TX_MIXED; | ||
370 | |||
371 | hdr = (struct ieee80211_hdr_3addr *)&itp->data[sizeof(struct wsm_tx)]; | ||
372 | memset(hdr, 0, sizeof(*hdr)); | ||
373 | hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS); | ||
374 | memcpy(hdr->addr1, da_addr, ETH_ALEN); | ||
375 | memcpy(hdr->addr2, priv->vif->addr, ETH_ALEN); | ||
376 | memcpy(hdr->addr3, da_addr, ETH_ALEN); | ||
377 | |||
378 | cw1200_itp_fill_pattern(&itp->data[itp->hdr_len], | ||
379 | itp->data_len, itp->data_mode); | ||
380 | |||
381 | cw1200_update_listening(priv, false); | ||
382 | priv->channel = &priv->hw->wiphy->bands[itp->band]->channels[itp->ch]; | ||
383 | WARN_ON(wsm_set_output_power(priv, itp->power)); | ||
384 | if (itp->preamble == ITP_PREAMBLE_SHORT || | ||
385 | itp->preamble == ITP_PREAMBLE_LONG) | ||
386 | WARN_ON(wsm_set_association_mode(priv, | ||
387 | &assoc_mode)); | ||
388 | wsm_set_bssid_filtering(priv, false); | ||
389 | cw1200_update_listening(priv, true); | ||
390 | |||
391 | spin_lock_bh(&itp->tx_lock); | ||
392 | atomic_set(&itp->test_mode, TEST_MODE_TX_TEST); | ||
393 | atomic_set(&itp->awaiting_confirm, 0); | ||
394 | atomic_set(&itp->stop_tx, 0); | ||
395 | atomic_set(&priv->bh_tx, 1); | ||
396 | ktime_get_ts(&itp->last_sent); | ||
397 | wake_up(&priv->bh_wq); | ||
398 | spin_unlock_bh(&itp->tx_lock); | ||
399 | } | ||
400 | |||
401 | void __cw1200_itp_tx_stop(struct cw1200_common *priv) | ||
402 | { | ||
403 | struct cw1200_itp *itp = &priv->debug->itp; | ||
404 | spin_lock_bh(&itp->tx_lock); | ||
405 | kfree(itp->data); | ||
406 | itp->data = NULL; | ||
407 | atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); | ||
408 | spin_unlock_bh(&itp->tx_lock); | ||
409 | } | ||
410 | |||
411 | static void cw1200_itp_tx_stop(struct cw1200_common *priv) | ||
412 | { | ||
413 | struct cw1200_itp *itp = &priv->debug->itp; | ||
414 | pr_debug("[ITP] TX stop\n"); | ||
415 | atomic_set(&itp->stop_tx, 1); | ||
416 | flush_workqueue(priv->workqueue); | ||
417 | |||
418 | /* time for FW to confirm all tx requests */ | ||
419 | msleep(500); | ||
420 | |||
421 | __cw1200_itp_tx_stop(priv); | ||
422 | } | ||
423 | |||
424 | static int cw1200_print_fw_version(struct cw1200_common *priv, | ||
425 | u8 *buf, size_t len) | ||
426 | { | ||
427 | return snprintf(buf, len, "%s %d.%d", | ||
428 | cw1200_fw_types[priv->wsm_caps.fw_type], | ||
429 | priv->wsm_caps.fw_ver, | ||
430 | priv->wsm_caps.fw_build); | ||
431 | } | ||
432 | |||
433 | static void cw1200_itp_get_version(struct cw1200_common *priv, | ||
434 | enum cw1200_itp_version_type type) | ||
435 | { | ||
436 | struct cw1200_itp *itp = &priv->debug->itp; | ||
437 | struct sk_buff *skb; | ||
438 | char buf[ITP_BUF_SIZE]; | ||
439 | size_t size = 0; | ||
440 | int len; | ||
441 | pr_debug("[ITP] print %s version\n", | ||
442 | type == ITP_CHIP_ID ? "chip" : "firmware"); | ||
443 | |||
444 | len = snprintf(buf, ITP_BUF_SIZE, "2,"); | ||
445 | if (len <= 0) { | ||
446 | cw1200_itp_err(priv, -EINVAL, 40); | ||
447 | return; | ||
448 | } | ||
449 | size += len; | ||
450 | |||
451 | switch (type) { | ||
452 | case ITP_CHIP_ID: | ||
453 | len = cw1200_print_fw_version(priv, buf+size, | ||
454 | ITP_BUF_SIZE - size); | ||
455 | |||
456 | if (len <= 0) { | ||
457 | cw1200_itp_err(priv, -EINVAL, 41); | ||
458 | return; | ||
459 | } | ||
460 | size += len; | ||
461 | break; | ||
462 | case ITP_FW_VER: | ||
463 | len = snprintf(buf+size, ITP_BUF_SIZE - size, | ||
464 | "%d.%d", priv->wsm_caps.hw_id, | ||
465 | priv->wsm_caps.hw_subid); | ||
466 | if (len <= 0) { | ||
467 | cw1200_itp_err(priv, -EINVAL, 42); | ||
468 | return; | ||
469 | } | ||
470 | size += len; | ||
471 | break; | ||
472 | default: | ||
473 | cw1200_itp_err(priv, -EINVAL, 43); | ||
474 | break; | ||
475 | } | ||
476 | |||
477 | len = snprintf(buf+size, ITP_BUF_SIZE-size, "\n"); | ||
478 | if (len <= 0) { | ||
479 | cw1200_itp_err(priv, -EINVAL, 44); | ||
480 | return; | ||
481 | } | ||
482 | size += len; | ||
483 | |||
484 | skb = dev_alloc_skb(size); | ||
485 | if (!skb) { | ||
486 | cw1200_itp_err(priv, -ENOMEM, 45); | ||
487 | return; | ||
488 | } | ||
489 | |||
490 | skb_trim(skb, 0); | ||
491 | skb_put(skb, size); | ||
492 | |||
493 | memcpy(skb->data, buf, size); | ||
494 | skb_queue_tail(&itp->log_queue, skb); | ||
495 | wake_up(&itp->read_wait); | ||
496 | } | ||
497 | |||
498 | int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, | ||
499 | size_t *tx_len, int *burst) | ||
500 | { | ||
501 | struct cw1200_itp *itp; | ||
502 | struct timespec now; | ||
503 | int time_left_us; | ||
504 | |||
505 | if (!priv->debug) | ||
506 | return 0; | ||
507 | |||
508 | itp = &priv->debug->itp; | ||
509 | |||
510 | if (!itp) | ||
511 | return 0; | ||
512 | |||
513 | spin_lock_bh(&itp->tx_lock); | ||
514 | if (atomic_read(&itp->test_mode) != TEST_MODE_TX_TEST) | ||
515 | goto out; | ||
516 | |||
517 | if (atomic_read(&itp->stop_tx)) | ||
518 | goto out; | ||
519 | |||
520 | if (itp->number == 0) { | ||
521 | atomic_set(&itp->stop_tx, 1); | ||
522 | queue_delayed_work(priv->workqueue, &itp->tx_finish, HZ/10); | ||
523 | goto out; | ||
524 | } | ||
525 | |||
526 | if (!itp->data) | ||
527 | goto out; | ||
528 | |||
529 | if (priv->hw_bufs_used >= 2) { | ||
530 | if (!atomic_read(&priv->bh_rx)) | ||
531 | atomic_set(&priv->bh_rx, 1); | ||
532 | atomic_set(&priv->bh_tx, 1); | ||
533 | goto out; | ||
534 | } | ||
535 | |||
536 | ktime_get_ts(&now); | ||
537 | time_left_us = (itp->last_sent.tv_sec - now.tv_sec)*1000000 + | ||
538 | (itp->last_sent.tv_nsec - now.tv_nsec)/1000 + | ||
539 | itp->interval_us; | ||
540 | |||
541 | if (time_left_us > ITP_TIME_THRES_US) { | ||
542 | queue_delayed_work(priv->workqueue, &itp->tx_work, | ||
543 | ITP_US_TO_MS(time_left_us)*HZ/1000); | ||
544 | goto out; | ||
545 | } | ||
546 | |||
547 | if (time_left_us > 50) | ||
548 | udelay(time_left_us); | ||
549 | |||
550 | if (itp->number > 0) | ||
551 | itp->number--; | ||
552 | |||
553 | *data = itp->data; | ||
554 | *tx_len = itp->data_len + itp->hdr_len; | ||
555 | |||
556 | if (itp->data_mode == ITP_DATA_RANDOM) | ||
557 | cw1200_itp_fill_pattern(&itp->data[itp->hdr_len], | ||
558 | itp->data_len, itp->data_mode); | ||
559 | *burst = 2; | ||
560 | atomic_set(&priv->bh_tx, 1); | ||
561 | ktime_get_ts(&itp->last_sent); | ||
562 | atomic_add(1, &itp->awaiting_confirm); | ||
563 | spin_unlock_bh(&itp->tx_lock); | ||
564 | return 1; | ||
565 | |||
566 | out: | ||
567 | spin_unlock_bh(&itp->tx_lock); | ||
568 | return 0; | ||
569 | } | ||
570 | |||
571 | bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb) | ||
572 | { | ||
573 | struct cw1200_itp *itp = &priv->debug->itp; | ||
574 | struct ieee80211_rx_status *rx = IEEE80211_SKB_RXCB(skb); | ||
575 | int signal; | ||
576 | |||
577 | if (atomic_read(&itp->test_mode) != TEST_MODE_RX_TEST) | ||
578 | return cw1200_is_itp(priv); | ||
579 | if (rx->freq != priv->channel->center_freq) | ||
580 | return true; | ||
581 | |||
582 | signal = rx->signal; | ||
583 | itp->rx_cnt++; | ||
584 | itp->rx_rssi += signal; | ||
585 | if (itp->rx_rssi_min > rx->signal) | ||
586 | itp->rx_rssi_min = rx->signal; | ||
587 | if (itp->rx_rssi_max < rx->signal) | ||
588 | itp->rx_rssi_max = rx->signal; | ||
589 | |||
590 | return true; | ||
591 | } | ||
592 | |||
593 | void cw1200_itp_wake_up_tx(struct cw1200_common *priv) | ||
594 | { | ||
595 | wake_up(&priv->debug->itp.write_wait); | ||
596 | } | ||
597 | |||
598 | bool cw1200_itp_tx_running(struct cw1200_common *priv) | ||
599 | { | ||
600 | if (atomic_read(&priv->debug->itp.awaiting_confirm) || | ||
601 | atomic_read(&priv->debug->itp.test_mode) == | ||
602 | TEST_MODE_TX_TEST) { | ||
603 | atomic_sub(1, &priv->debug->itp.awaiting_confirm); | ||
604 | return true; | ||
605 | } | ||
606 | return false; | ||
607 | } | ||
608 | |||
609 | static void cw1200_itp_handle(struct cw1200_common *priv, | ||
610 | struct sk_buff *skb) | ||
611 | { | ||
612 | struct cw1200_itp *itp = &priv->debug->itp; | ||
613 | const struct wiphy *wiphy = priv->hw->wiphy; | ||
614 | int cmd; | ||
615 | int ret; | ||
616 | |||
617 | pr_debug("[ITP] <<< %s", skb->data); | ||
618 | if (sscanf(skb->data, "%d", &cmd) != 1) { | ||
619 | cw1200_itp_err(priv, -EINVAL, 1); | ||
620 | return; | ||
621 | } | ||
622 | |||
623 | switch (cmd) { | ||
624 | case 1: /* RX test */ | ||
625 | if (atomic_read(&itp->test_mode)) { | ||
626 | cw1200_itp_err(priv, -EBUSY, 0); | ||
627 | return; | ||
628 | } | ||
629 | ret = sscanf(skb->data, "%d,%d,%d", | ||
630 | &cmd, &itp->band, &itp->ch); | ||
631 | if (ret != 3) { | ||
632 | cw1200_itp_err(priv, -EINVAL, ret + 1); | ||
633 | return; | ||
634 | } | ||
635 | if (itp->band >= 2) { | ||
636 | cw1200_itp_err(priv, -EINVAL, 2); | ||
637 | } else if (!wiphy->bands[itp->band]) { | ||
638 | cw1200_itp_err(priv, -EINVAL, 2); | ||
639 | } else if (itp->ch >= wiphy->bands[itp->band]->n_channels) { | ||
640 | cw1200_itp_err(priv, -EINVAL, 3); | ||
641 | } else { | ||
642 | cw1200_itp_rx_stats(priv); | ||
643 | cw1200_itp_rx_start(priv); | ||
644 | } | ||
645 | break; | ||
646 | case 2: /* RX stat */ | ||
647 | cw1200_itp_rx_stats(priv); | ||
648 | break; | ||
649 | case 3: /* RX/TX stop */ | ||
650 | if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) { | ||
651 | cw1200_itp_rx_stats(priv); | ||
652 | cw1200_itp_rx_stop(priv); | ||
653 | } else if (atomic_read(&itp->test_mode) == TEST_MODE_TX_TEST) { | ||
654 | cw1200_itp_tx_stop(priv); | ||
655 | } else { | ||
656 | cw1200_itp_err(priv, -EBUSY, 0); | ||
657 | } | ||
658 | break; | ||
659 | case 4: /* TX start */ | ||
660 | if (atomic_read(&itp->test_mode) != TEST_MODE_NO_TEST) { | ||
661 | cw1200_itp_err(priv, -EBUSY, 0); | ||
662 | return; | ||
663 | } | ||
664 | ret = sscanf(skb->data, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", | ||
665 | &cmd, &itp->band, &itp->ch, &itp->rate, | ||
666 | &itp->preamble, &itp->number, &itp->data_mode, | ||
667 | &itp->interval_us, &itp->power, &itp->data_len); | ||
668 | if (ret != 10) { | ||
669 | cw1200_itp_err(priv, -EINVAL, ret + 1); | ||
670 | return; | ||
671 | } | ||
672 | if (itp->band >= 2) { | ||
673 | cw1200_itp_err(priv, -EINVAL, 2); | ||
674 | } else if (!wiphy->bands[itp->band]) { | ||
675 | cw1200_itp_err(priv, -EINVAL, 2); | ||
676 | } else if (itp->ch >= wiphy->bands[itp->band]->n_channels) { | ||
677 | cw1200_itp_err(priv, -EINVAL, 3); | ||
678 | } else if (itp->rate >= 20) { | ||
679 | cw1200_itp_err(priv, -EINVAL, 4); | ||
680 | } else if (itp->preamble >= ITP_PREAMBLE_MAX) { | ||
681 | cw1200_itp_err(priv, -EINVAL, 5); | ||
682 | } else if (itp->data_mode >= ITP_DATA_MAX_MODE) { | ||
683 | cw1200_itp_err(priv, -EINVAL, 7); | ||
684 | } else if (itp->data_len < ITP_MIN_DATA_SIZE || | ||
685 | itp->data_len > (priv->wsm_caps.input_buffer_size - itp->hdr_len)) { | ||
686 | cw1200_itp_err(priv, -EINVAL, 8); | ||
687 | } else { | ||
688 | cw1200_itp_tx_start(priv); | ||
689 | } | ||
690 | break; | ||
691 | case 5: | ||
692 | cw1200_itp_get_version(priv, ITP_CHIP_ID); | ||
693 | break; | ||
694 | case 6: | ||
695 | cw1200_itp_get_version(priv, ITP_FW_VER); | ||
696 | break; | ||
697 | } | ||
698 | } | ||
699 | |||
700 | static void cw1200_itp_err(struct cw1200_common *priv, | ||
701 | int err, int arg) | ||
702 | { | ||
703 | struct cw1200_itp *itp = &priv->debug->itp; | ||
704 | struct sk_buff *skb; | ||
705 | static char buf[255]; | ||
706 | int len; | ||
707 | |||
708 | len = snprintf(buf, sizeof(buf), "%d,%d\n", | ||
709 | err, arg); | ||
710 | if (len <= 0) | ||
711 | return; | ||
712 | |||
713 | skb = dev_alloc_skb(len); | ||
714 | if (!skb) | ||
715 | return; | ||
716 | |||
717 | skb_trim(skb, 0); | ||
718 | skb_put(skb, len); | ||
719 | |||
720 | memcpy(skb->data, buf, len); | ||
721 | skb_queue_tail(&itp->log_queue, skb); | ||
722 | wake_up(&itp->read_wait); | ||
723 | |||
724 | len = sprint_symbol(buf, | ||
725 | (unsigned long)__builtin_return_address(0)); | ||
726 | if (len <= 0) | ||
727 | return; | ||
728 | pr_debug("[ITP] error %d,%d from %s\n", | ||
729 | err, arg, buf); | ||
730 | } | ||
diff --git a/drivers/net/wireless/cw1200/itp.h b/drivers/net/wireless/cw1200/itp.h new file mode 100644 index 000000000000..1e9dfb7fcadc --- /dev/null +++ b/drivers/net/wireless/cw1200/itp.h | |||
@@ -0,0 +1,144 @@ | |||
1 | /* | ||
2 | * ITP code for ST-Ericsson CW1200 mac80211 driver | ||
3 | * | ||
4 | * Copyright (c) 2011, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef CW1200_ITP_H_INCLUDED | ||
13 | #define CW1200_ITP_H_INCLUDED | ||
14 | |||
15 | struct cw200_common; | ||
16 | struct wsm_tx_confirm; | ||
17 | struct dentry; | ||
18 | |||
19 | #ifdef CONFIG_CW1200_ITP | ||
20 | |||
21 | /*extern*/ struct ieee80211_channel; | ||
22 | |||
23 | #define TEST_MODE_NO_TEST (0) | ||
24 | #define TEST_MODE_RX_TEST (1) | ||
25 | #define TEST_MODE_TX_TEST (2) | ||
26 | #define ITP_DEFAULT_DA_ADDR {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} | ||
27 | #define ITP_MIN_DATA_SIZE 6 | ||
28 | #define ITP_MAX_DATA_SIZE 1600 | ||
29 | #define ITP_TIME_THRES_US 10000 | ||
30 | #define ITP_US_TO_MS(x) ((x)/1000) | ||
31 | #define ITP_MS_TO_US(x) ((x)*1000) | ||
32 | #define ITP_BUF_SIZE 255 | ||
33 | |||
34 | |||
35 | enum cw1200_itp_data_modes { | ||
36 | ITP_DATA_ZEROS, | ||
37 | ITP_DATA_ONES, | ||
38 | ITP_DATA_ZERONES, | ||
39 | ITP_DATA_RANDOM, | ||
40 | ITP_DATA_MAX_MODE, | ||
41 | }; | ||
42 | |||
43 | enum cw1200_itp_version_type { | ||
44 | ITP_CHIP_ID, | ||
45 | ITP_FW_VER, | ||
46 | }; | ||
47 | |||
48 | enum cw1200_itp_preamble_type { | ||
49 | ITP_PREAMBLE_LONG, | ||
50 | ITP_PREAMBLE_SHORT, | ||
51 | ITP_PREAMBLE_OFDM, | ||
52 | ITP_PREAMBLE_MIXED, | ||
53 | ITP_PREAMBLE_GREENFIELD, | ||
54 | ITP_PREAMBLE_MAX, | ||
55 | }; | ||
56 | |||
57 | |||
58 | struct cw1200_itp { | ||
59 | struct cw1200_common *priv; | ||
60 | atomic_t open_count; | ||
61 | atomic_t awaiting_confirm; | ||
62 | struct sk_buff_head log_queue; | ||
63 | wait_queue_head_t read_wait; | ||
64 | wait_queue_head_t write_wait; | ||
65 | wait_queue_head_t close_wait; | ||
66 | struct ieee80211_channel *saved_channel; | ||
67 | atomic_t stop_tx; | ||
68 | struct delayed_work tx_work; | ||
69 | struct delayed_work tx_finish; | ||
70 | spinlock_t tx_lock; | ||
71 | struct timespec last_sent; | ||
72 | atomic_t test_mode; | ||
73 | int rx_cnt; | ||
74 | long rx_rssi; | ||
75 | int rx_rssi_max; | ||
76 | int rx_rssi_min; | ||
77 | unsigned band; | ||
78 | unsigned ch; | ||
79 | unsigned rate; | ||
80 | unsigned preamble; | ||
81 | unsigned int number; | ||
82 | unsigned data_mode; | ||
83 | int interval_us; | ||
84 | int power; | ||
85 | u8 *data; | ||
86 | int hdr_len; | ||
87 | int data_len; | ||
88 | }; | ||
89 | |||
90 | int cw1200_itp_init(struct cw1200_common *priv); | ||
91 | void cw1200_itp_release(struct cw1200_common *priv); | ||
92 | |||
93 | bool cw1200_is_itp(struct cw1200_common *priv); | ||
94 | bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb); | ||
95 | void cw1200_itp_wake_up_tx(struct cw1200_common *priv); | ||
96 | int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, | ||
97 | size_t *tx_len, int *burst); | ||
98 | bool cw1200_itp_tx_running(struct cw1200_common *priv); | ||
99 | |||
100 | #else /* CONFIG_CW1200_ITP */ | ||
101 | |||
102 | static inline int cw1200_itp_init(struct cw1200_common *priv) | ||
103 | { | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static inline void cw1200_itp_release(struct cw1200_common *priv) | ||
108 | { | ||
109 | } | ||
110 | |||
111 | static inline bool cw1200_is_itp(struct cw1200_common *priv) | ||
112 | { | ||
113 | return false; | ||
114 | } | ||
115 | |||
116 | static inline bool cw1200_itp_rxed(struct cw1200_common *priv, | ||
117 | struct sk_buff *skb) | ||
118 | { | ||
119 | return false; | ||
120 | } | ||
121 | |||
122 | |||
123 | static inline void cw1200_itp_consume_txed(struct cw1200_common *priv) | ||
124 | { | ||
125 | } | ||
126 | |||
127 | static inline void cw1200_itp_wake_up_tx(struct cw1200_common *priv) | ||
128 | { | ||
129 | } | ||
130 | |||
131 | static inline int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, | ||
132 | size_t *tx_len, int *burst) | ||
133 | { | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | static inline bool cw1200_itp_tx_running(struct cw1200_common *priv) | ||
138 | { | ||
139 | return false; | ||
140 | } | ||
141 | |||
142 | #endif /* CONFIG_CW1200_ITP */ | ||
143 | |||
144 | #endif /* CW1200_ITP_H_INCLUDED */ | ||
diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c new file mode 100644 index 000000000000..8426d3d7607e --- /dev/null +++ b/drivers/net/wireless/cw1200/main.c | |||
@@ -0,0 +1,618 @@ | |||
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/init.h> | ||
25 | #include <linux/firmware.h> | ||
26 | #include <linux/etherdevice.h> | ||
27 | #include <linux/vmalloc.h> | ||
28 | #include <linux/random.h> | ||
29 | #include <linux/sched.h> | ||
30 | #include <net/mac80211.h> | ||
31 | |||
32 | #include "cw1200.h" | ||
33 | #include "txrx.h" | ||
34 | #include "sbus.h" | ||
35 | #include "fwio.h" | ||
36 | #include "hwio.h" | ||
37 | #include "bh.h" | ||
38 | #include "sta.h" | ||
39 | #include "scan.h" | ||
40 | #include "debug.h" | ||
41 | #include "pm.h" | ||
42 | |||
43 | MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>"); | ||
44 | MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code"); | ||
45 | MODULE_LICENSE("GPL"); | ||
46 | MODULE_ALIAS("cw1200_core"); | ||
47 | |||
48 | /* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */ | ||
49 | static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00}; | ||
50 | module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO); | ||
51 | MODULE_PARM_DESC(macaddr, "Override platform_data MAC address"); | ||
52 | |||
53 | static char *cw1200_sdd_path; | ||
54 | module_param(cw1200_sdd_path, charp, 0644); | ||
55 | MODULE_PARM_DESC(cw1200_sdd_path, "Override platform_data SDD file"); | ||
56 | static int cw1200_refclk; | ||
57 | module_param(cw1200_refclk, int, 0644); | ||
58 | MODULE_PARM_DESC(cw1200_refclk, "Override platform_data reference clock"); | ||
59 | |||
60 | int cw1200_power_mode = wsm_power_mode_quiescent; | ||
61 | module_param(cw1200_power_mode, int, 0644); | ||
62 | MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode. 0 == active, 1 == doze, 2 == quiescent (default)"); | ||
63 | |||
64 | #ifdef CONFIG_CW1200_ETF | ||
65 | int etf_mode; | ||
66 | module_param(etf_mode, int, 0644); | ||
67 | MODULE_PARM_DESC(etf_mode, "Enable EngineeringTestingFramework operation"); | ||
68 | #endif | ||
69 | |||
70 | #define RATETAB_ENT(_rate, _rateid, _flags) \ | ||
71 | { \ | ||
72 | .bitrate = (_rate), \ | ||
73 | .hw_value = (_rateid), \ | ||
74 | .flags = (_flags), \ | ||
75 | } | ||
76 | |||
77 | static struct ieee80211_rate cw1200_rates[] = { | ||
78 | RATETAB_ENT(10, 0, 0), | ||
79 | RATETAB_ENT(20, 1, 0), | ||
80 | RATETAB_ENT(55, 2, 0), | ||
81 | RATETAB_ENT(110, 3, 0), | ||
82 | RATETAB_ENT(60, 6, 0), | ||
83 | RATETAB_ENT(90, 7, 0), | ||
84 | RATETAB_ENT(120, 8, 0), | ||
85 | RATETAB_ENT(180, 9, 0), | ||
86 | RATETAB_ENT(240, 10, 0), | ||
87 | RATETAB_ENT(360, 11, 0), | ||
88 | RATETAB_ENT(480, 12, 0), | ||
89 | RATETAB_ENT(540, 13, 0), | ||
90 | }; | ||
91 | |||
92 | static struct ieee80211_rate cw1200_mcs_rates[] = { | ||
93 | RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS), | ||
94 | RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS), | ||
95 | RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS), | ||
96 | RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS), | ||
97 | RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS), | ||
98 | RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS), | ||
99 | RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS), | ||
100 | RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS), | ||
101 | }; | ||
102 | |||
103 | #define cw1200_a_rates (cw1200_rates + 4) | ||
104 | #define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4) | ||
105 | #define cw1200_g_rates (cw1200_rates + 0) | ||
106 | #define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates)) | ||
107 | #define cw1200_n_rates (cw1200_mcs_rates) | ||
108 | #define cw1200_n_rates_size (ARRAY_SIZE(cw1200_mcs_rates)) | ||
109 | |||
110 | |||
111 | #define CHAN2G(_channel, _freq, _flags) { \ | ||
112 | .band = IEEE80211_BAND_2GHZ, \ | ||
113 | .center_freq = (_freq), \ | ||
114 | .hw_value = (_channel), \ | ||
115 | .flags = (_flags), \ | ||
116 | .max_antenna_gain = 0, \ | ||
117 | .max_power = 30, \ | ||
118 | } | ||
119 | |||
120 | #define CHAN5G(_channel, _flags) { \ | ||
121 | .band = IEEE80211_BAND_5GHZ, \ | ||
122 | .center_freq = 5000 + (5 * (_channel)), \ | ||
123 | .hw_value = (_channel), \ | ||
124 | .flags = (_flags), \ | ||
125 | .max_antenna_gain = 0, \ | ||
126 | .max_power = 30, \ | ||
127 | } | ||
128 | |||
129 | static struct ieee80211_channel cw1200_2ghz_chantable[] = { | ||
130 | CHAN2G(1, 2412, 0), | ||
131 | CHAN2G(2, 2417, 0), | ||
132 | CHAN2G(3, 2422, 0), | ||
133 | CHAN2G(4, 2427, 0), | ||
134 | CHAN2G(5, 2432, 0), | ||
135 | CHAN2G(6, 2437, 0), | ||
136 | CHAN2G(7, 2442, 0), | ||
137 | CHAN2G(8, 2447, 0), | ||
138 | CHAN2G(9, 2452, 0), | ||
139 | CHAN2G(10, 2457, 0), | ||
140 | CHAN2G(11, 2462, 0), | ||
141 | CHAN2G(12, 2467, 0), | ||
142 | CHAN2G(13, 2472, 0), | ||
143 | CHAN2G(14, 2484, 0), | ||
144 | }; | ||
145 | |||
146 | static struct ieee80211_channel cw1200_5ghz_chantable[] = { | ||
147 | CHAN5G(34, 0), CHAN5G(36, 0), | ||
148 | CHAN5G(38, 0), CHAN5G(40, 0), | ||
149 | CHAN5G(42, 0), CHAN5G(44, 0), | ||
150 | CHAN5G(46, 0), CHAN5G(48, 0), | ||
151 | CHAN5G(52, 0), CHAN5G(56, 0), | ||
152 | CHAN5G(60, 0), CHAN5G(64, 0), | ||
153 | CHAN5G(100, 0), CHAN5G(104, 0), | ||
154 | CHAN5G(108, 0), CHAN5G(112, 0), | ||
155 | CHAN5G(116, 0), CHAN5G(120, 0), | ||
156 | CHAN5G(124, 0), CHAN5G(128, 0), | ||
157 | CHAN5G(132, 0), CHAN5G(136, 0), | ||
158 | CHAN5G(140, 0), CHAN5G(149, 0), | ||
159 | CHAN5G(153, 0), CHAN5G(157, 0), | ||
160 | CHAN5G(161, 0), CHAN5G(165, 0), | ||
161 | CHAN5G(184, 0), CHAN5G(188, 0), | ||
162 | CHAN5G(192, 0), CHAN5G(196, 0), | ||
163 | CHAN5G(200, 0), CHAN5G(204, 0), | ||
164 | CHAN5G(208, 0), CHAN5G(212, 0), | ||
165 | CHAN5G(216, 0), | ||
166 | }; | ||
167 | |||
168 | static struct ieee80211_supported_band cw1200_band_2ghz = { | ||
169 | .channels = cw1200_2ghz_chantable, | ||
170 | .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable), | ||
171 | .bitrates = cw1200_g_rates, | ||
172 | .n_bitrates = cw1200_g_rates_size, | ||
173 | .ht_cap = { | ||
174 | .cap = IEEE80211_HT_CAP_GRN_FLD | | ||
175 | (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | | ||
176 | IEEE80211_HT_CAP_MAX_AMSDU, | ||
177 | .ht_supported = 1, | ||
178 | .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, | ||
179 | .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, | ||
180 | .mcs = { | ||
181 | .rx_mask[0] = 0xFF, | ||
182 | .rx_highest = __cpu_to_le16(0x41), | ||
183 | .tx_params = IEEE80211_HT_MCS_TX_DEFINED, | ||
184 | }, | ||
185 | }, | ||
186 | }; | ||
187 | |||
188 | static struct ieee80211_supported_band cw1200_band_5ghz = { | ||
189 | .channels = cw1200_5ghz_chantable, | ||
190 | .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable), | ||
191 | .bitrates = cw1200_a_rates, | ||
192 | .n_bitrates = cw1200_a_rates_size, | ||
193 | .ht_cap = { | ||
194 | .cap = IEEE80211_HT_CAP_GRN_FLD | | ||
195 | (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | | ||
196 | IEEE80211_HT_CAP_MAX_AMSDU, | ||
197 | .ht_supported = 1, | ||
198 | .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, | ||
199 | .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, | ||
200 | .mcs = { | ||
201 | .rx_mask[0] = 0xFF, | ||
202 | .rx_highest = __cpu_to_le16(0x41), | ||
203 | .tx_params = IEEE80211_HT_MCS_TX_DEFINED, | ||
204 | }, | ||
205 | }, | ||
206 | }; | ||
207 | |||
208 | static const unsigned long cw1200_ttl[] = { | ||
209 | 1 * HZ, /* VO */ | ||
210 | 2 * HZ, /* VI */ | ||
211 | 5 * HZ, /* BE */ | ||
212 | 10 * HZ /* BK */ | ||
213 | }; | ||
214 | |||
215 | static const struct ieee80211_ops cw1200_ops = { | ||
216 | .start = cw1200_start, | ||
217 | .stop = cw1200_stop, | ||
218 | .add_interface = cw1200_add_interface, | ||
219 | .remove_interface = cw1200_remove_interface, | ||
220 | .change_interface = cw1200_change_interface, | ||
221 | .tx = cw1200_tx, | ||
222 | .hw_scan = cw1200_hw_scan, | ||
223 | .set_tim = cw1200_set_tim, | ||
224 | .sta_notify = cw1200_sta_notify, | ||
225 | .sta_add = cw1200_sta_add, | ||
226 | .sta_remove = cw1200_sta_remove, | ||
227 | .set_key = cw1200_set_key, | ||
228 | .set_rts_threshold = cw1200_set_rts_threshold, | ||
229 | .config = cw1200_config, | ||
230 | .bss_info_changed = cw1200_bss_info_changed, | ||
231 | .prepare_multicast = cw1200_prepare_multicast, | ||
232 | .configure_filter = cw1200_configure_filter, | ||
233 | .conf_tx = cw1200_conf_tx, | ||
234 | .get_stats = cw1200_get_stats, | ||
235 | .ampdu_action = cw1200_ampdu_action, | ||
236 | .flush = cw1200_flush, | ||
237 | .suspend = cw1200_wow_suspend, | ||
238 | .resume = cw1200_wow_resume, | ||
239 | /* Intentionally not offloaded: */ | ||
240 | /*.channel_switch = cw1200_channel_switch, */ | ||
241 | /*.remain_on_channel = cw1200_remain_on_channel, */ | ||
242 | /*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel, */ | ||
243 | }; | ||
244 | |||
245 | int cw1200_ba_rx_tids = -1; | ||
246 | int cw1200_ba_tx_tids = -1; | ||
247 | module_param(cw1200_ba_rx_tids, int, 0644); | ||
248 | module_param(cw1200_ba_tx_tids, int, 0644); | ||
249 | MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs"); | ||
250 | MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs"); | ||
251 | |||
252 | static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr, | ||
253 | const bool have_5ghz) | ||
254 | { | ||
255 | int i, band; | ||
256 | struct ieee80211_hw *hw; | ||
257 | struct cw1200_common *priv; | ||
258 | |||
259 | hw = ieee80211_alloc_hw(sizeof(struct cw1200_common), &cw1200_ops); | ||
260 | if (!hw) | ||
261 | return NULL; | ||
262 | |||
263 | priv = hw->priv; | ||
264 | priv->hw = hw; | ||
265 | priv->hw_type = -1; | ||
266 | priv->mode = NL80211_IFTYPE_UNSPECIFIED; | ||
267 | priv->rates = cw1200_rates; /* TODO: fetch from FW */ | ||
268 | priv->mcs_rates = cw1200_n_rates; | ||
269 | if (cw1200_ba_rx_tids != -1) | ||
270 | priv->ba_rx_tid_mask = cw1200_ba_rx_tids; | ||
271 | else | ||
272 | priv->ba_rx_tid_mask = 0xFF; /* Enable RX BLKACK for all TIDs */ | ||
273 | if (cw1200_ba_tx_tids != -1) | ||
274 | priv->ba_tx_tid_mask = cw1200_ba_tx_tids; | ||
275 | else | ||
276 | priv->ba_tx_tid_mask = 0xff; /* Enable TX BLKACK for all TIDs */ | ||
277 | |||
278 | hw->flags = IEEE80211_HW_SIGNAL_DBM | | ||
279 | IEEE80211_HW_SUPPORTS_PS | | ||
280 | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | | ||
281 | IEEE80211_HW_REPORTS_TX_ACK_STATUS | | ||
282 | IEEE80211_HW_SUPPORTS_UAPSD | | ||
283 | IEEE80211_HW_CONNECTION_MONITOR | | ||
284 | IEEE80211_HW_AMPDU_AGGREGATION | | ||
285 | IEEE80211_HW_TX_AMPDU_SETUP_IN_HW | | ||
286 | IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC; | ||
287 | |||
288 | hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | | ||
289 | BIT(NL80211_IFTYPE_ADHOC) | | ||
290 | BIT(NL80211_IFTYPE_AP) | | ||
291 | BIT(NL80211_IFTYPE_MESH_POINT) | | ||
292 | BIT(NL80211_IFTYPE_P2P_CLIENT) | | ||
293 | BIT(NL80211_IFTYPE_P2P_GO); | ||
294 | |||
295 | /* Support only for limited wowlan functionalities */ | ||
296 | hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | | ||
297 | WIPHY_WOWLAN_DISCONNECT; | ||
298 | hw->wiphy->wowlan.n_patterns = 0; | ||
299 | |||
300 | hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; | ||
301 | |||
302 | hw->channel_change_time = 1000; /* TODO: find actual value */ | ||
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 | init_timer(&priv->mcast_timeout); | ||
377 | priv->mcast_timeout.data = (unsigned long)priv; | ||
378 | priv->mcast_timeout.function = cw1200_mcast_timeout; | ||
379 | |||
380 | if (cw1200_queue_stats_init(&priv->tx_queue_stats, | ||
381 | CW1200_LINK_ID_MAX, | ||
382 | cw1200_skb_dtor, | ||
383 | priv)) { | ||
384 | ieee80211_free_hw(hw); | ||
385 | return NULL; | ||
386 | } | ||
387 | |||
388 | for (i = 0; i < 4; ++i) { | ||
389 | if (cw1200_queue_init(&priv->tx_queue[i], | ||
390 | &priv->tx_queue_stats, i, 16, | ||
391 | cw1200_ttl[i])) { | ||
392 | for (; i > 0; i--) | ||
393 | cw1200_queue_deinit(&priv->tx_queue[i - 1]); | ||
394 | cw1200_queue_stats_deinit(&priv->tx_queue_stats); | ||
395 | ieee80211_free_hw(hw); | ||
396 | return NULL; | ||
397 | } | ||
398 | } | ||
399 | |||
400 | init_waitqueue_head(&priv->channel_switch_done); | ||
401 | init_waitqueue_head(&priv->wsm_cmd_wq); | ||
402 | init_waitqueue_head(&priv->wsm_startup_done); | ||
403 | init_waitqueue_head(&priv->ps_mode_switch_done); | ||
404 | wsm_buf_init(&priv->wsm_cmd_buf); | ||
405 | spin_lock_init(&priv->wsm_cmd.lock); | ||
406 | priv->wsm_cmd.done = 1; | ||
407 | tx_policy_init(priv); | ||
408 | |||
409 | return hw; | ||
410 | } | ||
411 | |||
412 | static int cw1200_register_common(struct ieee80211_hw *dev) | ||
413 | { | ||
414 | struct cw1200_common *priv = dev->priv; | ||
415 | int err; | ||
416 | |||
417 | #ifdef CONFIG_CW1200_ETF | ||
418 | if (etf_mode) | ||
419 | goto done; | ||
420 | #endif | ||
421 | |||
422 | err = cw1200_pm_init(&priv->pm_state, priv); | ||
423 | if (err) { | ||
424 | pr_err("Cannot init PM. (%d).\n", | ||
425 | err); | ||
426 | return err; | ||
427 | } | ||
428 | |||
429 | err = ieee80211_register_hw(dev); | ||
430 | if (err) { | ||
431 | pr_err("Cannot register device (%d).\n", | ||
432 | err); | ||
433 | cw1200_pm_deinit(&priv->pm_state); | ||
434 | return err; | ||
435 | } | ||
436 | |||
437 | #ifdef CONFIG_CW1200_ETF | ||
438 | done: | ||
439 | #endif | ||
440 | cw1200_debug_init(priv); | ||
441 | |||
442 | pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy)); | ||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | static void cw1200_free_common(struct ieee80211_hw *dev) | ||
447 | { | ||
448 | ieee80211_free_hw(dev); | ||
449 | } | ||
450 | |||
451 | static void cw1200_unregister_common(struct ieee80211_hw *dev) | ||
452 | { | ||
453 | struct cw1200_common *priv = dev->priv; | ||
454 | int i; | ||
455 | |||
456 | #ifdef CONFIG_CW1200_ETF | ||
457 | if (!etf_mode) { | ||
458 | #endif | ||
459 | ieee80211_unregister_hw(dev); | ||
460 | #ifdef CONFIG_CW1200_ETF | ||
461 | } | ||
462 | #endif | ||
463 | |||
464 | del_timer_sync(&priv->mcast_timeout); | ||
465 | cw1200_unregister_bh(priv); | ||
466 | |||
467 | cw1200_debug_release(priv); | ||
468 | |||
469 | mutex_destroy(&priv->conf_mutex); | ||
470 | |||
471 | wsm_buf_deinit(&priv->wsm_cmd_buf); | ||
472 | |||
473 | destroy_workqueue(priv->workqueue); | ||
474 | priv->workqueue = NULL; | ||
475 | |||
476 | if (priv->sdd) { | ||
477 | release_firmware(priv->sdd); | ||
478 | priv->sdd = NULL; | ||
479 | } | ||
480 | |||
481 | for (i = 0; i < 4; ++i) | ||
482 | cw1200_queue_deinit(&priv->tx_queue[i]); | ||
483 | |||
484 | cw1200_queue_stats_deinit(&priv->tx_queue_stats); | ||
485 | cw1200_pm_deinit(&priv->pm_state); | ||
486 | } | ||
487 | |||
488 | /* Clock is in KHz */ | ||
489 | u32 cw1200_dpll_from_clk(u16 clk_khz) | ||
490 | { | ||
491 | switch (clk_khz) { | ||
492 | case 0x32C8: /* 13000 KHz */ | ||
493 | return 0x1D89D241; | ||
494 | case 0x3E80: /* 16000 KHz */ | ||
495 | return 0x000001E1; | ||
496 | case 0x41A0: /* 16800 KHz */ | ||
497 | return 0x124931C1; | ||
498 | case 0x4B00: /* 19200 KHz */ | ||
499 | return 0x00000191; | ||
500 | case 0x5DC0: /* 24000 KHz */ | ||
501 | return 0x00000141; | ||
502 | case 0x6590: /* 26000 KHz */ | ||
503 | return 0x0EC4F121; | ||
504 | case 0x8340: /* 33600 KHz */ | ||
505 | return 0x092490E1; | ||
506 | case 0x9600: /* 38400 KHz */ | ||
507 | return 0x100010C1; | ||
508 | case 0x9C40: /* 40000 KHz */ | ||
509 | return 0x000000C1; | ||
510 | case 0xBB80: /* 48000 KHz */ | ||
511 | return 0x000000A1; | ||
512 | case 0xCB20: /* 52000 KHz */ | ||
513 | return 0x07627091; | ||
514 | default: | ||
515 | pr_err("Unknown Refclk freq (0x%04x), using 2600KHz\n", | ||
516 | clk_khz); | ||
517 | return 0x0EC4F121; | ||
518 | } | ||
519 | } | ||
520 | |||
521 | int cw1200_core_probe(const struct sbus_ops *sbus_ops, | ||
522 | struct sbus_priv *sbus, | ||
523 | struct device *pdev, | ||
524 | struct cw1200_common **core, | ||
525 | int ref_clk, const u8 *macaddr, | ||
526 | const char *sdd_path, bool have_5ghz) | ||
527 | { | ||
528 | int err = -EINVAL; | ||
529 | struct ieee80211_hw *dev; | ||
530 | struct cw1200_common *priv; | ||
531 | struct wsm_operational_mode mode = { | ||
532 | .power_mode = cw1200_power_mode, | ||
533 | .disable_more_flag_usage = true, | ||
534 | }; | ||
535 | |||
536 | dev = cw1200_init_common(macaddr, have_5ghz); | ||
537 | if (!dev) | ||
538 | goto err; | ||
539 | |||
540 | priv = dev->priv; | ||
541 | priv->hw_refclk = ref_clk; | ||
542 | if (cw1200_refclk) | ||
543 | priv->hw_refclk = cw1200_refclk; | ||
544 | |||
545 | priv->sdd_path = (char *)sdd_path; | ||
546 | if (cw1200_sdd_path) | ||
547 | priv->sdd_path = cw1200_sdd_path; | ||
548 | |||
549 | priv->sbus_ops = sbus_ops; | ||
550 | priv->sbus_priv = sbus; | ||
551 | priv->pdev = pdev; | ||
552 | SET_IEEE80211_DEV(priv->hw, pdev); | ||
553 | |||
554 | /* Pass struct cw1200_common back up */ | ||
555 | *core = priv; | ||
556 | |||
557 | err = cw1200_register_bh(priv); | ||
558 | if (err) | ||
559 | goto err1; | ||
560 | |||
561 | #ifdef CONFIG_CW1200_ETF | ||
562 | if (etf_mode) | ||
563 | goto skip_fw; | ||
564 | #endif | ||
565 | |||
566 | err = cw1200_load_firmware(priv); | ||
567 | if (err) | ||
568 | goto err2; | ||
569 | |||
570 | if (wait_event_interruptible_timeout(priv->wsm_startup_done, | ||
571 | priv->firmware_ready, | ||
572 | 3*HZ) <= 0) { | ||
573 | /* TODO: Need to find how to reset device | ||
574 | in QUEUE mode properly. | ||
575 | */ | ||
576 | pr_err("Timeout waiting on device startup\n"); | ||
577 | err = -ETIMEDOUT; | ||
578 | goto err2; | ||
579 | } | ||
580 | |||
581 | /* Set low-power mode. */ | ||
582 | wsm_set_operational_mode(priv, &mode); | ||
583 | |||
584 | /* Enable multi-TX confirmation */ | ||
585 | wsm_use_multi_tx_conf(priv, true); | ||
586 | |||
587 | #ifdef CONFIG_CW1200_ETF | ||
588 | skip_fw: | ||
589 | #endif | ||
590 | err = cw1200_register_common(dev); | ||
591 | if (err) | ||
592 | goto err2; | ||
593 | |||
594 | return err; | ||
595 | |||
596 | err2: | ||
597 | cw1200_unregister_bh(priv); | ||
598 | err1: | ||
599 | cw1200_free_common(dev); | ||
600 | err: | ||
601 | *core = NULL; | ||
602 | return err; | ||
603 | } | ||
604 | EXPORT_SYMBOL_GPL(cw1200_core_probe); | ||
605 | |||
606 | void cw1200_core_release(struct cw1200_common *self) | ||
607 | { | ||
608 | /* Disable device interrupts */ | ||
609 | self->sbus_ops->lock(self->sbus_priv); | ||
610 | __cw1200_irq_enable(self, 0); | ||
611 | self->sbus_ops->unlock(self->sbus_priv); | ||
612 | |||
613 | /* And then clean up */ | ||
614 | cw1200_unregister_common(self->hw); | ||
615 | cw1200_free_common(self->hw); | ||
616 | return; | ||
617 | } | ||
618 | EXPORT_SYMBOL_GPL(cw1200_core_release); | ||
diff --git a/drivers/net/wireless/cw1200/pm.c b/drivers/net/wireless/cw1200/pm.c new file mode 100644 index 000000000000..79edfb93b292 --- /dev/null +++ b/drivers/net/wireless/cw1200/pm.c | |||
@@ -0,0 +1,367 @@ | |||
1 | /* | ||
2 | * Mac80211 power management API for ST-Ericsson CW1200 drivers | ||
3 | * | ||
4 | * Copyright (c) 2011, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/if_ether.h> | ||
14 | #include "cw1200.h" | ||
15 | #include "pm.h" | ||
16 | #include "sta.h" | ||
17 | #include "bh.h" | ||
18 | #include "sbus.h" | ||
19 | |||
20 | #define CW1200_BEACON_SKIPPING_MULTIPLIER 3 | ||
21 | |||
22 | struct cw1200_udp_port_filter { | ||
23 | struct wsm_udp_port_filter_hdr hdr; | ||
24 | /* Up to 4 filters are allowed. */ | ||
25 | struct wsm_udp_port_filter filters[WSM_MAX_FILTER_ELEMENTS]; | ||
26 | } __packed; | ||
27 | |||
28 | struct cw1200_ether_type_filter { | ||
29 | struct wsm_ether_type_filter_hdr hdr; | ||
30 | /* Up to 4 filters are allowed. */ | ||
31 | struct wsm_ether_type_filter filters[WSM_MAX_FILTER_ELEMENTS]; | ||
32 | } __packed; | ||
33 | |||
34 | static struct cw1200_udp_port_filter cw1200_udp_port_filter_on = { | ||
35 | .hdr.num = 2, | ||
36 | .filters = { | ||
37 | [0] = { | ||
38 | .action = WSM_FILTER_ACTION_FILTER_OUT, | ||
39 | .type = WSM_FILTER_PORT_TYPE_DST, | ||
40 | .port = __cpu_to_le16(67), /* DHCP Bootps */ | ||
41 | }, | ||
42 | [1] = { | ||
43 | .action = WSM_FILTER_ACTION_FILTER_OUT, | ||
44 | .type = WSM_FILTER_PORT_TYPE_DST, | ||
45 | .port = __cpu_to_le16(68), /* DHCP Bootpc */ | ||
46 | }, | ||
47 | } | ||
48 | }; | ||
49 | |||
50 | static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off = { | ||
51 | .num = 0, | ||
52 | }; | ||
53 | |||
54 | #ifndef ETH_P_WAPI | ||
55 | #define ETH_P_WAPI 0x88B4 | ||
56 | #endif | ||
57 | |||
58 | static struct cw1200_ether_type_filter cw1200_ether_type_filter_on = { | ||
59 | .hdr.num = 4, | ||
60 | .filters = { | ||
61 | [0] = { | ||
62 | .action = WSM_FILTER_ACTION_FILTER_IN, | ||
63 | .type = __cpu_to_le16(ETH_P_IP), | ||
64 | }, | ||
65 | [1] = { | ||
66 | .action = WSM_FILTER_ACTION_FILTER_IN, | ||
67 | .type = __cpu_to_le16(ETH_P_PAE), | ||
68 | }, | ||
69 | [2] = { | ||
70 | .action = WSM_FILTER_ACTION_FILTER_IN, | ||
71 | .type = __cpu_to_le16(ETH_P_WAPI), | ||
72 | }, | ||
73 | [3] = { | ||
74 | .action = WSM_FILTER_ACTION_FILTER_IN, | ||
75 | .type = __cpu_to_le16(ETH_P_ARP), | ||
76 | }, | ||
77 | }, | ||
78 | }; | ||
79 | |||
80 | static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off = { | ||
81 | .num = 0, | ||
82 | }; | ||
83 | |||
84 | /* private */ | ||
85 | struct cw1200_suspend_state { | ||
86 | unsigned long bss_loss_tmo; | ||
87 | unsigned long join_tmo; | ||
88 | unsigned long direct_probe; | ||
89 | unsigned long link_id_gc; | ||
90 | bool beacon_skipping; | ||
91 | u8 prev_ps_mode; | ||
92 | }; | ||
93 | |||
94 | static void cw1200_pm_stay_awake_tmo(unsigned long arg) | ||
95 | { | ||
96 | /* XXX what's the point of this ? */ | ||
97 | } | ||
98 | |||
99 | int cw1200_pm_init(struct cw1200_pm_state *pm, | ||
100 | struct cw1200_common *priv) | ||
101 | { | ||
102 | spin_lock_init(&pm->lock); | ||
103 | |||
104 | init_timer(&pm->stay_awake); | ||
105 | pm->stay_awake.data = (unsigned long)pm; | ||
106 | pm->stay_awake.function = cw1200_pm_stay_awake_tmo; | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | void cw1200_pm_deinit(struct cw1200_pm_state *pm) | ||
112 | { | ||
113 | del_timer_sync(&pm->stay_awake); | ||
114 | } | ||
115 | |||
116 | void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, | ||
117 | unsigned long tmo) | ||
118 | { | ||
119 | long cur_tmo; | ||
120 | spin_lock_bh(&pm->lock); | ||
121 | cur_tmo = pm->stay_awake.expires - jiffies; | ||
122 | if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo) | ||
123 | mod_timer(&pm->stay_awake, jiffies + tmo); | ||
124 | spin_unlock_bh(&pm->lock); | ||
125 | } | ||
126 | |||
127 | static long cw1200_suspend_work(struct delayed_work *work) | ||
128 | { | ||
129 | int ret = cancel_delayed_work(work); | ||
130 | long tmo; | ||
131 | if (ret > 0) { | ||
132 | /* Timer is pending */ | ||
133 | tmo = work->timer.expires - jiffies; | ||
134 | if (tmo < 0) | ||
135 | tmo = 0; | ||
136 | } else { | ||
137 | tmo = -1; | ||
138 | } | ||
139 | return tmo; | ||
140 | } | ||
141 | |||
142 | static int cw1200_resume_work(struct cw1200_common *priv, | ||
143 | struct delayed_work *work, | ||
144 | unsigned long tmo) | ||
145 | { | ||
146 | if ((long)tmo < 0) | ||
147 | return 1; | ||
148 | |||
149 | return queue_delayed_work(priv->workqueue, work, tmo); | ||
150 | } | ||
151 | |||
152 | int cw1200_can_suspend(struct cw1200_common *priv) | ||
153 | { | ||
154 | if (atomic_read(&priv->bh_rx)) { | ||
155 | wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n"); | ||
156 | return 0; | ||
157 | } | ||
158 | return 1; | ||
159 | } | ||
160 | EXPORT_SYMBOL_GPL(cw1200_can_suspend); | ||
161 | |||
162 | int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) | ||
163 | { | ||
164 | struct cw1200_common *priv = hw->priv; | ||
165 | struct cw1200_pm_state *pm_state = &priv->pm_state; | ||
166 | struct cw1200_suspend_state *state; | ||
167 | int ret; | ||
168 | |||
169 | spin_lock_bh(&pm_state->lock); | ||
170 | ret = timer_pending(&pm_state->stay_awake); | ||
171 | spin_unlock_bh(&pm_state->lock); | ||
172 | if (ret) | ||
173 | return -EAGAIN; | ||
174 | |||
175 | /* Do not suspend when datapath is not idle */ | ||
176 | if (priv->tx_queue_stats.num_queued) | ||
177 | return -EBUSY; | ||
178 | |||
179 | /* Make sure there is no configuration requests in progress. */ | ||
180 | if (!mutex_trylock(&priv->conf_mutex)) | ||
181 | return -EBUSY; | ||
182 | |||
183 | /* Ensure pending operations are done. | ||
184 | * Note also that wow_suspend must return in ~2.5sec, before | ||
185 | * watchdog is triggered. | ||
186 | */ | ||
187 | if (priv->channel_switch_in_progress) | ||
188 | goto revert1; | ||
189 | |||
190 | /* Do not suspend when join is pending */ | ||
191 | if (priv->join_pending) | ||
192 | goto revert1; | ||
193 | |||
194 | /* Do not suspend when scanning */ | ||
195 | if (down_trylock(&priv->scan.lock)) | ||
196 | goto revert1; | ||
197 | |||
198 | /* Lock TX. */ | ||
199 | wsm_lock_tx_async(priv); | ||
200 | |||
201 | /* Wait to avoid possible race with bh code. | ||
202 | * But do not wait too long... | ||
203 | */ | ||
204 | if (wait_event_timeout(priv->bh_evt_wq, | ||
205 | !priv->hw_bufs_used, HZ / 10) <= 0) | ||
206 | goto revert2; | ||
207 | |||
208 | /* Set UDP filter */ | ||
209 | wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_on.hdr); | ||
210 | |||
211 | /* Set ethernet frame type filter */ | ||
212 | wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_on.hdr); | ||
213 | |||
214 | /* Allocate state */ | ||
215 | state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL); | ||
216 | if (!state) | ||
217 | goto revert3; | ||
218 | |||
219 | /* Change to legacy PS while going to suspend */ | ||
220 | if (!priv->vif->p2p && | ||
221 | priv->join_status == CW1200_JOIN_STATUS_STA && | ||
222 | priv->powersave_mode.mode != WSM_PSM_PS) { | ||
223 | state->prev_ps_mode = priv->powersave_mode.mode; | ||
224 | priv->powersave_mode.mode = WSM_PSM_PS; | ||
225 | cw1200_set_pm(priv, &priv->powersave_mode); | ||
226 | if (wait_event_interruptible_timeout(priv->ps_mode_switch_done, | ||
227 | !priv->ps_mode_switch_in_progress, 1*HZ) <= 0) { | ||
228 | goto revert3; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | /* Store delayed work states. */ | ||
233 | state->bss_loss_tmo = | ||
234 | cw1200_suspend_work(&priv->bss_loss_work); | ||
235 | state->join_tmo = | ||
236 | cw1200_suspend_work(&priv->join_timeout); | ||
237 | state->direct_probe = | ||
238 | cw1200_suspend_work(&priv->scan.probe_work); | ||
239 | state->link_id_gc = | ||
240 | cw1200_suspend_work(&priv->link_id_gc_work); | ||
241 | |||
242 | cancel_delayed_work_sync(&priv->clear_recent_scan_work); | ||
243 | atomic_set(&priv->recent_scan, 0); | ||
244 | |||
245 | /* Enable beacon skipping */ | ||
246 | if (priv->join_status == CW1200_JOIN_STATUS_STA && | ||
247 | priv->join_dtim_period && | ||
248 | !priv->has_multicast_subscription) { | ||
249 | state->beacon_skipping = true; | ||
250 | wsm_set_beacon_wakeup_period(priv, | ||
251 | priv->join_dtim_period, | ||
252 | CW1200_BEACON_SKIPPING_MULTIPLIER * priv->join_dtim_period); | ||
253 | } | ||
254 | |||
255 | /* Stop serving thread */ | ||
256 | if (cw1200_bh_suspend(priv)) | ||
257 | goto revert4; | ||
258 | |||
259 | ret = timer_pending(&priv->mcast_timeout); | ||
260 | if (ret) | ||
261 | goto revert5; | ||
262 | |||
263 | /* Store suspend state */ | ||
264 | pm_state->suspend_state = state; | ||
265 | |||
266 | /* Enable IRQ wake */ | ||
267 | ret = priv->sbus_ops->power_mgmt(priv->sbus_priv, true); | ||
268 | if (ret) { | ||
269 | wiphy_err(priv->hw->wiphy, | ||
270 | "PM request failed: %d. WoW is disabled.\n", ret); | ||
271 | cw1200_wow_resume(hw); | ||
272 | return -EBUSY; | ||
273 | } | ||
274 | |||
275 | /* Force resume if event is coming from the device. */ | ||
276 | if (atomic_read(&priv->bh_rx)) { | ||
277 | cw1200_wow_resume(hw); | ||
278 | return -EAGAIN; | ||
279 | } | ||
280 | |||
281 | return 0; | ||
282 | |||
283 | revert5: | ||
284 | WARN_ON(cw1200_bh_resume(priv)); | ||
285 | revert4: | ||
286 | cw1200_resume_work(priv, &priv->bss_loss_work, | ||
287 | state->bss_loss_tmo); | ||
288 | cw1200_resume_work(priv, &priv->join_timeout, | ||
289 | state->join_tmo); | ||
290 | cw1200_resume_work(priv, &priv->scan.probe_work, | ||
291 | state->direct_probe); | ||
292 | cw1200_resume_work(priv, &priv->link_id_gc_work, | ||
293 | state->link_id_gc); | ||
294 | kfree(state); | ||
295 | revert3: | ||
296 | wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off); | ||
297 | wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off); | ||
298 | revert2: | ||
299 | wsm_unlock_tx(priv); | ||
300 | up(&priv->scan.lock); | ||
301 | revert1: | ||
302 | mutex_unlock(&priv->conf_mutex); | ||
303 | return -EBUSY; | ||
304 | } | ||
305 | |||
306 | int cw1200_wow_resume(struct ieee80211_hw *hw) | ||
307 | { | ||
308 | struct cw1200_common *priv = hw->priv; | ||
309 | struct cw1200_pm_state *pm_state = &priv->pm_state; | ||
310 | struct cw1200_suspend_state *state; | ||
311 | |||
312 | state = pm_state->suspend_state; | ||
313 | pm_state->suspend_state = NULL; | ||
314 | |||
315 | /* Disable IRQ wake */ | ||
316 | priv->sbus_ops->power_mgmt(priv->sbus_priv, false); | ||
317 | |||
318 | /* Scan.lock must be released before BH is resumed other way | ||
319 | * in case when BSS_LOST command arrived the processing of the | ||
320 | * command will be delayed. | ||
321 | */ | ||
322 | up(&priv->scan.lock); | ||
323 | |||
324 | /* Resume BH thread */ | ||
325 | WARN_ON(cw1200_bh_resume(priv)); | ||
326 | |||
327 | /* Restores previous PS mode */ | ||
328 | if (!priv->vif->p2p && priv->join_status == CW1200_JOIN_STATUS_STA) { | ||
329 | priv->powersave_mode.mode = state->prev_ps_mode; | ||
330 | cw1200_set_pm(priv, &priv->powersave_mode); | ||
331 | } | ||
332 | |||
333 | if (state->beacon_skipping) { | ||
334 | wsm_set_beacon_wakeup_period(priv, priv->beacon_int * | ||
335 | priv->join_dtim_period > | ||
336 | MAX_BEACON_SKIP_TIME_MS ? 1 : | ||
337 | priv->join_dtim_period, 0); | ||
338 | state->beacon_skipping = false; | ||
339 | } | ||
340 | |||
341 | /* Resume delayed work */ | ||
342 | cw1200_resume_work(priv, &priv->bss_loss_work, | ||
343 | state->bss_loss_tmo); | ||
344 | cw1200_resume_work(priv, &priv->join_timeout, | ||
345 | state->join_tmo); | ||
346 | cw1200_resume_work(priv, &priv->scan.probe_work, | ||
347 | state->direct_probe); | ||
348 | cw1200_resume_work(priv, &priv->link_id_gc_work, | ||
349 | state->link_id_gc); | ||
350 | |||
351 | /* Remove UDP port filter */ | ||
352 | wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off); | ||
353 | |||
354 | /* Remove ethernet frame type filter */ | ||
355 | wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off); | ||
356 | |||
357 | /* Unlock datapath */ | ||
358 | wsm_unlock_tx(priv); | ||
359 | |||
360 | /* Unlock configuration mutex */ | ||
361 | mutex_unlock(&priv->conf_mutex); | ||
362 | |||
363 | /* Free memory */ | ||
364 | kfree(state); | ||
365 | |||
366 | return 0; | ||
367 | } | ||
diff --git a/drivers/net/wireless/cw1200/pm.h b/drivers/net/wireless/cw1200/pm.h new file mode 100644 index 000000000000..516d9671dd1e --- /dev/null +++ b/drivers/net/wireless/cw1200/pm.h | |||
@@ -0,0 +1,38 @@ | |||
1 | /* | ||
2 | * Mac80211 power management interface for ST-Ericsson CW1200 mac80211 drivers | ||
3 | * | ||
4 | * Copyright (c) 2011, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef PM_H_INCLUDED | ||
13 | #define PM_H_INCLUDED | ||
14 | |||
15 | /* ******************************************************************** */ | ||
16 | /* mac80211 API */ | ||
17 | |||
18 | /* extern */ struct cw1200_common; | ||
19 | /* private */ struct cw1200_suspend_state; | ||
20 | |||
21 | struct cw1200_pm_state { | ||
22 | struct cw1200_suspend_state *suspend_state; | ||
23 | struct timer_list stay_awake; | ||
24 | struct platform_device *pm_dev; | ||
25 | spinlock_t lock; /* Protect access */ | ||
26 | }; | ||
27 | |||
28 | int cw1200_pm_init(struct cw1200_pm_state *pm, | ||
29 | struct cw1200_common *priv); | ||
30 | void cw1200_pm_deinit(struct cw1200_pm_state *pm); | ||
31 | void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, | ||
32 | unsigned long tmo); | ||
33 | int cw1200_wow_suspend(struct ieee80211_hw *hw, | ||
34 | struct cfg80211_wowlan *wowlan); | ||
35 | int cw1200_wow_resume(struct ieee80211_hw *hw); | ||
36 | int cw1200_can_suspend(struct cw1200_common *priv); | ||
37 | |||
38 | #endif | ||
diff --git a/drivers/net/wireless/cw1200/queue.c b/drivers/net/wireless/cw1200/queue.c new file mode 100644 index 000000000000..8510454d5db1 --- /dev/null +++ b/drivers/net/wireless/cw1200/queue.c | |||
@@ -0,0 +1,583 @@ | |||
1 | /* | ||
2 | * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers | ||
3 | * | ||
4 | * Copyright (c) 2010, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <net/mac80211.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include "queue.h" | ||
15 | #include "cw1200.h" | ||
16 | #include "debug.h" | ||
17 | |||
18 | /* private */ struct cw1200_queue_item | ||
19 | { | ||
20 | struct list_head head; | ||
21 | struct sk_buff *skb; | ||
22 | u32 packet_id; | ||
23 | unsigned long queue_timestamp; | ||
24 | unsigned long xmit_timestamp; | ||
25 | struct cw1200_txpriv txpriv; | ||
26 | u8 generation; | ||
27 | }; | ||
28 | |||
29 | static inline void __cw1200_queue_lock(struct cw1200_queue *queue) | ||
30 | { | ||
31 | struct cw1200_queue_stats *stats = queue->stats; | ||
32 | if (queue->tx_locked_cnt++ == 0) { | ||
33 | pr_debug("[TX] Queue %d is locked.\n", | ||
34 | queue->queue_id); | ||
35 | ieee80211_stop_queue(stats->priv->hw, queue->queue_id); | ||
36 | } | ||
37 | } | ||
38 | |||
39 | static inline void __cw1200_queue_unlock(struct cw1200_queue *queue) | ||
40 | { | ||
41 | struct cw1200_queue_stats *stats = queue->stats; | ||
42 | BUG_ON(!queue->tx_locked_cnt); | ||
43 | if (--queue->tx_locked_cnt == 0) { | ||
44 | pr_debug("[TX] Queue %d is unlocked.\n", | ||
45 | queue->queue_id); | ||
46 | ieee80211_wake_queue(stats->priv->hw, queue->queue_id); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | static inline void cw1200_queue_parse_id(u32 packet_id, u8 *queue_generation, | ||
51 | u8 *queue_id, u8 *item_generation, | ||
52 | u8 *item_id) | ||
53 | { | ||
54 | *item_id = (packet_id >> 0) & 0xFF; | ||
55 | *item_generation = (packet_id >> 8) & 0xFF; | ||
56 | *queue_id = (packet_id >> 16) & 0xFF; | ||
57 | *queue_generation = (packet_id >> 24) & 0xFF; | ||
58 | } | ||
59 | |||
60 | static inline u32 cw1200_queue_mk_packet_id(u8 queue_generation, u8 queue_id, | ||
61 | u8 item_generation, u8 item_id) | ||
62 | { | ||
63 | return ((u32)item_id << 0) | | ||
64 | ((u32)item_generation << 8) | | ||
65 | ((u32)queue_id << 16) | | ||
66 | ((u32)queue_generation << 24); | ||
67 | } | ||
68 | |||
69 | static void cw1200_queue_post_gc(struct cw1200_queue_stats *stats, | ||
70 | struct list_head *gc_list) | ||
71 | { | ||
72 | struct cw1200_queue_item *item, *tmp; | ||
73 | |||
74 | list_for_each_entry_safe(item, tmp, gc_list, head) { | ||
75 | list_del(&item->head); | ||
76 | stats->skb_dtor(stats->priv, item->skb, &item->txpriv); | ||
77 | kfree(item); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | static void cw1200_queue_register_post_gc(struct list_head *gc_list, | ||
82 | struct cw1200_queue_item *item) | ||
83 | { | ||
84 | struct cw1200_queue_item *gc_item; | ||
85 | gc_item = kmalloc(sizeof(struct cw1200_queue_item), | ||
86 | GFP_ATOMIC); | ||
87 | BUG_ON(!gc_item); | ||
88 | memcpy(gc_item, item, sizeof(struct cw1200_queue_item)); | ||
89 | list_add_tail(&gc_item->head, gc_list); | ||
90 | } | ||
91 | |||
92 | static void __cw1200_queue_gc(struct cw1200_queue *queue, | ||
93 | struct list_head *head, | ||
94 | bool unlock) | ||
95 | { | ||
96 | struct cw1200_queue_stats *stats = queue->stats; | ||
97 | struct cw1200_queue_item *item = NULL, *tmp; | ||
98 | bool wakeup_stats = false; | ||
99 | |||
100 | list_for_each_entry_safe(item, tmp, &queue->queue, head) { | ||
101 | if (jiffies - item->queue_timestamp < queue->ttl) | ||
102 | break; | ||
103 | --queue->num_queued; | ||
104 | --queue->link_map_cache[item->txpriv.link_id]; | ||
105 | spin_lock_bh(&stats->lock); | ||
106 | --stats->num_queued; | ||
107 | if (!--stats->link_map_cache[item->txpriv.link_id]) | ||
108 | wakeup_stats = true; | ||
109 | spin_unlock_bh(&stats->lock); | ||
110 | cw1200_debug_tx_ttl(stats->priv); | ||
111 | cw1200_queue_register_post_gc(head, item); | ||
112 | item->skb = NULL; | ||
113 | list_move_tail(&item->head, &queue->free_pool); | ||
114 | } | ||
115 | |||
116 | if (wakeup_stats) | ||
117 | wake_up(&stats->wait_link_id_empty); | ||
118 | |||
119 | if (queue->overfull) { | ||
120 | if (queue->num_queued <= (queue->capacity >> 1)) { | ||
121 | queue->overfull = false; | ||
122 | if (unlock) | ||
123 | __cw1200_queue_unlock(queue); | ||
124 | } else if (item) { | ||
125 | unsigned long tmo = item->queue_timestamp + queue->ttl; | ||
126 | mod_timer(&queue->gc, tmo); | ||
127 | cw1200_pm_stay_awake(&stats->priv->pm_state, | ||
128 | tmo - jiffies); | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | |||
133 | static void cw1200_queue_gc(unsigned long arg) | ||
134 | { | ||
135 | LIST_HEAD(list); | ||
136 | struct cw1200_queue *queue = | ||
137 | (struct cw1200_queue *)arg; | ||
138 | |||
139 | spin_lock_bh(&queue->lock); | ||
140 | __cw1200_queue_gc(queue, &list, true); | ||
141 | spin_unlock_bh(&queue->lock); | ||
142 | cw1200_queue_post_gc(queue->stats, &list); | ||
143 | } | ||
144 | |||
145 | int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, | ||
146 | size_t map_capacity, | ||
147 | cw1200_queue_skb_dtor_t skb_dtor, | ||
148 | struct cw1200_common *priv) | ||
149 | { | ||
150 | memset(stats, 0, sizeof(*stats)); | ||
151 | stats->map_capacity = map_capacity; | ||
152 | stats->skb_dtor = skb_dtor; | ||
153 | stats->priv = priv; | ||
154 | spin_lock_init(&stats->lock); | ||
155 | init_waitqueue_head(&stats->wait_link_id_empty); | ||
156 | |||
157 | stats->link_map_cache = kzalloc(sizeof(int) * map_capacity, | ||
158 | GFP_KERNEL); | ||
159 | if (!stats->link_map_cache) | ||
160 | return -ENOMEM; | ||
161 | |||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | int cw1200_queue_init(struct cw1200_queue *queue, | ||
166 | struct cw1200_queue_stats *stats, | ||
167 | u8 queue_id, | ||
168 | size_t capacity, | ||
169 | unsigned long ttl) | ||
170 | { | ||
171 | size_t i; | ||
172 | |||
173 | memset(queue, 0, sizeof(*queue)); | ||
174 | queue->stats = stats; | ||
175 | queue->capacity = capacity; | ||
176 | queue->queue_id = queue_id; | ||
177 | queue->ttl = ttl; | ||
178 | INIT_LIST_HEAD(&queue->queue); | ||
179 | INIT_LIST_HEAD(&queue->pending); | ||
180 | INIT_LIST_HEAD(&queue->free_pool); | ||
181 | spin_lock_init(&queue->lock); | ||
182 | init_timer(&queue->gc); | ||
183 | queue->gc.data = (unsigned long)queue; | ||
184 | queue->gc.function = cw1200_queue_gc; | ||
185 | |||
186 | queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity, | ||
187 | GFP_KERNEL); | ||
188 | if (!queue->pool) | ||
189 | return -ENOMEM; | ||
190 | |||
191 | queue->link_map_cache = kzalloc(sizeof(int) * stats->map_capacity, | ||
192 | GFP_KERNEL); | ||
193 | if (!queue->link_map_cache) { | ||
194 | kfree(queue->pool); | ||
195 | queue->pool = NULL; | ||
196 | return -ENOMEM; | ||
197 | } | ||
198 | |||
199 | for (i = 0; i < capacity; ++i) | ||
200 | list_add_tail(&queue->pool[i].head, &queue->free_pool); | ||
201 | |||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | int cw1200_queue_clear(struct cw1200_queue *queue) | ||
206 | { | ||
207 | int i; | ||
208 | LIST_HEAD(gc_list); | ||
209 | struct cw1200_queue_stats *stats = queue->stats; | ||
210 | struct cw1200_queue_item *item, *tmp; | ||
211 | |||
212 | spin_lock_bh(&queue->lock); | ||
213 | queue->generation++; | ||
214 | list_splice_tail_init(&queue->queue, &queue->pending); | ||
215 | list_for_each_entry_safe(item, tmp, &queue->pending, head) { | ||
216 | WARN_ON(!item->skb); | ||
217 | cw1200_queue_register_post_gc(&gc_list, item); | ||
218 | item->skb = NULL; | ||
219 | list_move_tail(&item->head, &queue->free_pool); | ||
220 | } | ||
221 | queue->num_queued = 0; | ||
222 | queue->num_pending = 0; | ||
223 | |||
224 | spin_lock_bh(&stats->lock); | ||
225 | for (i = 0; i < stats->map_capacity; ++i) { | ||
226 | stats->num_queued -= queue->link_map_cache[i]; | ||
227 | stats->link_map_cache[i] -= queue->link_map_cache[i]; | ||
228 | queue->link_map_cache[i] = 0; | ||
229 | } | ||
230 | spin_unlock_bh(&stats->lock); | ||
231 | if (queue->overfull) { | ||
232 | queue->overfull = false; | ||
233 | __cw1200_queue_unlock(queue); | ||
234 | } | ||
235 | spin_unlock_bh(&queue->lock); | ||
236 | wake_up(&stats->wait_link_id_empty); | ||
237 | cw1200_queue_post_gc(stats, &gc_list); | ||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats) | ||
242 | { | ||
243 | kfree(stats->link_map_cache); | ||
244 | stats->link_map_cache = NULL; | ||
245 | } | ||
246 | |||
247 | void cw1200_queue_deinit(struct cw1200_queue *queue) | ||
248 | { | ||
249 | cw1200_queue_clear(queue); | ||
250 | del_timer_sync(&queue->gc); | ||
251 | INIT_LIST_HEAD(&queue->free_pool); | ||
252 | kfree(queue->pool); | ||
253 | kfree(queue->link_map_cache); | ||
254 | queue->pool = NULL; | ||
255 | queue->link_map_cache = NULL; | ||
256 | queue->capacity = 0; | ||
257 | } | ||
258 | |||
259 | size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, | ||
260 | u32 link_id_map) | ||
261 | { | ||
262 | size_t ret; | ||
263 | int i, bit; | ||
264 | size_t map_capacity = queue->stats->map_capacity; | ||
265 | |||
266 | if (!link_id_map) | ||
267 | return 0; | ||
268 | |||
269 | spin_lock_bh(&queue->lock); | ||
270 | if (link_id_map == (u32)-1) { | ||
271 | ret = queue->num_queued - queue->num_pending; | ||
272 | } else { | ||
273 | ret = 0; | ||
274 | for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) { | ||
275 | if (link_id_map & bit) | ||
276 | ret += queue->link_map_cache[i]; | ||
277 | } | ||
278 | } | ||
279 | spin_unlock_bh(&queue->lock); | ||
280 | return ret; | ||
281 | } | ||
282 | |||
283 | int cw1200_queue_put(struct cw1200_queue *queue, | ||
284 | struct sk_buff *skb, | ||
285 | struct cw1200_txpriv *txpriv) | ||
286 | { | ||
287 | int ret = 0; | ||
288 | LIST_HEAD(gc_list); | ||
289 | struct cw1200_queue_stats *stats = queue->stats; | ||
290 | |||
291 | if (txpriv->link_id >= queue->stats->map_capacity) | ||
292 | return -EINVAL; | ||
293 | |||
294 | spin_lock_bh(&queue->lock); | ||
295 | if (!WARN_ON(list_empty(&queue->free_pool))) { | ||
296 | struct cw1200_queue_item *item = list_first_entry( | ||
297 | &queue->free_pool, struct cw1200_queue_item, head); | ||
298 | BUG_ON(item->skb); | ||
299 | |||
300 | list_move_tail(&item->head, &queue->queue); | ||
301 | item->skb = skb; | ||
302 | item->txpriv = *txpriv; | ||
303 | item->generation = 0; | ||
304 | item->packet_id = cw1200_queue_mk_packet_id(queue->generation, | ||
305 | queue->queue_id, | ||
306 | item->generation, | ||
307 | item - queue->pool); | ||
308 | item->queue_timestamp = jiffies; | ||
309 | |||
310 | ++queue->num_queued; | ||
311 | ++queue->link_map_cache[txpriv->link_id]; | ||
312 | |||
313 | spin_lock_bh(&stats->lock); | ||
314 | ++stats->num_queued; | ||
315 | ++stats->link_map_cache[txpriv->link_id]; | ||
316 | spin_unlock_bh(&stats->lock); | ||
317 | |||
318 | /* TX may happen in parallel sometimes. | ||
319 | * Leave extra queue slots so we don't overflow. | ||
320 | */ | ||
321 | if (queue->overfull == false && | ||
322 | queue->num_queued >= | ||
323 | (queue->capacity - (num_present_cpus() - 1))) { | ||
324 | queue->overfull = true; | ||
325 | __cw1200_queue_lock(queue); | ||
326 | mod_timer(&queue->gc, jiffies); | ||
327 | } | ||
328 | } else { | ||
329 | ret = -ENOENT; | ||
330 | } | ||
331 | spin_unlock_bh(&queue->lock); | ||
332 | return ret; | ||
333 | } | ||
334 | |||
335 | int cw1200_queue_get(struct cw1200_queue *queue, | ||
336 | u32 link_id_map, | ||
337 | struct wsm_tx **tx, | ||
338 | struct ieee80211_tx_info **tx_info, | ||
339 | const struct cw1200_txpriv **txpriv) | ||
340 | { | ||
341 | int ret = -ENOENT; | ||
342 | struct cw1200_queue_item *item; | ||
343 | struct cw1200_queue_stats *stats = queue->stats; | ||
344 | bool wakeup_stats = false; | ||
345 | |||
346 | spin_lock_bh(&queue->lock); | ||
347 | list_for_each_entry(item, &queue->queue, head) { | ||
348 | if (link_id_map & BIT(item->txpriv.link_id)) { | ||
349 | ret = 0; | ||
350 | break; | ||
351 | } | ||
352 | } | ||
353 | |||
354 | if (!WARN_ON(ret)) { | ||
355 | *tx = (struct wsm_tx *)item->skb->data; | ||
356 | *tx_info = IEEE80211_SKB_CB(item->skb); | ||
357 | *txpriv = &item->txpriv; | ||
358 | (*tx)->packet_id = __cpu_to_le32(item->packet_id); | ||
359 | list_move_tail(&item->head, &queue->pending); | ||
360 | ++queue->num_pending; | ||
361 | --queue->link_map_cache[item->txpriv.link_id]; | ||
362 | item->xmit_timestamp = jiffies; | ||
363 | |||
364 | spin_lock_bh(&stats->lock); | ||
365 | --stats->num_queued; | ||
366 | if (!--stats->link_map_cache[item->txpriv.link_id]) | ||
367 | wakeup_stats = true; | ||
368 | spin_unlock_bh(&stats->lock); | ||
369 | } | ||
370 | spin_unlock_bh(&queue->lock); | ||
371 | if (wakeup_stats) | ||
372 | wake_up(&stats->wait_link_id_empty); | ||
373 | return ret; | ||
374 | } | ||
375 | |||
376 | int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id) | ||
377 | { | ||
378 | int ret = 0; | ||
379 | u8 queue_generation, queue_id, item_generation, item_id; | ||
380 | struct cw1200_queue_item *item; | ||
381 | struct cw1200_queue_stats *stats = queue->stats; | ||
382 | |||
383 | cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id, | ||
384 | &item_generation, &item_id); | ||
385 | |||
386 | item = &queue->pool[item_id]; | ||
387 | |||
388 | spin_lock_bh(&queue->lock); | ||
389 | BUG_ON(queue_id != queue->queue_id); | ||
390 | if (queue_generation != queue->generation) { | ||
391 | ret = -ENOENT; | ||
392 | } else if (item_id >= (unsigned) queue->capacity) { | ||
393 | WARN_ON(1); | ||
394 | ret = -EINVAL; | ||
395 | } else if (item->generation != item_generation) { | ||
396 | WARN_ON(1); | ||
397 | ret = -ENOENT; | ||
398 | } else { | ||
399 | --queue->num_pending; | ||
400 | ++queue->link_map_cache[item->txpriv.link_id]; | ||
401 | |||
402 | spin_lock_bh(&stats->lock); | ||
403 | ++stats->num_queued; | ||
404 | ++stats->link_map_cache[item->txpriv.link_id]; | ||
405 | spin_unlock_bh(&stats->lock); | ||
406 | |||
407 | item->generation = ++item_generation; | ||
408 | item->packet_id = cw1200_queue_mk_packet_id(queue_generation, | ||
409 | queue_id, | ||
410 | item_generation, | ||
411 | item_id); | ||
412 | list_move(&item->head, &queue->queue); | ||
413 | } | ||
414 | spin_unlock_bh(&queue->lock); | ||
415 | return ret; | ||
416 | } | ||
417 | |||
418 | int cw1200_queue_requeue_all(struct cw1200_queue *queue) | ||
419 | { | ||
420 | struct cw1200_queue_item *item, *tmp; | ||
421 | struct cw1200_queue_stats *stats = queue->stats; | ||
422 | spin_lock_bh(&queue->lock); | ||
423 | |||
424 | list_for_each_entry_safe_reverse(item, tmp, &queue->pending, head) { | ||
425 | --queue->num_pending; | ||
426 | ++queue->link_map_cache[item->txpriv.link_id]; | ||
427 | |||
428 | spin_lock_bh(&stats->lock); | ||
429 | ++stats->num_queued; | ||
430 | ++stats->link_map_cache[item->txpriv.link_id]; | ||
431 | spin_unlock_bh(&stats->lock); | ||
432 | |||
433 | ++item->generation; | ||
434 | item->packet_id = cw1200_queue_mk_packet_id(queue->generation, | ||
435 | queue->queue_id, | ||
436 | item->generation, | ||
437 | item - queue->pool); | ||
438 | list_move(&item->head, &queue->queue); | ||
439 | } | ||
440 | spin_unlock_bh(&queue->lock); | ||
441 | |||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | int cw1200_queue_remove(struct cw1200_queue *queue, u32 packet_id) | ||
446 | { | ||
447 | int ret = 0; | ||
448 | u8 queue_generation, queue_id, item_generation, item_id; | ||
449 | struct cw1200_queue_item *item; | ||
450 | struct cw1200_queue_stats *stats = queue->stats; | ||
451 | struct sk_buff *gc_skb = NULL; | ||
452 | struct cw1200_txpriv gc_txpriv; | ||
453 | |||
454 | cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id, | ||
455 | &item_generation, &item_id); | ||
456 | |||
457 | item = &queue->pool[item_id]; | ||
458 | |||
459 | spin_lock_bh(&queue->lock); | ||
460 | BUG_ON(queue_id != queue->queue_id); | ||
461 | if (queue_generation != queue->generation) { | ||
462 | ret = -ENOENT; | ||
463 | } else if (item_id >= (unsigned) queue->capacity) { | ||
464 | WARN_ON(1); | ||
465 | ret = -EINVAL; | ||
466 | } else if (item->generation != item_generation) { | ||
467 | WARN_ON(1); | ||
468 | ret = -ENOENT; | ||
469 | } else { | ||
470 | gc_txpriv = item->txpriv; | ||
471 | gc_skb = item->skb; | ||
472 | item->skb = NULL; | ||
473 | --queue->num_pending; | ||
474 | --queue->num_queued; | ||
475 | ++queue->num_sent; | ||
476 | ++item->generation; | ||
477 | /* Do not use list_move_tail here, but list_move: | ||
478 | * try to utilize cache row. | ||
479 | */ | ||
480 | list_move(&item->head, &queue->free_pool); | ||
481 | |||
482 | if (queue->overfull && | ||
483 | (queue->num_queued <= (queue->capacity >> 1))) { | ||
484 | queue->overfull = false; | ||
485 | __cw1200_queue_unlock(queue); | ||
486 | } | ||
487 | } | ||
488 | spin_unlock_bh(&queue->lock); | ||
489 | |||
490 | if (gc_skb) | ||
491 | stats->skb_dtor(stats->priv, gc_skb, &gc_txpriv); | ||
492 | |||
493 | return ret; | ||
494 | } | ||
495 | |||
496 | int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id, | ||
497 | struct sk_buff **skb, | ||
498 | const struct cw1200_txpriv **txpriv) | ||
499 | { | ||
500 | int ret = 0; | ||
501 | u8 queue_generation, queue_id, item_generation, item_id; | ||
502 | struct cw1200_queue_item *item; | ||
503 | cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id, | ||
504 | &item_generation, &item_id); | ||
505 | |||
506 | item = &queue->pool[item_id]; | ||
507 | |||
508 | spin_lock_bh(&queue->lock); | ||
509 | BUG_ON(queue_id != queue->queue_id); | ||
510 | if (queue_generation != queue->generation) { | ||
511 | ret = -ENOENT; | ||
512 | } else if (item_id >= (unsigned) queue->capacity) { | ||
513 | WARN_ON(1); | ||
514 | ret = -EINVAL; | ||
515 | } else if (item->generation != item_generation) { | ||
516 | WARN_ON(1); | ||
517 | ret = -ENOENT; | ||
518 | } else { | ||
519 | *skb = item->skb; | ||
520 | *txpriv = &item->txpriv; | ||
521 | } | ||
522 | spin_unlock_bh(&queue->lock); | ||
523 | return ret; | ||
524 | } | ||
525 | |||
526 | void cw1200_queue_lock(struct cw1200_queue *queue) | ||
527 | { | ||
528 | spin_lock_bh(&queue->lock); | ||
529 | __cw1200_queue_lock(queue); | ||
530 | spin_unlock_bh(&queue->lock); | ||
531 | } | ||
532 | |||
533 | void cw1200_queue_unlock(struct cw1200_queue *queue) | ||
534 | { | ||
535 | spin_lock_bh(&queue->lock); | ||
536 | __cw1200_queue_unlock(queue); | ||
537 | spin_unlock_bh(&queue->lock); | ||
538 | } | ||
539 | |||
540 | bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue, | ||
541 | unsigned long *timestamp, | ||
542 | u32 pending_frame_id) | ||
543 | { | ||
544 | struct cw1200_queue_item *item; | ||
545 | bool ret; | ||
546 | |||
547 | spin_lock_bh(&queue->lock); | ||
548 | ret = !list_empty(&queue->pending); | ||
549 | if (ret) { | ||
550 | list_for_each_entry(item, &queue->pending, head) { | ||
551 | if (item->packet_id != pending_frame_id) | ||
552 | if (time_before(item->xmit_timestamp, | ||
553 | *timestamp)) | ||
554 | *timestamp = item->xmit_timestamp; | ||
555 | } | ||
556 | } | ||
557 | spin_unlock_bh(&queue->lock); | ||
558 | return ret; | ||
559 | } | ||
560 | |||
561 | bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, | ||
562 | u32 link_id_map) | ||
563 | { | ||
564 | bool empty = true; | ||
565 | |||
566 | spin_lock_bh(&stats->lock); | ||
567 | if (link_id_map == (u32)-1) { | ||
568 | empty = stats->num_queued == 0; | ||
569 | } else { | ||
570 | int i; | ||
571 | for (i = 0; i < stats->map_capacity; ++i) { | ||
572 | if (link_id_map & BIT(i)) { | ||
573 | if (stats->link_map_cache[i]) { | ||
574 | empty = false; | ||
575 | break; | ||
576 | } | ||
577 | } | ||
578 | } | ||
579 | } | ||
580 | spin_unlock_bh(&stats->lock); | ||
581 | |||
582 | return empty; | ||
583 | } | ||
diff --git a/drivers/net/wireless/cw1200/queue.h b/drivers/net/wireless/cw1200/queue.h new file mode 100644 index 000000000000..119f9c79c14e --- /dev/null +++ b/drivers/net/wireless/cw1200/queue.h | |||
@@ -0,0 +1,116 @@ | |||
1 | /* | ||
2 | * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers | ||
3 | * | ||
4 | * Copyright (c) 2010, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef CW1200_QUEUE_H_INCLUDED | ||
13 | #define CW1200_QUEUE_H_INCLUDED | ||
14 | |||
15 | /* private */ struct cw1200_queue_item; | ||
16 | |||
17 | /* extern */ struct sk_buff; | ||
18 | /* extern */ struct wsm_tx; | ||
19 | /* extern */ struct cw1200_common; | ||
20 | /* extern */ struct ieee80211_tx_queue_stats; | ||
21 | /* extern */ struct cw1200_txpriv; | ||
22 | |||
23 | /* forward */ struct cw1200_queue_stats; | ||
24 | |||
25 | typedef void (*cw1200_queue_skb_dtor_t)(struct cw1200_common *priv, | ||
26 | struct sk_buff *skb, | ||
27 | const struct cw1200_txpriv *txpriv); | ||
28 | |||
29 | struct cw1200_queue { | ||
30 | struct cw1200_queue_stats *stats; | ||
31 | size_t capacity; | ||
32 | size_t num_queued; | ||
33 | size_t num_pending; | ||
34 | size_t num_sent; | ||
35 | struct cw1200_queue_item *pool; | ||
36 | struct list_head queue; | ||
37 | struct list_head free_pool; | ||
38 | struct list_head pending; | ||
39 | int tx_locked_cnt; | ||
40 | int *link_map_cache; | ||
41 | bool overfull; | ||
42 | spinlock_t lock; /* Protect queue entry */ | ||
43 | u8 queue_id; | ||
44 | u8 generation; | ||
45 | struct timer_list gc; | ||
46 | unsigned long ttl; | ||
47 | }; | ||
48 | |||
49 | struct cw1200_queue_stats { | ||
50 | spinlock_t lock; /* Protect stats entry */ | ||
51 | int *link_map_cache; | ||
52 | int num_queued; | ||
53 | size_t map_capacity; | ||
54 | wait_queue_head_t wait_link_id_empty; | ||
55 | cw1200_queue_skb_dtor_t skb_dtor; | ||
56 | struct cw1200_common *priv; | ||
57 | }; | ||
58 | |||
59 | struct cw1200_txpriv { | ||
60 | u8 link_id; | ||
61 | u8 raw_link_id; | ||
62 | u8 tid; | ||
63 | u8 rate_id; | ||
64 | u8 offset; | ||
65 | }; | ||
66 | |||
67 | int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, | ||
68 | size_t map_capacity, | ||
69 | cw1200_queue_skb_dtor_t skb_dtor, | ||
70 | struct cw1200_common *priv); | ||
71 | int cw1200_queue_init(struct cw1200_queue *queue, | ||
72 | struct cw1200_queue_stats *stats, | ||
73 | u8 queue_id, | ||
74 | size_t capacity, | ||
75 | unsigned long ttl); | ||
76 | int cw1200_queue_clear(struct cw1200_queue *queue); | ||
77 | void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats); | ||
78 | void cw1200_queue_deinit(struct cw1200_queue *queue); | ||
79 | |||
80 | size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, | ||
81 | u32 link_id_map); | ||
82 | int cw1200_queue_put(struct cw1200_queue *queue, | ||
83 | struct sk_buff *skb, | ||
84 | struct cw1200_txpriv *txpriv); | ||
85 | int cw1200_queue_get(struct cw1200_queue *queue, | ||
86 | u32 link_id_map, | ||
87 | struct wsm_tx **tx, | ||
88 | struct ieee80211_tx_info **tx_info, | ||
89 | const struct cw1200_txpriv **txpriv); | ||
90 | int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id); | ||
91 | int cw1200_queue_requeue_all(struct cw1200_queue *queue); | ||
92 | int cw1200_queue_remove(struct cw1200_queue *queue, | ||
93 | u32 packet_id); | ||
94 | int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id, | ||
95 | struct sk_buff **skb, | ||
96 | const struct cw1200_txpriv **txpriv); | ||
97 | void cw1200_queue_lock(struct cw1200_queue *queue); | ||
98 | void cw1200_queue_unlock(struct cw1200_queue *queue); | ||
99 | bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue, | ||
100 | unsigned long *timestamp, | ||
101 | u32 pending_frame_id); | ||
102 | |||
103 | bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, | ||
104 | u32 link_id_map); | ||
105 | |||
106 | static inline u8 cw1200_queue_get_queue_id(u32 packet_id) | ||
107 | { | ||
108 | return (packet_id >> 16) & 0xFF; | ||
109 | } | ||
110 | |||
111 | static inline u8 cw1200_queue_get_generation(u32 packet_id) | ||
112 | { | ||
113 | return (packet_id >> 8) & 0xFF; | ||
114 | } | ||
115 | |||
116 | #endif /* CW1200_QUEUE_H_INCLUDED */ | ||
diff --git a/drivers/net/wireless/cw1200/sbus.h b/drivers/net/wireless/cw1200/sbus.h new file mode 100644 index 000000000000..603fd25eaa4a --- /dev/null +++ b/drivers/net/wireless/cw1200/sbus.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* | ||
2 | * Common sbus abstraction layer interface for cw1200 wireless driver | ||
3 | * | ||
4 | * Copyright (c) 2010, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef CW1200_SBUS_H | ||
13 | #define CW1200_SBUS_H | ||
14 | |||
15 | /* | ||
16 | * sbus priv forward definition. | ||
17 | * Implemented and instantiated in particular modules. | ||
18 | */ | ||
19 | struct sbus_priv; | ||
20 | |||
21 | void cw1200_irq_handler(struct cw1200_common *priv); | ||
22 | |||
23 | /* This MUST be wrapped with sbus_ops->lock/unlock! */ | ||
24 | int __cw1200_irq_enable(struct cw1200_common *priv, int enable); | ||
25 | |||
26 | struct sbus_ops { | ||
27 | int (*sbus_memcpy_fromio)(struct sbus_priv *self, unsigned int addr, | ||
28 | void *dst, int count); | ||
29 | int (*sbus_memcpy_toio)(struct sbus_priv *self, unsigned int addr, | ||
30 | const void *src, int count); | ||
31 | void (*lock)(struct sbus_priv *self); | ||
32 | void (*unlock)(struct sbus_priv *self); | ||
33 | size_t (*align_size)(struct sbus_priv *self, size_t size); | ||
34 | int (*power_mgmt)(struct sbus_priv *self, bool suspend); | ||
35 | }; | ||
36 | |||
37 | #endif /* CW1200_SBUS_H */ | ||
diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c new file mode 100644 index 000000000000..ee3c19037aac --- /dev/null +++ b/drivers/net/wireless/cw1200/scan.c | |||
@@ -0,0 +1,461 @@ | |||
1 | /* | ||
2 | * Scan implementation for ST-Ericsson CW1200 mac80211 drivers | ||
3 | * | ||
4 | * Copyright (c) 2010, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/sched.h> | ||
13 | #include "cw1200.h" | ||
14 | #include "scan.h" | ||
15 | #include "sta.h" | ||
16 | #include "pm.h" | ||
17 | |||
18 | static void cw1200_scan_restart_delayed(struct cw1200_common *priv); | ||
19 | |||
20 | static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) | ||
21 | { | ||
22 | int ret, i; | ||
23 | int tmo = 2000; | ||
24 | |||
25 | switch (priv->join_status) { | ||
26 | case CW1200_JOIN_STATUS_PRE_STA: | ||
27 | case CW1200_JOIN_STATUS_JOINING: | ||
28 | return -EBUSY; | ||
29 | default: | ||
30 | break; | ||
31 | } | ||
32 | |||
33 | wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n", | ||
34 | scan->type, scan->num_channels, scan->flags); | ||
35 | |||
36 | for (i = 0; i < scan->num_channels; ++i) | ||
37 | tmo += scan->ch[i].max_chan_time + 10; | ||
38 | |||
39 | cancel_delayed_work_sync(&priv->clear_recent_scan_work); | ||
40 | atomic_set(&priv->scan.in_progress, 1); | ||
41 | atomic_set(&priv->recent_scan, 1); | ||
42 | cw1200_pm_stay_awake(&priv->pm_state, tmo * HZ / 1000); | ||
43 | queue_delayed_work(priv->workqueue, &priv->scan.timeout, | ||
44 | tmo * HZ / 1000); | ||
45 | ret = wsm_scan(priv, scan); | ||
46 | if (ret) { | ||
47 | atomic_set(&priv->scan.in_progress, 0); | ||
48 | cancel_delayed_work_sync(&priv->scan.timeout); | ||
49 | cw1200_scan_restart_delayed(priv); | ||
50 | } | ||
51 | return ret; | ||
52 | } | ||
53 | |||
54 | int cw1200_hw_scan(struct ieee80211_hw *hw, | ||
55 | struct ieee80211_vif *vif, | ||
56 | struct cfg80211_scan_request *req) | ||
57 | { | ||
58 | struct cw1200_common *priv = hw->priv; | ||
59 | struct wsm_template_frame frame = { | ||
60 | .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, | ||
61 | }; | ||
62 | int i, ret; | ||
63 | |||
64 | if (!priv->vif) | ||
65 | return -EINVAL; | ||
66 | |||
67 | /* Scan when P2P_GO corrupt firmware MiniAP mode */ | ||
68 | if (priv->join_status == CW1200_JOIN_STATUS_AP) | ||
69 | return -EOPNOTSUPP; | ||
70 | |||
71 | if (req->n_ssids == 1 && !req->ssids[0].ssid_len) | ||
72 | req->n_ssids = 0; | ||
73 | |||
74 | wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n", | ||
75 | req->n_ssids); | ||
76 | |||
77 | if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS) | ||
78 | return -EINVAL; | ||
79 | |||
80 | frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0, | ||
81 | req->ie_len); | ||
82 | if (!frame.skb) | ||
83 | return -ENOMEM; | ||
84 | |||
85 | if (req->ie_len) | ||
86 | memcpy(skb_put(frame.skb, req->ie_len), req->ie, req->ie_len); | ||
87 | |||
88 | /* will be unlocked in cw1200_scan_work() */ | ||
89 | down(&priv->scan.lock); | ||
90 | mutex_lock(&priv->conf_mutex); | ||
91 | |||
92 | ret = wsm_set_template_frame(priv, &frame); | ||
93 | if (!ret) { | ||
94 | /* Host want to be the probe responder. */ | ||
95 | ret = wsm_set_probe_responder(priv, true); | ||
96 | } | ||
97 | if (ret) { | ||
98 | mutex_unlock(&priv->conf_mutex); | ||
99 | up(&priv->scan.lock); | ||
100 | dev_kfree_skb(frame.skb); | ||
101 | return ret; | ||
102 | } | ||
103 | |||
104 | wsm_lock_tx(priv); | ||
105 | |||
106 | BUG_ON(priv->scan.req); | ||
107 | priv->scan.req = req; | ||
108 | priv->scan.n_ssids = 0; | ||
109 | priv->scan.status = 0; | ||
110 | priv->scan.begin = &req->channels[0]; | ||
111 | priv->scan.curr = priv->scan.begin; | ||
112 | priv->scan.end = &req->channels[req->n_channels]; | ||
113 | priv->scan.output_power = priv->output_power; | ||
114 | |||
115 | for (i = 0; i < req->n_ssids; ++i) { | ||
116 | struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids]; | ||
117 | memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid)); | ||
118 | dst->length = req->ssids[i].ssid_len; | ||
119 | ++priv->scan.n_ssids; | ||
120 | } | ||
121 | |||
122 | mutex_unlock(&priv->conf_mutex); | ||
123 | |||
124 | if (frame.skb) | ||
125 | dev_kfree_skb(frame.skb); | ||
126 | queue_work(priv->workqueue, &priv->scan.work); | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | void cw1200_scan_work(struct work_struct *work) | ||
131 | { | ||
132 | struct cw1200_common *priv = container_of(work, struct cw1200_common, | ||
133 | scan.work); | ||
134 | struct ieee80211_channel **it; | ||
135 | struct wsm_scan scan = { | ||
136 | .type = WSM_SCAN_TYPE_FOREGROUND, | ||
137 | .flags = WSM_SCAN_FLAG_SPLIT_METHOD, | ||
138 | }; | ||
139 | bool first_run = (priv->scan.begin == priv->scan.curr && | ||
140 | priv->scan.begin != priv->scan.end); | ||
141 | int i; | ||
142 | |||
143 | if (first_run) { | ||
144 | /* Firmware gets crazy if scan request is sent | ||
145 | * when STA is joined but not yet associated. | ||
146 | * Force unjoin in this case. | ||
147 | */ | ||
148 | if (cancel_delayed_work_sync(&priv->join_timeout) > 0) | ||
149 | cw1200_join_timeout(&priv->join_timeout.work); | ||
150 | } | ||
151 | |||
152 | mutex_lock(&priv->conf_mutex); | ||
153 | |||
154 | if (first_run) { | ||
155 | if (priv->join_status == CW1200_JOIN_STATUS_STA && | ||
156 | !(priv->powersave_mode.mode & WSM_PSM_PS)) { | ||
157 | struct wsm_set_pm pm = priv->powersave_mode; | ||
158 | pm.mode = WSM_PSM_PS; | ||
159 | cw1200_set_pm(priv, &pm); | ||
160 | } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { | ||
161 | /* FW bug: driver has to restart p2p-dev mode | ||
162 | * after scan | ||
163 | */ | ||
164 | cw1200_disable_listening(priv); | ||
165 | } | ||
166 | } | ||
167 | |||
168 | if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) { | ||
169 | if (priv->scan.output_power != priv->output_power) | ||
170 | wsm_set_output_power(priv, priv->output_power * 10); | ||
171 | if (priv->join_status == CW1200_JOIN_STATUS_STA && | ||
172 | !(priv->powersave_mode.mode & WSM_PSM_PS)) | ||
173 | cw1200_set_pm(priv, &priv->powersave_mode); | ||
174 | |||
175 | if (priv->scan.status < 0) | ||
176 | wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan failed (%d).\n", | ||
177 | priv->scan.status); | ||
178 | else if (priv->scan.req) | ||
179 | wiphy_dbg(priv->hw->wiphy, | ||
180 | "[SCAN] Scan completed.\n"); | ||
181 | else | ||
182 | wiphy_dbg(priv->hw->wiphy, | ||
183 | "[SCAN] Scan canceled.\n"); | ||
184 | |||
185 | priv->scan.req = NULL; | ||
186 | cw1200_scan_restart_delayed(priv); | ||
187 | wsm_unlock_tx(priv); | ||
188 | mutex_unlock(&priv->conf_mutex); | ||
189 | ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0); | ||
190 | up(&priv->scan.lock); | ||
191 | return; | ||
192 | } else { | ||
193 | struct ieee80211_channel *first = *priv->scan.curr; | ||
194 | for (it = priv->scan.curr + 1, i = 1; | ||
195 | it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS; | ||
196 | ++it, ++i) { | ||
197 | if ((*it)->band != first->band) | ||
198 | break; | ||
199 | if (((*it)->flags ^ first->flags) & | ||
200 | IEEE80211_CHAN_PASSIVE_SCAN) | ||
201 | break; | ||
202 | if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && | ||
203 | (*it)->max_power != first->max_power) | ||
204 | break; | ||
205 | } | ||
206 | scan.band = first->band; | ||
207 | |||
208 | if (priv->scan.req->no_cck) | ||
209 | scan.max_tx_rate = WSM_TRANSMIT_RATE_6; | ||
210 | else | ||
211 | scan.max_tx_rate = WSM_TRANSMIT_RATE_1; | ||
212 | scan.num_probes = | ||
213 | (first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2; | ||
214 | scan.num_ssids = priv->scan.n_ssids; | ||
215 | scan.ssids = &priv->scan.ssids[0]; | ||
216 | scan.num_channels = it - priv->scan.curr; | ||
217 | /* TODO: Is it optimal? */ | ||
218 | scan.probe_delay = 100; | ||
219 | /* It is not stated in WSM specification, however | ||
220 | * FW team says that driver may not use FG scan | ||
221 | * when joined. | ||
222 | */ | ||
223 | if (priv->join_status == CW1200_JOIN_STATUS_STA) { | ||
224 | scan.type = WSM_SCAN_TYPE_BACKGROUND; | ||
225 | scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND; | ||
226 | } | ||
227 | scan.ch = kzalloc( | ||
228 | sizeof(struct wsm_scan_ch) * (it - priv->scan.curr), | ||
229 | GFP_KERNEL); | ||
230 | if (!scan.ch) { | ||
231 | priv->scan.status = -ENOMEM; | ||
232 | goto fail; | ||
233 | } | ||
234 | for (i = 0; i < scan.num_channels; ++i) { | ||
235 | scan.ch[i].number = priv->scan.curr[i]->hw_value; | ||
236 | if (priv->scan.curr[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) { | ||
237 | scan.ch[i].min_chan_time = 50; | ||
238 | scan.ch[i].max_chan_time = 100; | ||
239 | } else { | ||
240 | scan.ch[i].min_chan_time = 10; | ||
241 | scan.ch[i].max_chan_time = 25; | ||
242 | } | ||
243 | } | ||
244 | if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && | ||
245 | priv->scan.output_power != first->max_power) { | ||
246 | priv->scan.output_power = first->max_power; | ||
247 | wsm_set_output_power(priv, | ||
248 | priv->scan.output_power * 10); | ||
249 | } | ||
250 | priv->scan.status = cw1200_scan_start(priv, &scan); | ||
251 | kfree(scan.ch); | ||
252 | if (priv->scan.status) | ||
253 | goto fail; | ||
254 | priv->scan.curr = it; | ||
255 | } | ||
256 | mutex_unlock(&priv->conf_mutex); | ||
257 | return; | ||
258 | |||
259 | fail: | ||
260 | priv->scan.curr = priv->scan.end; | ||
261 | mutex_unlock(&priv->conf_mutex); | ||
262 | queue_work(priv->workqueue, &priv->scan.work); | ||
263 | return; | ||
264 | } | ||
265 | |||
266 | static void cw1200_scan_restart_delayed(struct cw1200_common *priv) | ||
267 | { | ||
268 | /* FW bug: driver has to restart p2p-dev mode after scan. */ | ||
269 | if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { | ||
270 | cw1200_enable_listening(priv); | ||
271 | cw1200_update_filtering(priv); | ||
272 | } | ||
273 | |||
274 | if (priv->delayed_unjoin) { | ||
275 | priv->delayed_unjoin = false; | ||
276 | if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) | ||
277 | wsm_unlock_tx(priv); | ||
278 | } else if (priv->delayed_link_loss) { | ||
279 | wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n"); | ||
280 | priv->delayed_link_loss = 0; | ||
281 | cw1200_cqm_bssloss_sm(priv, 1, 0, 0); | ||
282 | } | ||
283 | } | ||
284 | |||
285 | static void cw1200_scan_complete(struct cw1200_common *priv) | ||
286 | { | ||
287 | queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ); | ||
288 | if (priv->scan.direct_probe) { | ||
289 | wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n"); | ||
290 | cw1200_scan_restart_delayed(priv); | ||
291 | priv->scan.direct_probe = 0; | ||
292 | up(&priv->scan.lock); | ||
293 | wsm_unlock_tx(priv); | ||
294 | } else { | ||
295 | cw1200_scan_work(&priv->scan.work); | ||
296 | } | ||
297 | } | ||
298 | |||
299 | void cw1200_scan_failed_cb(struct cw1200_common *priv) | ||
300 | { | ||
301 | if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) | ||
302 | /* STA is stopped. */ | ||
303 | return; | ||
304 | |||
305 | if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) { | ||
306 | priv->scan.status = -EIO; | ||
307 | queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0); | ||
308 | } | ||
309 | } | ||
310 | |||
311 | |||
312 | void cw1200_scan_complete_cb(struct cw1200_common *priv, | ||
313 | struct wsm_scan_complete *arg) | ||
314 | { | ||
315 | if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) | ||
316 | /* STA is stopped. */ | ||
317 | return; | ||
318 | |||
319 | if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) { | ||
320 | priv->scan.status = 1; | ||
321 | queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0); | ||
322 | } | ||
323 | } | ||
324 | |||
325 | void cw1200_clear_recent_scan_work(struct work_struct *work) | ||
326 | { | ||
327 | struct cw1200_common *priv = | ||
328 | container_of(work, struct cw1200_common, | ||
329 | clear_recent_scan_work.work); | ||
330 | atomic_xchg(&priv->recent_scan, 0); | ||
331 | } | ||
332 | |||
333 | void cw1200_scan_timeout(struct work_struct *work) | ||
334 | { | ||
335 | struct cw1200_common *priv = | ||
336 | container_of(work, struct cw1200_common, scan.timeout.work); | ||
337 | if (atomic_xchg(&priv->scan.in_progress, 0)) { | ||
338 | if (priv->scan.status > 0) { | ||
339 | priv->scan.status = 0; | ||
340 | } else if (!priv->scan.status) { | ||
341 | wiphy_warn(priv->hw->wiphy, | ||
342 | "Timeout waiting for scan complete notification.\n"); | ||
343 | priv->scan.status = -ETIMEDOUT; | ||
344 | priv->scan.curr = priv->scan.end; | ||
345 | wsm_stop_scan(priv); | ||
346 | } | ||
347 | cw1200_scan_complete(priv); | ||
348 | } | ||
349 | } | ||
350 | |||
351 | void cw1200_probe_work(struct work_struct *work) | ||
352 | { | ||
353 | struct cw1200_common *priv = | ||
354 | container_of(work, struct cw1200_common, scan.probe_work.work); | ||
355 | u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id); | ||
356 | struct cw1200_queue *queue = &priv->tx_queue[queue_id]; | ||
357 | const struct cw1200_txpriv *txpriv; | ||
358 | struct wsm_tx *wsm; | ||
359 | struct wsm_template_frame frame = { | ||
360 | .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, | ||
361 | }; | ||
362 | struct wsm_ssid ssids[1] = {{ | ||
363 | .length = 0, | ||
364 | } }; | ||
365 | struct wsm_scan_ch ch[1] = {{ | ||
366 | .min_chan_time = 0, | ||
367 | .max_chan_time = 10, | ||
368 | } }; | ||
369 | struct wsm_scan scan = { | ||
370 | .type = WSM_SCAN_TYPE_FOREGROUND, | ||
371 | .num_probes = 1, | ||
372 | .probe_delay = 0, | ||
373 | .num_channels = 1, | ||
374 | .ssids = ssids, | ||
375 | .ch = ch, | ||
376 | }; | ||
377 | u8 *ies; | ||
378 | size_t ies_len; | ||
379 | int ret; | ||
380 | |||
381 | wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n"); | ||
382 | |||
383 | mutex_lock(&priv->conf_mutex); | ||
384 | if (down_trylock(&priv->scan.lock)) { | ||
385 | /* Scan is already in progress. Requeue self. */ | ||
386 | schedule(); | ||
387 | queue_delayed_work(priv->workqueue, | ||
388 | &priv->scan.probe_work, HZ / 10); | ||
389 | mutex_unlock(&priv->conf_mutex); | ||
390 | return; | ||
391 | } | ||
392 | |||
393 | /* Make sure we still have a pending probe req */ | ||
394 | if (cw1200_queue_get_skb(queue, priv->pending_frame_id, | ||
395 | &frame.skb, &txpriv)) { | ||
396 | up(&priv->scan.lock); | ||
397 | mutex_unlock(&priv->conf_mutex); | ||
398 | wsm_unlock_tx(priv); | ||
399 | return; | ||
400 | } | ||
401 | wsm = (struct wsm_tx *)frame.skb->data; | ||
402 | scan.max_tx_rate = wsm->max_tx_rate; | ||
403 | scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? | ||
404 | WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; | ||
405 | if (priv->join_status == CW1200_JOIN_STATUS_STA || | ||
406 | priv->join_status == CW1200_JOIN_STATUS_IBSS) { | ||
407 | scan.type = WSM_SCAN_TYPE_BACKGROUND; | ||
408 | scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND; | ||
409 | } | ||
410 | ch[0].number = priv->channel->hw_value; | ||
411 | |||
412 | skb_pull(frame.skb, txpriv->offset); | ||
413 | |||
414 | ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)]; | ||
415 | ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr); | ||
416 | |||
417 | if (ies_len) { | ||
418 | u8 *ssidie = | ||
419 | (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len); | ||
420 | if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) { | ||
421 | u8 *nextie = &ssidie[2 + ssidie[1]]; | ||
422 | /* Remove SSID from the IE list. It has to be provided | ||
423 | * as a separate argument in cw1200_scan_start call | ||
424 | */ | ||
425 | |||
426 | /* Store SSID localy */ | ||
427 | ssids[0].length = ssidie[1]; | ||
428 | memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length); | ||
429 | scan.num_ssids = 1; | ||
430 | |||
431 | /* Remove SSID from IE list */ | ||
432 | ssidie[1] = 0; | ||
433 | memmove(&ssidie[2], nextie, &ies[ies_len] - nextie); | ||
434 | skb_trim(frame.skb, frame.skb->len - ssids[0].length); | ||
435 | } | ||
436 | } | ||
437 | |||
438 | /* FW bug: driver has to restart p2p-dev mode after scan */ | ||
439 | if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) | ||
440 | cw1200_disable_listening(priv); | ||
441 | ret = wsm_set_template_frame(priv, &frame); | ||
442 | priv->scan.direct_probe = 1; | ||
443 | if (!ret) { | ||
444 | wsm_flush_tx(priv); | ||
445 | ret = cw1200_scan_start(priv, &scan); | ||
446 | } | ||
447 | mutex_unlock(&priv->conf_mutex); | ||
448 | |||
449 | skb_push(frame.skb, txpriv->offset); | ||
450 | if (!ret) | ||
451 | IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK; | ||
452 | BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id)); | ||
453 | |||
454 | if (ret) { | ||
455 | priv->scan.direct_probe = 0; | ||
456 | up(&priv->scan.lock); | ||
457 | wsm_unlock_tx(priv); | ||
458 | } | ||
459 | |||
460 | return; | ||
461 | } | ||
diff --git a/drivers/net/wireless/cw1200/scan.h b/drivers/net/wireless/cw1200/scan.h new file mode 100644 index 000000000000..5a8296ccfa82 --- /dev/null +++ b/drivers/net/wireless/cw1200/scan.h | |||
@@ -0,0 +1,56 @@ | |||
1 | /* | ||
2 | * Scan interface for ST-Ericsson CW1200 mac80211 drivers | ||
3 | * | ||
4 | * Copyright (c) 2010, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef SCAN_H_INCLUDED | ||
13 | #define SCAN_H_INCLUDED | ||
14 | |||
15 | #include <linux/semaphore.h> | ||
16 | #include "wsm.h" | ||
17 | |||
18 | /* external */ struct sk_buff; | ||
19 | /* external */ struct cfg80211_scan_request; | ||
20 | /* external */ struct ieee80211_channel; | ||
21 | /* external */ struct ieee80211_hw; | ||
22 | /* external */ struct work_struct; | ||
23 | |||
24 | struct cw1200_scan { | ||
25 | struct semaphore lock; | ||
26 | struct work_struct work; | ||
27 | struct delayed_work timeout; | ||
28 | struct cfg80211_scan_request *req; | ||
29 | struct ieee80211_channel **begin; | ||
30 | struct ieee80211_channel **curr; | ||
31 | struct ieee80211_channel **end; | ||
32 | struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS]; | ||
33 | int output_power; | ||
34 | int n_ssids; | ||
35 | int status; | ||
36 | atomic_t in_progress; | ||
37 | /* Direct probe requests workaround */ | ||
38 | struct delayed_work probe_work; | ||
39 | int direct_probe; | ||
40 | }; | ||
41 | |||
42 | int cw1200_hw_scan(struct ieee80211_hw *hw, | ||
43 | struct ieee80211_vif *vif, | ||
44 | struct cfg80211_scan_request *req); | ||
45 | void cw1200_scan_work(struct work_struct *work); | ||
46 | void cw1200_scan_timeout(struct work_struct *work); | ||
47 | void cw1200_clear_recent_scan_work(struct work_struct *work); | ||
48 | void cw1200_scan_complete_cb(struct cw1200_common *priv, | ||
49 | struct wsm_scan_complete *arg); | ||
50 | void cw1200_scan_failed_cb(struct cw1200_common *priv); | ||
51 | |||
52 | /* ******************************************************************** */ | ||
53 | /* Raw probe requests TX workaround */ | ||
54 | void cw1200_probe_work(struct work_struct *work); | ||
55 | |||
56 | #endif | ||
diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c new file mode 100644 index 000000000000..679c55f15c67 --- /dev/null +++ b/drivers/net/wireless/cw1200/sta.c | |||
@@ -0,0 +1,2406 @@ | |||
1 | /* | ||
2 | * Mac80211 STA API for ST-Ericsson CW1200 drivers | ||
3 | * | ||
4 | * Copyright (c) 2010, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/vmalloc.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/firmware.h> | ||
15 | #include <linux/module.h> | ||
16 | |||
17 | #include "cw1200.h" | ||
18 | #include "sta.h" | ||
19 | #include "fwio.h" | ||
20 | #include "bh.h" | ||
21 | #include "debug.h" | ||
22 | |||
23 | #ifndef ERP_INFO_BYTE_OFFSET | ||
24 | #define ERP_INFO_BYTE_OFFSET 2 | ||
25 | #endif | ||
26 | |||
27 | static void cw1200_do_join(struct cw1200_common *priv); | ||
28 | static void cw1200_do_unjoin(struct cw1200_common *priv); | ||
29 | |||
30 | static int cw1200_upload_beacon(struct cw1200_common *priv); | ||
31 | static int cw1200_upload_pspoll(struct cw1200_common *priv); | ||
32 | static int cw1200_upload_null(struct cw1200_common *priv); | ||
33 | static int cw1200_upload_qosnull(struct cw1200_common *priv); | ||
34 | static int cw1200_start_ap(struct cw1200_common *priv); | ||
35 | static int cw1200_update_beaconing(struct cw1200_common *priv); | ||
36 | static int cw1200_enable_beaconing(struct cw1200_common *priv, | ||
37 | bool enable); | ||
38 | static void __cw1200_sta_notify(struct ieee80211_hw *dev, | ||
39 | struct ieee80211_vif *vif, | ||
40 | enum sta_notify_cmd notify_cmd, | ||
41 | int link_id); | ||
42 | static int __cw1200_flush(struct cw1200_common *priv, bool drop); | ||
43 | |||
44 | static inline void __cw1200_free_event_queue(struct list_head *list) | ||
45 | { | ||
46 | struct cw1200_wsm_event *event, *tmp; | ||
47 | list_for_each_entry_safe(event, tmp, list, link) { | ||
48 | list_del(&event->link); | ||
49 | kfree(event); | ||
50 | } | ||
51 | } | ||
52 | |||
53 | /* ******************************************************************** */ | ||
54 | /* STA API */ | ||
55 | |||
56 | int cw1200_start(struct ieee80211_hw *dev) | ||
57 | { | ||
58 | struct cw1200_common *priv = dev->priv; | ||
59 | int ret = 0; | ||
60 | |||
61 | cw1200_pm_stay_awake(&priv->pm_state, HZ); | ||
62 | |||
63 | mutex_lock(&priv->conf_mutex); | ||
64 | |||
65 | /* default EDCA */ | ||
66 | WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false); | ||
67 | WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false); | ||
68 | WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false); | ||
69 | WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false); | ||
70 | ret = wsm_set_edca_params(priv, &priv->edca); | ||
71 | if (ret) | ||
72 | goto out; | ||
73 | |||
74 | ret = cw1200_set_uapsd_param(priv, &priv->edca); | ||
75 | if (ret) | ||
76 | goto out; | ||
77 | |||
78 | priv->setbssparams_done = false; | ||
79 | |||
80 | memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN); | ||
81 | priv->mode = NL80211_IFTYPE_MONITOR; | ||
82 | priv->wep_default_key_id = -1; | ||
83 | |||
84 | priv->cqm_beacon_loss_count = 10; | ||
85 | |||
86 | ret = cw1200_setup_mac(priv); | ||
87 | if (ret) | ||
88 | goto out; | ||
89 | |||
90 | out: | ||
91 | mutex_unlock(&priv->conf_mutex); | ||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | void cw1200_stop(struct ieee80211_hw *dev) | ||
96 | { | ||
97 | struct cw1200_common *priv = dev->priv; | ||
98 | LIST_HEAD(list); | ||
99 | int i; | ||
100 | |||
101 | wsm_lock_tx(priv); | ||
102 | |||
103 | while (down_trylock(&priv->scan.lock)) { | ||
104 | /* Scan is in progress. Force it to stop. */ | ||
105 | priv->scan.req = NULL; | ||
106 | schedule(); | ||
107 | } | ||
108 | up(&priv->scan.lock); | ||
109 | |||
110 | cancel_delayed_work_sync(&priv->scan.probe_work); | ||
111 | cancel_delayed_work_sync(&priv->scan.timeout); | ||
112 | cancel_delayed_work_sync(&priv->clear_recent_scan_work); | ||
113 | cancel_delayed_work_sync(&priv->join_timeout); | ||
114 | cw1200_cqm_bssloss_sm(priv, 0, 0, 0); | ||
115 | cancel_work_sync(&priv->unjoin_work); | ||
116 | cancel_delayed_work_sync(&priv->link_id_gc_work); | ||
117 | flush_workqueue(priv->workqueue); | ||
118 | del_timer_sync(&priv->mcast_timeout); | ||
119 | mutex_lock(&priv->conf_mutex); | ||
120 | priv->mode = NL80211_IFTYPE_UNSPECIFIED; | ||
121 | priv->listening = false; | ||
122 | |||
123 | spin_lock(&priv->event_queue_lock); | ||
124 | list_splice_init(&priv->event_queue, &list); | ||
125 | spin_unlock(&priv->event_queue_lock); | ||
126 | __cw1200_free_event_queue(&list); | ||
127 | |||
128 | |||
129 | priv->join_status = CW1200_JOIN_STATUS_PASSIVE; | ||
130 | priv->join_pending = false; | ||
131 | |||
132 | for (i = 0; i < 4; i++) | ||
133 | cw1200_queue_clear(&priv->tx_queue[i]); | ||
134 | mutex_unlock(&priv->conf_mutex); | ||
135 | tx_policy_clean(priv); | ||
136 | |||
137 | /* HACK! */ | ||
138 | if (atomic_xchg(&priv->tx_lock, 1) != 1) | ||
139 | pr_debug("[STA] TX is force-unlocked due to stop request.\n"); | ||
140 | |||
141 | wsm_unlock_tx(priv); | ||
142 | atomic_xchg(&priv->tx_lock, 0); /* for recovery to work */ | ||
143 | } | ||
144 | |||
145 | static int cw1200_bssloss_mitigation = 1; | ||
146 | module_param(cw1200_bssloss_mitigation, int, 0644); | ||
147 | MODULE_PARM_DESC(cw1200_bssloss_mitigation, "BSS Loss mitigation. 0 == disabled, 1 == enabled (default)"); | ||
148 | |||
149 | |||
150 | void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, | ||
151 | int init, int good, int bad) | ||
152 | { | ||
153 | int tx = 0; | ||
154 | |||
155 | priv->delayed_link_loss = 0; | ||
156 | cancel_work_sync(&priv->bss_params_work); | ||
157 | |||
158 | pr_debug("[STA] CQM BSSLOSS_SM: state: %d init %d good %d bad: %d txlock: %d uj: %d\n", | ||
159 | priv->bss_loss_state, | ||
160 | init, good, bad, | ||
161 | atomic_read(&priv->tx_lock), | ||
162 | priv->delayed_unjoin); | ||
163 | |||
164 | /* If we have a pending unjoin */ | ||
165 | if (priv->delayed_unjoin) | ||
166 | return; | ||
167 | |||
168 | if (init) { | ||
169 | queue_delayed_work(priv->workqueue, | ||
170 | &priv->bss_loss_work, | ||
171 | HZ); | ||
172 | priv->bss_loss_state = 0; | ||
173 | |||
174 | /* Skip the confimration procedure in P2P case */ | ||
175 | if (!priv->vif->p2p && !atomic_read(&priv->tx_lock)) | ||
176 | tx = 1; | ||
177 | } else if (good) { | ||
178 | cancel_delayed_work_sync(&priv->bss_loss_work); | ||
179 | priv->bss_loss_state = 0; | ||
180 | queue_work(priv->workqueue, &priv->bss_params_work); | ||
181 | } else if (bad) { | ||
182 | /* XXX Should we just keep going until we time out? */ | ||
183 | if (priv->bss_loss_state < 3) | ||
184 | tx = 1; | ||
185 | } else { | ||
186 | cancel_delayed_work_sync(&priv->bss_loss_work); | ||
187 | priv->bss_loss_state = 0; | ||
188 | } | ||
189 | |||
190 | /* Bypass mitigation if it's disabled */ | ||
191 | if (!cw1200_bssloss_mitigation) | ||
192 | tx = 0; | ||
193 | |||
194 | /* Spit out a NULL packet to our AP if necessary */ | ||
195 | if (tx) { | ||
196 | struct sk_buff *skb; | ||
197 | |||
198 | priv->bss_loss_state++; | ||
199 | |||
200 | skb = ieee80211_nullfunc_get(priv->hw, priv->vif); | ||
201 | WARN_ON(!skb); | ||
202 | if (skb) | ||
203 | cw1200_tx(priv->hw, NULL, skb); | ||
204 | } | ||
205 | } | ||
206 | |||
207 | int cw1200_add_interface(struct ieee80211_hw *dev, | ||
208 | struct ieee80211_vif *vif) | ||
209 | { | ||
210 | int ret; | ||
211 | struct cw1200_common *priv = dev->priv; | ||
212 | /* __le32 auto_calibration_mode = __cpu_to_le32(1); */ | ||
213 | |||
214 | vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | | ||
215 | IEEE80211_VIF_SUPPORTS_CQM_RSSI; | ||
216 | |||
217 | mutex_lock(&priv->conf_mutex); | ||
218 | |||
219 | if (priv->mode != NL80211_IFTYPE_MONITOR) { | ||
220 | mutex_unlock(&priv->conf_mutex); | ||
221 | return -EOPNOTSUPP; | ||
222 | } | ||
223 | |||
224 | switch (vif->type) { | ||
225 | case NL80211_IFTYPE_STATION: | ||
226 | case NL80211_IFTYPE_ADHOC: | ||
227 | case NL80211_IFTYPE_MESH_POINT: | ||
228 | case NL80211_IFTYPE_AP: | ||
229 | priv->mode = vif->type; | ||
230 | break; | ||
231 | default: | ||
232 | mutex_unlock(&priv->conf_mutex); | ||
233 | return -EOPNOTSUPP; | ||
234 | } | ||
235 | |||
236 | priv->vif = vif; | ||
237 | memcpy(priv->mac_addr, vif->addr, ETH_ALEN); | ||
238 | ret = cw1200_setup_mac(priv); | ||
239 | /* Enable auto-calibration */ | ||
240 | /* Exception in subsequent channel switch; disabled. | ||
241 | * wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE, | ||
242 | * &auto_calibration_mode, sizeof(auto_calibration_mode)); | ||
243 | */ | ||
244 | |||
245 | mutex_unlock(&priv->conf_mutex); | ||
246 | return ret; | ||
247 | } | ||
248 | |||
249 | void cw1200_remove_interface(struct ieee80211_hw *dev, | ||
250 | struct ieee80211_vif *vif) | ||
251 | { | ||
252 | struct cw1200_common *priv = dev->priv; | ||
253 | struct wsm_reset reset = { | ||
254 | .reset_statistics = true, | ||
255 | }; | ||
256 | int i; | ||
257 | |||
258 | mutex_lock(&priv->conf_mutex); | ||
259 | switch (priv->join_status) { | ||
260 | case CW1200_JOIN_STATUS_JOINING: | ||
261 | case CW1200_JOIN_STATUS_PRE_STA: | ||
262 | case CW1200_JOIN_STATUS_STA: | ||
263 | case CW1200_JOIN_STATUS_IBSS: | ||
264 | wsm_lock_tx(priv); | ||
265 | if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) | ||
266 | wsm_unlock_tx(priv); | ||
267 | break; | ||
268 | case CW1200_JOIN_STATUS_AP: | ||
269 | for (i = 0; priv->link_id_map; ++i) { | ||
270 | if (priv->link_id_map & BIT(i)) { | ||
271 | reset.link_id = i; | ||
272 | wsm_reset(priv, &reset); | ||
273 | priv->link_id_map &= ~BIT(i); | ||
274 | } | ||
275 | } | ||
276 | memset(priv->link_id_db, 0, sizeof(priv->link_id_db)); | ||
277 | priv->sta_asleep_mask = 0; | ||
278 | priv->enable_beacon = false; | ||
279 | priv->tx_multicast = false; | ||
280 | priv->aid0_bit_set = false; | ||
281 | priv->buffered_multicasts = false; | ||
282 | priv->pspoll_mask = 0; | ||
283 | reset.link_id = 0; | ||
284 | wsm_reset(priv, &reset); | ||
285 | break; | ||
286 | case CW1200_JOIN_STATUS_MONITOR: | ||
287 | cw1200_update_listening(priv, false); | ||
288 | break; | ||
289 | default: | ||
290 | break; | ||
291 | } | ||
292 | priv->vif = NULL; | ||
293 | priv->mode = NL80211_IFTYPE_MONITOR; | ||
294 | memset(priv->mac_addr, 0, ETH_ALEN); | ||
295 | memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo)); | ||
296 | cw1200_free_keys(priv); | ||
297 | cw1200_setup_mac(priv); | ||
298 | priv->listening = false; | ||
299 | priv->join_status = CW1200_JOIN_STATUS_PASSIVE; | ||
300 | if (!__cw1200_flush(priv, true)) | ||
301 | wsm_unlock_tx(priv); | ||
302 | |||
303 | mutex_unlock(&priv->conf_mutex); | ||
304 | } | ||
305 | |||
306 | int cw1200_change_interface(struct ieee80211_hw *dev, | ||
307 | struct ieee80211_vif *vif, | ||
308 | enum nl80211_iftype new_type, | ||
309 | bool p2p) | ||
310 | { | ||
311 | int ret = 0; | ||
312 | pr_debug("change_interface new: %d (%d), old: %d (%d)\n", new_type, | ||
313 | p2p, vif->type, vif->p2p); | ||
314 | |||
315 | if (new_type != vif->type || vif->p2p != p2p) { | ||
316 | cw1200_remove_interface(dev, vif); | ||
317 | vif->type = new_type; | ||
318 | vif->p2p = p2p; | ||
319 | ret = cw1200_add_interface(dev, vif); | ||
320 | } | ||
321 | |||
322 | return ret; | ||
323 | } | ||
324 | |||
325 | int cw1200_config(struct ieee80211_hw *dev, u32 changed) | ||
326 | { | ||
327 | int ret = 0; | ||
328 | struct cw1200_common *priv = dev->priv; | ||
329 | struct ieee80211_conf *conf = &dev->conf; | ||
330 | |||
331 | pr_debug("CONFIG CHANGED: %08x\n", changed); | ||
332 | |||
333 | down(&priv->scan.lock); | ||
334 | mutex_lock(&priv->conf_mutex); | ||
335 | /* TODO: IEEE80211_CONF_CHANGE_QOS */ | ||
336 | /* TODO: IEEE80211_CONF_CHANGE_LISTEN_INTERVAL */ | ||
337 | |||
338 | if (changed & IEEE80211_CONF_CHANGE_POWER) { | ||
339 | priv->output_power = conf->power_level; | ||
340 | pr_debug("[STA] TX power: %d\n", priv->output_power); | ||
341 | wsm_set_output_power(priv, priv->output_power * 10); | ||
342 | } | ||
343 | |||
344 | if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) && | ||
345 | (priv->channel != conf->chandef.chan)) { | ||
346 | struct ieee80211_channel *ch = conf->chandef.chan; | ||
347 | struct wsm_switch_channel channel = { | ||
348 | .channel_number = ch->hw_value, | ||
349 | }; | ||
350 | pr_debug("[STA] Freq %d (wsm ch: %d).\n", | ||
351 | ch->center_freq, ch->hw_value); | ||
352 | |||
353 | /* __cw1200_flush() implicitly locks tx, if successful */ | ||
354 | if (!__cw1200_flush(priv, false)) { | ||
355 | if (!wsm_switch_channel(priv, &channel)) { | ||
356 | ret = wait_event_timeout(priv->channel_switch_done, | ||
357 | !priv->channel_switch_in_progress, | ||
358 | 3 * HZ); | ||
359 | if (ret) { | ||
360 | /* Already unlocks if successful */ | ||
361 | priv->channel = ch; | ||
362 | ret = 0; | ||
363 | } else { | ||
364 | ret = -ETIMEDOUT; | ||
365 | } | ||
366 | } else { | ||
367 | /* Unlock if switch channel fails */ | ||
368 | wsm_unlock_tx(priv); | ||
369 | } | ||
370 | } | ||
371 | } | ||
372 | |||
373 | if (changed & IEEE80211_CONF_CHANGE_PS) { | ||
374 | if (!(conf->flags & IEEE80211_CONF_PS)) | ||
375 | priv->powersave_mode.mode = WSM_PSM_ACTIVE; | ||
376 | else if (conf->dynamic_ps_timeout <= 0) | ||
377 | priv->powersave_mode.mode = WSM_PSM_PS; | ||
378 | else | ||
379 | priv->powersave_mode.mode = WSM_PSM_FAST_PS; | ||
380 | |||
381 | /* Firmware requires that value for this 1-byte field must | ||
382 | * be specified in units of 500us. Values above the 128ms | ||
383 | * threshold are not supported. | ||
384 | */ | ||
385 | if (conf->dynamic_ps_timeout >= 0x80) | ||
386 | priv->powersave_mode.fast_psm_idle_period = 0xFF; | ||
387 | else | ||
388 | priv->powersave_mode.fast_psm_idle_period = | ||
389 | conf->dynamic_ps_timeout << 1; | ||
390 | |||
391 | if (priv->join_status == CW1200_JOIN_STATUS_STA && | ||
392 | priv->bss_params.aid) | ||
393 | cw1200_set_pm(priv, &priv->powersave_mode); | ||
394 | } | ||
395 | |||
396 | if (changed & IEEE80211_CONF_CHANGE_MONITOR) { | ||
397 | /* TBD: It looks like it's transparent | ||
398 | * there's a monitor interface present -- use this | ||
399 | * to determine for example whether to calculate | ||
400 | * timestamps for packets or not, do not use instead | ||
401 | * of filter flags! | ||
402 | */ | ||
403 | } | ||
404 | |||
405 | if (changed & IEEE80211_CONF_CHANGE_IDLE) { | ||
406 | struct wsm_operational_mode mode = { | ||
407 | .power_mode = cw1200_power_mode, | ||
408 | .disable_more_flag_usage = true, | ||
409 | }; | ||
410 | |||
411 | wsm_lock_tx(priv); | ||
412 | /* Disable p2p-dev mode forced by TX request */ | ||
413 | if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) && | ||
414 | (conf->flags & IEEE80211_CONF_IDLE) && | ||
415 | !priv->listening) { | ||
416 | cw1200_disable_listening(priv); | ||
417 | priv->join_status = CW1200_JOIN_STATUS_PASSIVE; | ||
418 | } | ||
419 | wsm_set_operational_mode(priv, &mode); | ||
420 | wsm_unlock_tx(priv); | ||
421 | } | ||
422 | |||
423 | if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { | ||
424 | pr_debug("[STA] Retry limits: %d (long), %d (short).\n", | ||
425 | conf->long_frame_max_tx_count, | ||
426 | conf->short_frame_max_tx_count); | ||
427 | spin_lock_bh(&priv->tx_policy_cache.lock); | ||
428 | priv->long_frame_max_tx_count = conf->long_frame_max_tx_count; | ||
429 | priv->short_frame_max_tx_count = | ||
430 | (conf->short_frame_max_tx_count < 0x0F) ? | ||
431 | conf->short_frame_max_tx_count : 0x0F; | ||
432 | priv->hw->max_rate_tries = priv->short_frame_max_tx_count; | ||
433 | spin_unlock_bh(&priv->tx_policy_cache.lock); | ||
434 | } | ||
435 | mutex_unlock(&priv->conf_mutex); | ||
436 | up(&priv->scan.lock); | ||
437 | return ret; | ||
438 | } | ||
439 | |||
440 | void cw1200_update_filtering(struct cw1200_common *priv) | ||
441 | { | ||
442 | int ret; | ||
443 | bool bssid_filtering = !priv->rx_filter.bssid; | ||
444 | bool is_p2p = priv->vif && priv->vif->p2p; | ||
445 | bool is_sta = priv->vif && NL80211_IFTYPE_STATION == priv->vif->type; | ||
446 | |||
447 | static struct wsm_beacon_filter_control bf_ctrl; | ||
448 | static struct wsm_mib_beacon_filter_table bf_tbl = { | ||
449 | .entry[0].ie_id = WLAN_EID_VENDOR_SPECIFIC, | ||
450 | .entry[0].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | | ||
451 | WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | | ||
452 | WSM_BEACON_FILTER_IE_HAS_APPEARED, | ||
453 | .entry[0].oui[0] = 0x50, | ||
454 | .entry[0].oui[1] = 0x6F, | ||
455 | .entry[0].oui[2] = 0x9A, | ||
456 | .entry[1].ie_id = WLAN_EID_HT_OPERATION, | ||
457 | .entry[1].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | | ||
458 | WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | | ||
459 | WSM_BEACON_FILTER_IE_HAS_APPEARED, | ||
460 | .entry[2].ie_id = WLAN_EID_ERP_INFO, | ||
461 | .entry[2].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | | ||
462 | WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | | ||
463 | WSM_BEACON_FILTER_IE_HAS_APPEARED, | ||
464 | }; | ||
465 | |||
466 | if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) | ||
467 | return; | ||
468 | else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) | ||
469 | bssid_filtering = false; | ||
470 | |||
471 | if (priv->disable_beacon_filter) { | ||
472 | bf_ctrl.enabled = 0; | ||
473 | bf_ctrl.bcn_count = 1; | ||
474 | bf_tbl.num = __cpu_to_le32(0); | ||
475 | } else if (is_p2p || !is_sta) { | ||
476 | bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE | | ||
477 | WSM_BEACON_FILTER_AUTO_ERP; | ||
478 | bf_ctrl.bcn_count = 0; | ||
479 | bf_tbl.num = __cpu_to_le32(2); | ||
480 | } else { | ||
481 | bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE; | ||
482 | bf_ctrl.bcn_count = 0; | ||
483 | bf_tbl.num = __cpu_to_le32(3); | ||
484 | } | ||
485 | |||
486 | /* | ||
487 | * When acting as p2p client being connected to p2p GO, in order to | ||
488 | * receive frames from a different p2p device, turn off bssid filter. | ||
489 | * | ||
490 | * WARNING: FW dependency! | ||
491 | * This can only be used with FW WSM371 and its successors. | ||
492 | * In that FW version even with bssid filter turned off, | ||
493 | * device will block most of the unwanted frames. | ||
494 | */ | ||
495 | if (is_p2p) | ||
496 | bssid_filtering = false; | ||
497 | |||
498 | ret = wsm_set_rx_filter(priv, &priv->rx_filter); | ||
499 | if (!ret) | ||
500 | ret = wsm_set_beacon_filter_table(priv, &bf_tbl); | ||
501 | if (!ret) | ||
502 | ret = wsm_beacon_filter_control(priv, &bf_ctrl); | ||
503 | if (!ret) | ||
504 | ret = wsm_set_bssid_filtering(priv, bssid_filtering); | ||
505 | if (!ret) | ||
506 | ret = wsm_set_multicast_filter(priv, &priv->multicast_filter); | ||
507 | if (ret) | ||
508 | wiphy_err(priv->hw->wiphy, | ||
509 | "Update filtering failed: %d.\n", ret); | ||
510 | return; | ||
511 | } | ||
512 | |||
513 | void cw1200_update_filtering_work(struct work_struct *work) | ||
514 | { | ||
515 | struct cw1200_common *priv = | ||
516 | container_of(work, struct cw1200_common, | ||
517 | update_filtering_work); | ||
518 | |||
519 | cw1200_update_filtering(priv); | ||
520 | } | ||
521 | |||
522 | void cw1200_set_beacon_wakeup_period_work(struct work_struct *work) | ||
523 | { | ||
524 | struct cw1200_common *priv = | ||
525 | container_of(work, struct cw1200_common, | ||
526 | set_beacon_wakeup_period_work); | ||
527 | |||
528 | wsm_set_beacon_wakeup_period(priv, | ||
529 | priv->beacon_int * priv->join_dtim_period > | ||
530 | MAX_BEACON_SKIP_TIME_MS ? 1 : | ||
531 | priv->join_dtim_period, 0); | ||
532 | } | ||
533 | |||
534 | u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, | ||
535 | struct netdev_hw_addr_list *mc_list) | ||
536 | { | ||
537 | static u8 broadcast_ipv6[ETH_ALEN] = { | ||
538 | 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 | ||
539 | }; | ||
540 | static u8 broadcast_ipv4[ETH_ALEN] = { | ||
541 | 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01 | ||
542 | }; | ||
543 | struct cw1200_common *priv = hw->priv; | ||
544 | struct netdev_hw_addr *ha; | ||
545 | int count = 0; | ||
546 | |||
547 | /* Disable multicast filtering */ | ||
548 | priv->has_multicast_subscription = false; | ||
549 | memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter)); | ||
550 | |||
551 | if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES) | ||
552 | return 0; | ||
553 | |||
554 | /* Enable if requested */ | ||
555 | netdev_hw_addr_list_for_each(ha, mc_list) { | ||
556 | pr_debug("[STA] multicast: %pM\n", ha->addr); | ||
557 | memcpy(&priv->multicast_filter.macaddrs[count], | ||
558 | ha->addr, ETH_ALEN); | ||
559 | if (memcmp(ha->addr, broadcast_ipv4, ETH_ALEN) && | ||
560 | memcmp(ha->addr, broadcast_ipv6, ETH_ALEN)) | ||
561 | priv->has_multicast_subscription = true; | ||
562 | count++; | ||
563 | } | ||
564 | |||
565 | if (count) { | ||
566 | priv->multicast_filter.enable = __cpu_to_le32(1); | ||
567 | priv->multicast_filter.num_addrs = __cpu_to_le32(count); | ||
568 | } | ||
569 | |||
570 | return netdev_hw_addr_list_count(mc_list); | ||
571 | } | ||
572 | |||
573 | void cw1200_configure_filter(struct ieee80211_hw *dev, | ||
574 | unsigned int changed_flags, | ||
575 | unsigned int *total_flags, | ||
576 | u64 multicast) | ||
577 | { | ||
578 | struct cw1200_common *priv = dev->priv; | ||
579 | bool listening = !!(*total_flags & | ||
580 | (FIF_PROMISC_IN_BSS | | ||
581 | FIF_OTHER_BSS | | ||
582 | FIF_BCN_PRBRESP_PROMISC | | ||
583 | FIF_PROBE_REQ)); | ||
584 | |||
585 | *total_flags &= FIF_PROMISC_IN_BSS | | ||
586 | FIF_OTHER_BSS | | ||
587 | FIF_FCSFAIL | | ||
588 | FIF_BCN_PRBRESP_PROMISC | | ||
589 | FIF_PROBE_REQ; | ||
590 | |||
591 | down(&priv->scan.lock); | ||
592 | mutex_lock(&priv->conf_mutex); | ||
593 | |||
594 | priv->rx_filter.promiscuous = (*total_flags & FIF_PROMISC_IN_BSS) | ||
595 | ? 1 : 0; | ||
596 | priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS | | ||
597 | FIF_PROBE_REQ)) ? 1 : 0; | ||
598 | priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0; | ||
599 | priv->disable_beacon_filter = !(*total_flags & | ||
600 | (FIF_BCN_PRBRESP_PROMISC | | ||
601 | FIF_PROMISC_IN_BSS | | ||
602 | FIF_PROBE_REQ)); | ||
603 | if (priv->listening != listening) { | ||
604 | priv->listening = listening; | ||
605 | wsm_lock_tx(priv); | ||
606 | cw1200_update_listening(priv, listening); | ||
607 | wsm_unlock_tx(priv); | ||
608 | } | ||
609 | cw1200_update_filtering(priv); | ||
610 | mutex_unlock(&priv->conf_mutex); | ||
611 | up(&priv->scan.lock); | ||
612 | } | ||
613 | |||
614 | int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, | ||
615 | u16 queue, const struct ieee80211_tx_queue_params *params) | ||
616 | { | ||
617 | struct cw1200_common *priv = dev->priv; | ||
618 | int ret = 0; | ||
619 | /* To prevent re-applying PM request OID again and again*/ | ||
620 | bool old_uapsd_flags; | ||
621 | |||
622 | mutex_lock(&priv->conf_mutex); | ||
623 | |||
624 | if (queue < dev->queues) { | ||
625 | old_uapsd_flags = priv->uapsd_info.uapsd_flags; | ||
626 | |||
627 | WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0); | ||
628 | ret = wsm_set_tx_queue_params(priv, | ||
629 | &priv->tx_queue_params.params[queue], queue); | ||
630 | if (ret) { | ||
631 | ret = -EINVAL; | ||
632 | goto out; | ||
633 | } | ||
634 | |||
635 | WSM_EDCA_SET(&priv->edca, queue, params->aifs, | ||
636 | params->cw_min, params->cw_max, | ||
637 | params->txop, 0xc8, | ||
638 | params->uapsd); | ||
639 | ret = wsm_set_edca_params(priv, &priv->edca); | ||
640 | if (ret) { | ||
641 | ret = -EINVAL; | ||
642 | goto out; | ||
643 | } | ||
644 | |||
645 | if (priv->mode == NL80211_IFTYPE_STATION) { | ||
646 | ret = cw1200_set_uapsd_param(priv, &priv->edca); | ||
647 | if (!ret && priv->setbssparams_done && | ||
648 | (priv->join_status == CW1200_JOIN_STATUS_STA) && | ||
649 | (old_uapsd_flags != priv->uapsd_info.uapsd_flags)) | ||
650 | ret = cw1200_set_pm(priv, &priv->powersave_mode); | ||
651 | } | ||
652 | } else { | ||
653 | ret = -EINVAL; | ||
654 | } | ||
655 | |||
656 | out: | ||
657 | mutex_unlock(&priv->conf_mutex); | ||
658 | return ret; | ||
659 | } | ||
660 | |||
661 | int cw1200_get_stats(struct ieee80211_hw *dev, | ||
662 | struct ieee80211_low_level_stats *stats) | ||
663 | { | ||
664 | struct cw1200_common *priv = dev->priv; | ||
665 | |||
666 | memcpy(stats, &priv->stats, sizeof(*stats)); | ||
667 | return 0; | ||
668 | } | ||
669 | |||
670 | int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) | ||
671 | { | ||
672 | struct wsm_set_pm pm = *arg; | ||
673 | |||
674 | if (priv->uapsd_info.uapsd_flags != 0) | ||
675 | pm.mode &= ~WSM_PSM_FAST_PS_FLAG; | ||
676 | |||
677 | if (memcmp(&pm, &priv->firmware_ps_mode, | ||
678 | sizeof(struct wsm_set_pm))) { | ||
679 | priv->firmware_ps_mode = pm; | ||
680 | return wsm_set_pm(priv, &pm); | ||
681 | } else { | ||
682 | return 0; | ||
683 | } | ||
684 | } | ||
685 | |||
686 | int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, | ||
687 | struct ieee80211_vif *vif, struct ieee80211_sta *sta, | ||
688 | struct ieee80211_key_conf *key) | ||
689 | { | ||
690 | int ret = -EOPNOTSUPP; | ||
691 | struct cw1200_common *priv = dev->priv; | ||
692 | struct ieee80211_key_seq seq; | ||
693 | |||
694 | mutex_lock(&priv->conf_mutex); | ||
695 | |||
696 | if (cmd == SET_KEY) { | ||
697 | u8 *peer_addr = NULL; | ||
698 | int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? | ||
699 | 1 : 0; | ||
700 | int idx = cw1200_alloc_key(priv); | ||
701 | struct wsm_add_key *wsm_key = &priv->keys[idx]; | ||
702 | |||
703 | if (idx < 0) { | ||
704 | ret = -EINVAL; | ||
705 | goto finally; | ||
706 | } | ||
707 | |||
708 | if (sta) | ||
709 | peer_addr = sta->addr; | ||
710 | |||
711 | key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; | ||
712 | |||
713 | switch (key->cipher) { | ||
714 | case WLAN_CIPHER_SUITE_WEP40: | ||
715 | case WLAN_CIPHER_SUITE_WEP104: | ||
716 | if (key->keylen > 16) { | ||
717 | cw1200_free_key(priv, idx); | ||
718 | ret = -EINVAL; | ||
719 | goto finally; | ||
720 | } | ||
721 | |||
722 | if (pairwise) { | ||
723 | wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE; | ||
724 | memcpy(wsm_key->wep_pairwise.peer, | ||
725 | peer_addr, ETH_ALEN); | ||
726 | memcpy(wsm_key->wep_pairwise.keydata, | ||
727 | &key->key[0], key->keylen); | ||
728 | wsm_key->wep_pairwise.keylen = key->keylen; | ||
729 | } else { | ||
730 | wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT; | ||
731 | memcpy(wsm_key->wep_group.keydata, | ||
732 | &key->key[0], key->keylen); | ||
733 | wsm_key->wep_group.keylen = key->keylen; | ||
734 | wsm_key->wep_group.keyid = key->keyidx; | ||
735 | } | ||
736 | break; | ||
737 | case WLAN_CIPHER_SUITE_TKIP: | ||
738 | ieee80211_get_key_rx_seq(key, 0, &seq); | ||
739 | if (pairwise) { | ||
740 | wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE; | ||
741 | memcpy(wsm_key->tkip_pairwise.peer, | ||
742 | peer_addr, ETH_ALEN); | ||
743 | memcpy(wsm_key->tkip_pairwise.keydata, | ||
744 | &key->key[0], 16); | ||
745 | memcpy(wsm_key->tkip_pairwise.tx_mic_key, | ||
746 | &key->key[16], 8); | ||
747 | memcpy(wsm_key->tkip_pairwise.rx_mic_key, | ||
748 | &key->key[24], 8); | ||
749 | } else { | ||
750 | size_t mic_offset = | ||
751 | (priv->mode == NL80211_IFTYPE_AP) ? | ||
752 | 16 : 24; | ||
753 | wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP; | ||
754 | memcpy(wsm_key->tkip_group.keydata, | ||
755 | &key->key[0], 16); | ||
756 | memcpy(wsm_key->tkip_group.rx_mic_key, | ||
757 | &key->key[mic_offset], 8); | ||
758 | |||
759 | wsm_key->tkip_group.rx_seqnum[0] = seq.tkip.iv16 & 0xff; | ||
760 | wsm_key->tkip_group.rx_seqnum[1] = (seq.tkip.iv16 >> 8) & 0xff; | ||
761 | wsm_key->tkip_group.rx_seqnum[2] = seq.tkip.iv32 & 0xff; | ||
762 | wsm_key->tkip_group.rx_seqnum[3] = (seq.tkip.iv32 >> 8) & 0xff; | ||
763 | wsm_key->tkip_group.rx_seqnum[4] = (seq.tkip.iv32 >> 16) & 0xff; | ||
764 | wsm_key->tkip_group.rx_seqnum[5] = (seq.tkip.iv32 >> 24) & 0xff; | ||
765 | wsm_key->tkip_group.rx_seqnum[6] = 0; | ||
766 | wsm_key->tkip_group.rx_seqnum[7] = 0; | ||
767 | |||
768 | wsm_key->tkip_group.keyid = key->keyidx; | ||
769 | } | ||
770 | break; | ||
771 | case WLAN_CIPHER_SUITE_CCMP: | ||
772 | ieee80211_get_key_rx_seq(key, 0, &seq); | ||
773 | if (pairwise) { | ||
774 | wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE; | ||
775 | memcpy(wsm_key->aes_pairwise.peer, | ||
776 | peer_addr, ETH_ALEN); | ||
777 | memcpy(wsm_key->aes_pairwise.keydata, | ||
778 | &key->key[0], 16); | ||
779 | } else { | ||
780 | wsm_key->type = WSM_KEY_TYPE_AES_GROUP; | ||
781 | memcpy(wsm_key->aes_group.keydata, | ||
782 | &key->key[0], 16); | ||
783 | |||
784 | wsm_key->aes_group.rx_seqnum[0] = seq.ccmp.pn[5]; | ||
785 | wsm_key->aes_group.rx_seqnum[1] = seq.ccmp.pn[4]; | ||
786 | wsm_key->aes_group.rx_seqnum[2] = seq.ccmp.pn[3]; | ||
787 | wsm_key->aes_group.rx_seqnum[3] = seq.ccmp.pn[2]; | ||
788 | wsm_key->aes_group.rx_seqnum[4] = seq.ccmp.pn[1]; | ||
789 | wsm_key->aes_group.rx_seqnum[5] = seq.ccmp.pn[0]; | ||
790 | wsm_key->aes_group.rx_seqnum[6] = 0; | ||
791 | wsm_key->aes_group.rx_seqnum[7] = 0; | ||
792 | wsm_key->aes_group.keyid = key->keyidx; | ||
793 | } | ||
794 | break; | ||
795 | case WLAN_CIPHER_SUITE_SMS4: | ||
796 | if (pairwise) { | ||
797 | wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE; | ||
798 | memcpy(wsm_key->wapi_pairwise.peer, | ||
799 | peer_addr, ETH_ALEN); | ||
800 | memcpy(wsm_key->wapi_pairwise.keydata, | ||
801 | &key->key[0], 16); | ||
802 | memcpy(wsm_key->wapi_pairwise.mic_key, | ||
803 | &key->key[16], 16); | ||
804 | wsm_key->wapi_pairwise.keyid = key->keyidx; | ||
805 | } else { | ||
806 | wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP; | ||
807 | memcpy(wsm_key->wapi_group.keydata, | ||
808 | &key->key[0], 16); | ||
809 | memcpy(wsm_key->wapi_group.mic_key, | ||
810 | &key->key[16], 16); | ||
811 | wsm_key->wapi_group.keyid = key->keyidx; | ||
812 | } | ||
813 | break; | ||
814 | default: | ||
815 | pr_warn("Unhandled key type %d\n", key->cipher); | ||
816 | cw1200_free_key(priv, idx); | ||
817 | ret = -EOPNOTSUPP; | ||
818 | goto finally; | ||
819 | } | ||
820 | ret = wsm_add_key(priv, wsm_key); | ||
821 | if (!ret) | ||
822 | key->hw_key_idx = idx; | ||
823 | else | ||
824 | cw1200_free_key(priv, idx); | ||
825 | } else if (cmd == DISABLE_KEY) { | ||
826 | struct wsm_remove_key wsm_key = { | ||
827 | .index = key->hw_key_idx, | ||
828 | }; | ||
829 | |||
830 | if (wsm_key.index > WSM_KEY_MAX_INDEX) { | ||
831 | ret = -EINVAL; | ||
832 | goto finally; | ||
833 | } | ||
834 | |||
835 | cw1200_free_key(priv, wsm_key.index); | ||
836 | ret = wsm_remove_key(priv, &wsm_key); | ||
837 | } else { | ||
838 | pr_warn("Unhandled key command %d\n", cmd); | ||
839 | } | ||
840 | |||
841 | finally: | ||
842 | mutex_unlock(&priv->conf_mutex); | ||
843 | return ret; | ||
844 | } | ||
845 | |||
846 | void cw1200_wep_key_work(struct work_struct *work) | ||
847 | { | ||
848 | struct cw1200_common *priv = | ||
849 | container_of(work, struct cw1200_common, wep_key_work); | ||
850 | u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id); | ||
851 | struct cw1200_queue *queue = &priv->tx_queue[queue_id]; | ||
852 | __le32 wep_default_key_id = __cpu_to_le32( | ||
853 | priv->wep_default_key_id); | ||
854 | |||
855 | pr_debug("[STA] Setting default WEP key: %d\n", | ||
856 | priv->wep_default_key_id); | ||
857 | wsm_flush_tx(priv); | ||
858 | wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID, | ||
859 | &wep_default_key_id, sizeof(wep_default_key_id)); | ||
860 | cw1200_queue_requeue(queue, priv->pending_frame_id); | ||
861 | wsm_unlock_tx(priv); | ||
862 | } | ||
863 | |||
864 | int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value) | ||
865 | { | ||
866 | int ret = 0; | ||
867 | __le32 val32; | ||
868 | struct cw1200_common *priv = hw->priv; | ||
869 | |||
870 | if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) | ||
871 | return 0; | ||
872 | |||
873 | if (value != (u32) -1) | ||
874 | val32 = __cpu_to_le32(value); | ||
875 | else | ||
876 | val32 = 0; /* disabled */ | ||
877 | |||
878 | if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { | ||
879 | /* device is down, can _not_ set threshold */ | ||
880 | ret = -ENODEV; | ||
881 | goto out; | ||
882 | } | ||
883 | |||
884 | if (priv->rts_threshold == value) | ||
885 | goto out; | ||
886 | |||
887 | pr_debug("[STA] Setting RTS threshold: %d\n", | ||
888 | priv->rts_threshold); | ||
889 | |||
890 | /* mutex_lock(&priv->conf_mutex); */ | ||
891 | ret = wsm_write_mib(priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD, | ||
892 | &val32, sizeof(val32)); | ||
893 | if (!ret) | ||
894 | priv->rts_threshold = value; | ||
895 | /* mutex_unlock(&priv->conf_mutex); */ | ||
896 | |||
897 | out: | ||
898 | return ret; | ||
899 | } | ||
900 | |||
901 | /* If successful, LOCKS the TX queue! */ | ||
902 | static int __cw1200_flush(struct cw1200_common *priv, bool drop) | ||
903 | { | ||
904 | int i, ret; | ||
905 | |||
906 | for (;;) { | ||
907 | /* TODO: correct flush handling is required when dev_stop. | ||
908 | * Temporary workaround: 2s | ||
909 | */ | ||
910 | if (drop) { | ||
911 | for (i = 0; i < 4; ++i) | ||
912 | cw1200_queue_clear(&priv->tx_queue[i]); | ||
913 | } else { | ||
914 | ret = wait_event_timeout( | ||
915 | priv->tx_queue_stats.wait_link_id_empty, | ||
916 | cw1200_queue_stats_is_empty( | ||
917 | &priv->tx_queue_stats, -1), | ||
918 | 2 * HZ); | ||
919 | } | ||
920 | |||
921 | if (!drop && ret <= 0) { | ||
922 | ret = -ETIMEDOUT; | ||
923 | break; | ||
924 | } else { | ||
925 | ret = 0; | ||
926 | } | ||
927 | |||
928 | wsm_lock_tx(priv); | ||
929 | if (!cw1200_queue_stats_is_empty(&priv->tx_queue_stats, -1)) { | ||
930 | /* Highly unlikely: WSM requeued frames. */ | ||
931 | wsm_unlock_tx(priv); | ||
932 | continue; | ||
933 | } | ||
934 | break; | ||
935 | } | ||
936 | return ret; | ||
937 | } | ||
938 | |||
939 | void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop) | ||
940 | { | ||
941 | struct cw1200_common *priv = hw->priv; | ||
942 | |||
943 | switch (priv->mode) { | ||
944 | case NL80211_IFTYPE_MONITOR: | ||
945 | drop = true; | ||
946 | break; | ||
947 | case NL80211_IFTYPE_AP: | ||
948 | if (!priv->enable_beacon) | ||
949 | drop = true; | ||
950 | break; | ||
951 | } | ||
952 | |||
953 | if (!__cw1200_flush(priv, drop)) | ||
954 | wsm_unlock_tx(priv); | ||
955 | |||
956 | return; | ||
957 | } | ||
958 | |||
959 | /* ******************************************************************** */ | ||
960 | /* WSM callbacks */ | ||
961 | |||
962 | void cw1200_free_event_queue(struct cw1200_common *priv) | ||
963 | { | ||
964 | LIST_HEAD(list); | ||
965 | |||
966 | spin_lock(&priv->event_queue_lock); | ||
967 | list_splice_init(&priv->event_queue, &list); | ||
968 | spin_unlock(&priv->event_queue_lock); | ||
969 | |||
970 | __cw1200_free_event_queue(&list); | ||
971 | } | ||
972 | |||
973 | void cw1200_event_handler(struct work_struct *work) | ||
974 | { | ||
975 | struct cw1200_common *priv = | ||
976 | container_of(work, struct cw1200_common, event_handler); | ||
977 | struct cw1200_wsm_event *event; | ||
978 | LIST_HEAD(list); | ||
979 | |||
980 | spin_lock(&priv->event_queue_lock); | ||
981 | list_splice_init(&priv->event_queue, &list); | ||
982 | spin_unlock(&priv->event_queue_lock); | ||
983 | |||
984 | list_for_each_entry(event, &list, link) { | ||
985 | switch (event->evt.id) { | ||
986 | case WSM_EVENT_ERROR: | ||
987 | pr_err("Unhandled WSM Error from LMAC\n"); | ||
988 | break; | ||
989 | case WSM_EVENT_BSS_LOST: | ||
990 | pr_debug("[CQM] BSS lost.\n"); | ||
991 | cancel_work_sync(&priv->unjoin_work); | ||
992 | if (!down_trylock(&priv->scan.lock)) { | ||
993 | cw1200_cqm_bssloss_sm(priv, 1, 0, 0); | ||
994 | up(&priv->scan.lock); | ||
995 | } else { | ||
996 | /* Scan is in progress. Delay reporting. | ||
997 | * Scan complete will trigger bss_loss_work | ||
998 | */ | ||
999 | priv->delayed_link_loss = 1; | ||
1000 | /* Also start a watchdog. */ | ||
1001 | queue_delayed_work(priv->workqueue, | ||
1002 | &priv->bss_loss_work, 5*HZ); | ||
1003 | } | ||
1004 | break; | ||
1005 | case WSM_EVENT_BSS_REGAINED: | ||
1006 | pr_debug("[CQM] BSS regained.\n"); | ||
1007 | cw1200_cqm_bssloss_sm(priv, 0, 0, 0); | ||
1008 | cancel_work_sync(&priv->unjoin_work); | ||
1009 | break; | ||
1010 | case WSM_EVENT_RADAR_DETECTED: | ||
1011 | wiphy_info(priv->hw->wiphy, "radar pulse detected\n"); | ||
1012 | break; | ||
1013 | case WSM_EVENT_RCPI_RSSI: | ||
1014 | { | ||
1015 | /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 | ||
1016 | * RSSI = RCPI / 2 - 110 | ||
1017 | */ | ||
1018 | int rcpiRssi = (int)(event->evt.data & 0xFF); | ||
1019 | int cqm_evt; | ||
1020 | if (priv->cqm_use_rssi) | ||
1021 | rcpiRssi = (s8)rcpiRssi; | ||
1022 | else | ||
1023 | rcpiRssi = rcpiRssi / 2 - 110; | ||
1024 | |||
1025 | cqm_evt = (rcpiRssi <= priv->cqm_rssi_thold) ? | ||
1026 | NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW : | ||
1027 | NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; | ||
1028 | pr_debug("[CQM] RSSI event: %d.\n", rcpiRssi); | ||
1029 | ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, | ||
1030 | GFP_KERNEL); | ||
1031 | break; | ||
1032 | } | ||
1033 | case WSM_EVENT_BT_INACTIVE: | ||
1034 | pr_warn("Unhandled BT INACTIVE from LMAC\n"); | ||
1035 | break; | ||
1036 | case WSM_EVENT_BT_ACTIVE: | ||
1037 | pr_warn("Unhandled BT ACTIVE from LMAC\n"); | ||
1038 | break; | ||
1039 | } | ||
1040 | } | ||
1041 | __cw1200_free_event_queue(&list); | ||
1042 | } | ||
1043 | |||
1044 | void cw1200_bss_loss_work(struct work_struct *work) | ||
1045 | { | ||
1046 | struct cw1200_common *priv = | ||
1047 | container_of(work, struct cw1200_common, bss_loss_work.work); | ||
1048 | |||
1049 | pr_debug("[CQM] Reporting connection loss.\n"); | ||
1050 | wsm_lock_tx(priv); | ||
1051 | if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) | ||
1052 | wsm_unlock_tx(priv); | ||
1053 | } | ||
1054 | |||
1055 | void cw1200_bss_params_work(struct work_struct *work) | ||
1056 | { | ||
1057 | struct cw1200_common *priv = | ||
1058 | container_of(work, struct cw1200_common, bss_params_work); | ||
1059 | mutex_lock(&priv->conf_mutex); | ||
1060 | |||
1061 | priv->bss_params.reset_beacon_loss = 1; | ||
1062 | wsm_set_bss_params(priv, &priv->bss_params); | ||
1063 | priv->bss_params.reset_beacon_loss = 0; | ||
1064 | |||
1065 | mutex_unlock(&priv->conf_mutex); | ||
1066 | } | ||
1067 | |||
1068 | /* ******************************************************************** */ | ||
1069 | /* Internal API */ | ||
1070 | |||
1071 | /* | ||
1072 | * This function is called to Parse the SDD file | ||
1073 | * to extract listen_interval and PTA related information | ||
1074 | * sdd is a TLV: u8 id, u8 len, u8 data[] | ||
1075 | */ | ||
1076 | static int cw1200_parse_sdd_file(struct cw1200_common *priv) | ||
1077 | { | ||
1078 | const u8 *p = priv->sdd->data; | ||
1079 | int ret = 0; | ||
1080 | |||
1081 | while (p + 2 <= priv->sdd->data + priv->sdd->size) { | ||
1082 | if (p + p[1] + 2 > priv->sdd->data + priv->sdd->size) { | ||
1083 | pr_warn("Malformed sdd structure\n"); | ||
1084 | return -1; | ||
1085 | } | ||
1086 | switch (p[0]) { | ||
1087 | case SDD_PTA_CFG_ELT_ID: { | ||
1088 | u16 v; | ||
1089 | if (p[1] < 4) { | ||
1090 | pr_warn("SDD_PTA_CFG_ELT_ID malformed\n"); | ||
1091 | ret = -1; | ||
1092 | break; | ||
1093 | } | ||
1094 | v = le16_to_cpu(*((u16 *)(p + 2))); | ||
1095 | if (!v) /* non-zero means this is enabled */ | ||
1096 | break; | ||
1097 | |||
1098 | v = le16_to_cpu(*((u16 *)(p + 4))); | ||
1099 | priv->conf_listen_interval = (v >> 7) & 0x1F; | ||
1100 | pr_debug("PTA found; Listen Interval %d\n", | ||
1101 | priv->conf_listen_interval); | ||
1102 | break; | ||
1103 | } | ||
1104 | case SDD_REFERENCE_FREQUENCY_ELT_ID: { | ||
1105 | u16 clk = le16_to_cpu(*((u16 *)(p + 2))); | ||
1106 | if (clk != priv->hw_refclk) | ||
1107 | pr_warn("SDD file doesn't match configured refclk (%d vs %d)\n", | ||
1108 | clk, priv->hw_refclk); | ||
1109 | break; | ||
1110 | } | ||
1111 | default: | ||
1112 | break; | ||
1113 | } | ||
1114 | p += p[1] + 2; | ||
1115 | } | ||
1116 | |||
1117 | if (!priv->bt_present) { | ||
1118 | pr_debug("PTA element NOT found.\n"); | ||
1119 | priv->conf_listen_interval = 0; | ||
1120 | } | ||
1121 | return ret; | ||
1122 | } | ||
1123 | |||
1124 | int cw1200_setup_mac(struct cw1200_common *priv) | ||
1125 | { | ||
1126 | int ret = 0; | ||
1127 | |||
1128 | /* NOTE: There is a bug in FW: it reports signal | ||
1129 | * as RSSI if RSSI subscription is enabled. | ||
1130 | * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. | ||
1131 | * | ||
1132 | * NOTE2: RSSI based reports have been switched to RCPI, since | ||
1133 | * FW has a bug and RSSI reported values are not stable, | ||
1134 | * what can leads to signal level oscilations in user-end applications | ||
1135 | */ | ||
1136 | struct wsm_rcpi_rssi_threshold threshold = { | ||
1137 | .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE | | ||
1138 | WSM_RCPI_RSSI_DONT_USE_UPPER | | ||
1139 | WSM_RCPI_RSSI_DONT_USE_LOWER, | ||
1140 | .rollingAverageCount = 16, | ||
1141 | }; | ||
1142 | |||
1143 | struct wsm_configuration cfg = { | ||
1144 | .dot11StationId = &priv->mac_addr[0], | ||
1145 | }; | ||
1146 | |||
1147 | /* Remember the decission here to make sure, we will handle | ||
1148 | * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS | ||
1149 | */ | ||
1150 | if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI) | ||
1151 | priv->cqm_use_rssi = true; | ||
1152 | |||
1153 | if (!priv->sdd) { | ||
1154 | ret = request_firmware(&priv->sdd, priv->sdd_path, priv->pdev); | ||
1155 | if (ret) { | ||
1156 | pr_err("Can't load sdd file %s.\n", priv->sdd_path); | ||
1157 | return ret; | ||
1158 | } | ||
1159 | cw1200_parse_sdd_file(priv); | ||
1160 | } | ||
1161 | |||
1162 | cfg.dpdData = priv->sdd->data; | ||
1163 | cfg.dpdData_size = priv->sdd->size; | ||
1164 | ret = wsm_configuration(priv, &cfg); | ||
1165 | if (ret) | ||
1166 | return ret; | ||
1167 | |||
1168 | /* Configure RSSI/SCPI reporting as RSSI. */ | ||
1169 | wsm_set_rcpi_rssi_threshold(priv, &threshold); | ||
1170 | |||
1171 | return 0; | ||
1172 | } | ||
1173 | |||
1174 | static void cw1200_join_complete(struct cw1200_common *priv) | ||
1175 | { | ||
1176 | pr_debug("[STA] Join complete (%d)\n", priv->join_complete_status); | ||
1177 | |||
1178 | priv->join_pending = false; | ||
1179 | if (priv->join_complete_status) { | ||
1180 | priv->join_status = CW1200_JOIN_STATUS_PASSIVE; | ||
1181 | cw1200_update_listening(priv, priv->listening); | ||
1182 | cw1200_do_unjoin(priv); | ||
1183 | ieee80211_connection_loss(priv->vif); | ||
1184 | } else { | ||
1185 | if (priv->mode == NL80211_IFTYPE_ADHOC) | ||
1186 | priv->join_status = CW1200_JOIN_STATUS_IBSS; | ||
1187 | else | ||
1188 | priv->join_status = CW1200_JOIN_STATUS_PRE_STA; | ||
1189 | } | ||
1190 | wsm_unlock_tx(priv); /* Clearing the lock held before do_join() */ | ||
1191 | } | ||
1192 | |||
1193 | void cw1200_join_complete_work(struct work_struct *work) | ||
1194 | { | ||
1195 | struct cw1200_common *priv = | ||
1196 | container_of(work, struct cw1200_common, join_complete_work); | ||
1197 | mutex_lock(&priv->conf_mutex); | ||
1198 | cw1200_join_complete(priv); | ||
1199 | mutex_unlock(&priv->conf_mutex); | ||
1200 | } | ||
1201 | |||
1202 | void cw1200_join_complete_cb(struct cw1200_common *priv, | ||
1203 | struct wsm_join_complete *arg) | ||
1204 | { | ||
1205 | pr_debug("[STA] cw1200_join_complete_cb called, status=%d.\n", | ||
1206 | arg->status); | ||
1207 | |||
1208 | if (cancel_delayed_work(&priv->join_timeout)) { | ||
1209 | priv->join_complete_status = arg->status; | ||
1210 | queue_work(priv->workqueue, &priv->join_complete_work); | ||
1211 | } | ||
1212 | } | ||
1213 | |||
1214 | /* MUST be called with tx_lock held! It will be unlocked for us. */ | ||
1215 | static void cw1200_do_join(struct cw1200_common *priv) | ||
1216 | { | ||
1217 | const u8 *bssid; | ||
1218 | struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; | ||
1219 | struct cfg80211_bss *bss = NULL; | ||
1220 | struct wsm_protected_mgmt_policy mgmt_policy; | ||
1221 | struct wsm_join join = { | ||
1222 | .mode = conf->ibss_joined ? | ||
1223 | WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS, | ||
1224 | .preamble_type = WSM_JOIN_PREAMBLE_LONG, | ||
1225 | .probe_for_join = 1, | ||
1226 | .atim_window = 0, | ||
1227 | .basic_rate_set = cw1200_rate_mask_to_wsm(priv, | ||
1228 | conf->basic_rates), | ||
1229 | }; | ||
1230 | if (delayed_work_pending(&priv->join_timeout)) { | ||
1231 | pr_warn("[STA] - Join request already pending, skipping..\n"); | ||
1232 | wsm_unlock_tx(priv); | ||
1233 | return; | ||
1234 | } | ||
1235 | |||
1236 | if (priv->join_status) | ||
1237 | cw1200_do_unjoin(priv); | ||
1238 | |||
1239 | bssid = priv->vif->bss_conf.bssid; | ||
1240 | |||
1241 | bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, | ||
1242 | bssid, NULL, 0, 0, 0); | ||
1243 | |||
1244 | if (!bss && !conf->ibss_joined) { | ||
1245 | wsm_unlock_tx(priv); | ||
1246 | return; | ||
1247 | } | ||
1248 | |||
1249 | mutex_lock(&priv->conf_mutex); | ||
1250 | |||
1251 | /* Under the conf lock: check scan status and | ||
1252 | * bail out if it is in progress. | ||
1253 | */ | ||
1254 | if (atomic_read(&priv->scan.in_progress)) { | ||
1255 | wsm_unlock_tx(priv); | ||
1256 | goto done_put; | ||
1257 | } | ||
1258 | |||
1259 | priv->join_pending = true; | ||
1260 | |||
1261 | /* Sanity check basic rates */ | ||
1262 | if (!join.basic_rate_set) | ||
1263 | join.basic_rate_set = 7; | ||
1264 | |||
1265 | /* Sanity check beacon interval */ | ||
1266 | if (!priv->beacon_int) | ||
1267 | priv->beacon_int = 1; | ||
1268 | |||
1269 | join.beacon_interval = priv->beacon_int; | ||
1270 | |||
1271 | /* BT Coex related changes */ | ||
1272 | if (priv->bt_present) { | ||
1273 | if (((priv->conf_listen_interval * 100) % | ||
1274 | priv->beacon_int) == 0) | ||
1275 | priv->listen_interval = | ||
1276 | ((priv->conf_listen_interval * 100) / | ||
1277 | priv->beacon_int); | ||
1278 | else | ||
1279 | priv->listen_interval = | ||
1280 | ((priv->conf_listen_interval * 100) / | ||
1281 | priv->beacon_int + 1); | ||
1282 | } | ||
1283 | |||
1284 | if (priv->hw->conf.ps_dtim_period) | ||
1285 | priv->join_dtim_period = priv->hw->conf.ps_dtim_period; | ||
1286 | join.dtim_period = priv->join_dtim_period; | ||
1287 | |||
1288 | join.channel_number = priv->channel->hw_value; | ||
1289 | join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? | ||
1290 | WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; | ||
1291 | |||
1292 | memcpy(join.bssid, bssid, sizeof(join.bssid)); | ||
1293 | |||
1294 | pr_debug("[STA] Join BSSID: %pM DTIM: %d, interval: %d\n", | ||
1295 | join.bssid, | ||
1296 | join.dtim_period, priv->beacon_int); | ||
1297 | |||
1298 | if (!conf->ibss_joined) { | ||
1299 | const u8 *ssidie; | ||
1300 | rcu_read_lock(); | ||
1301 | ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); | ||
1302 | if (ssidie) { | ||
1303 | join.ssid_len = ssidie[1]; | ||
1304 | memcpy(join.ssid, &ssidie[2], join.ssid_len); | ||
1305 | } | ||
1306 | rcu_read_unlock(); | ||
1307 | } | ||
1308 | |||
1309 | if (priv->vif->p2p) { | ||
1310 | join.flags |= WSM_JOIN_FLAGS_P2P_GO; | ||
1311 | join.basic_rate_set = | ||
1312 | cw1200_rate_mask_to_wsm(priv, 0xFF0); | ||
1313 | } | ||
1314 | |||
1315 | /* Enable asynchronous join calls */ | ||
1316 | if (!conf->ibss_joined) { | ||
1317 | join.flags |= WSM_JOIN_FLAGS_FORCE; | ||
1318 | join.flags |= WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND; | ||
1319 | } | ||
1320 | |||
1321 | wsm_flush_tx(priv); | ||
1322 | |||
1323 | /* Stay Awake for Join and Auth Timeouts and a bit more */ | ||
1324 | cw1200_pm_stay_awake(&priv->pm_state, | ||
1325 | CW1200_JOIN_TIMEOUT + CW1200_AUTH_TIMEOUT); | ||
1326 | |||
1327 | cw1200_update_listening(priv, false); | ||
1328 | |||
1329 | /* Turn on Block ACKs */ | ||
1330 | wsm_set_block_ack_policy(priv, priv->ba_tx_tid_mask, | ||
1331 | priv->ba_rx_tid_mask); | ||
1332 | |||
1333 | /* Set up timeout */ | ||
1334 | if (join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND) { | ||
1335 | priv->join_status = CW1200_JOIN_STATUS_JOINING; | ||
1336 | queue_delayed_work(priv->workqueue, | ||
1337 | &priv->join_timeout, | ||
1338 | CW1200_JOIN_TIMEOUT); | ||
1339 | } | ||
1340 | |||
1341 | /* 802.11w protected mgmt frames */ | ||
1342 | mgmt_policy.protectedMgmtEnable = 0; | ||
1343 | mgmt_policy.unprotectedMgmtFramesAllowed = 1; | ||
1344 | mgmt_policy.encryptionForAuthFrame = 1; | ||
1345 | wsm_set_protected_mgmt_policy(priv, &mgmt_policy); | ||
1346 | |||
1347 | /* Perform actual join */ | ||
1348 | if (wsm_join(priv, &join)) { | ||
1349 | pr_err("[STA] cw1200_join_work: wsm_join failed!\n"); | ||
1350 | cancel_delayed_work_sync(&priv->join_timeout); | ||
1351 | cw1200_update_listening(priv, priv->listening); | ||
1352 | /* Tx lock still held, unjoin will clear it. */ | ||
1353 | if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) | ||
1354 | wsm_unlock_tx(priv); | ||
1355 | } else { | ||
1356 | if (!(join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND)) | ||
1357 | cw1200_join_complete(priv); /* Will clear tx_lock */ | ||
1358 | |||
1359 | /* Upload keys */ | ||
1360 | cw1200_upload_keys(priv); | ||
1361 | |||
1362 | /* Due to beacon filtering it is possible that the | ||
1363 | * AP's beacon is not known for the mac80211 stack. | ||
1364 | * Disable filtering temporary to make sure the stack | ||
1365 | * receives at least one | ||
1366 | */ | ||
1367 | priv->disable_beacon_filter = true; | ||
1368 | } | ||
1369 | cw1200_update_filtering(priv); | ||
1370 | |||
1371 | done_put: | ||
1372 | mutex_unlock(&priv->conf_mutex); | ||
1373 | if (bss) | ||
1374 | cfg80211_put_bss(priv->hw->wiphy, bss); | ||
1375 | } | ||
1376 | |||
1377 | void cw1200_join_timeout(struct work_struct *work) | ||
1378 | { | ||
1379 | struct cw1200_common *priv = | ||
1380 | container_of(work, struct cw1200_common, join_timeout.work); | ||
1381 | pr_debug("[WSM] Join timed out.\n"); | ||
1382 | wsm_lock_tx(priv); | ||
1383 | if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) | ||
1384 | wsm_unlock_tx(priv); | ||
1385 | } | ||
1386 | |||
1387 | static void cw1200_do_unjoin(struct cw1200_common *priv) | ||
1388 | { | ||
1389 | struct wsm_reset reset = { | ||
1390 | .reset_statistics = true, | ||
1391 | }; | ||
1392 | |||
1393 | cancel_delayed_work_sync(&priv->join_timeout); | ||
1394 | |||
1395 | mutex_lock(&priv->conf_mutex); | ||
1396 | priv->join_pending = false; | ||
1397 | |||
1398 | if (atomic_read(&priv->scan.in_progress)) { | ||
1399 | if (priv->delayed_unjoin) | ||
1400 | wiphy_dbg(priv->hw->wiphy, "Delayed unjoin is already scheduled.\n"); | ||
1401 | else | ||
1402 | priv->delayed_unjoin = true; | ||
1403 | goto done; | ||
1404 | } | ||
1405 | |||
1406 | priv->delayed_link_loss = false; | ||
1407 | |||
1408 | if (!priv->join_status) | ||
1409 | goto done; | ||
1410 | |||
1411 | if (priv->join_status > CW1200_JOIN_STATUS_IBSS) { | ||
1412 | wiphy_err(priv->hw->wiphy, "Unexpected: join status: %d\n", | ||
1413 | priv->join_status); | ||
1414 | BUG_ON(1); | ||
1415 | } | ||
1416 | |||
1417 | cancel_work_sync(&priv->update_filtering_work); | ||
1418 | cancel_work_sync(&priv->set_beacon_wakeup_period_work); | ||
1419 | priv->join_status = CW1200_JOIN_STATUS_PASSIVE; | ||
1420 | |||
1421 | /* Unjoin is a reset. */ | ||
1422 | wsm_flush_tx(priv); | ||
1423 | wsm_keep_alive_period(priv, 0); | ||
1424 | wsm_reset(priv, &reset); | ||
1425 | wsm_set_output_power(priv, priv->output_power * 10); | ||
1426 | priv->join_dtim_period = 0; | ||
1427 | cw1200_setup_mac(priv); | ||
1428 | cw1200_free_event_queue(priv); | ||
1429 | cancel_work_sync(&priv->event_handler); | ||
1430 | cw1200_update_listening(priv, priv->listening); | ||
1431 | cw1200_cqm_bssloss_sm(priv, 0, 0, 0); | ||
1432 | |||
1433 | /* Disable Block ACKs */ | ||
1434 | wsm_set_block_ack_policy(priv, 0, 0); | ||
1435 | |||
1436 | priv->disable_beacon_filter = false; | ||
1437 | cw1200_update_filtering(priv); | ||
1438 | memset(&priv->association_mode, 0, | ||
1439 | sizeof(priv->association_mode)); | ||
1440 | memset(&priv->bss_params, 0, sizeof(priv->bss_params)); | ||
1441 | priv->setbssparams_done = false; | ||
1442 | memset(&priv->firmware_ps_mode, 0, | ||
1443 | sizeof(priv->firmware_ps_mode)); | ||
1444 | |||
1445 | pr_debug("[STA] Unjoin completed.\n"); | ||
1446 | |||
1447 | done: | ||
1448 | mutex_unlock(&priv->conf_mutex); | ||
1449 | } | ||
1450 | |||
1451 | void cw1200_unjoin_work(struct work_struct *work) | ||
1452 | { | ||
1453 | struct cw1200_common *priv = | ||
1454 | container_of(work, struct cw1200_common, unjoin_work); | ||
1455 | |||
1456 | cw1200_do_unjoin(priv); | ||
1457 | |||
1458 | /* Tell the stack we're dead */ | ||
1459 | ieee80211_connection_loss(priv->vif); | ||
1460 | |||
1461 | wsm_unlock_tx(priv); | ||
1462 | } | ||
1463 | |||
1464 | int cw1200_enable_listening(struct cw1200_common *priv) | ||
1465 | { | ||
1466 | struct wsm_start start = { | ||
1467 | .mode = WSM_START_MODE_P2P_DEV, | ||
1468 | .band = WSM_PHY_BAND_2_4G, | ||
1469 | .beacon_interval = 100, | ||
1470 | .dtim_period = 1, | ||
1471 | .probe_delay = 0, | ||
1472 | .basic_rate_set = 0x0F, | ||
1473 | }; | ||
1474 | |||
1475 | if (priv->channel) { | ||
1476 | start.band = priv->channel->band == IEEE80211_BAND_5GHZ ? | ||
1477 | WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; | ||
1478 | start.channel_number = priv->channel->hw_value; | ||
1479 | } else { | ||
1480 | start.band = WSM_PHY_BAND_2_4G; | ||
1481 | start.channel_number = 1; | ||
1482 | } | ||
1483 | |||
1484 | return wsm_start(priv, &start); | ||
1485 | } | ||
1486 | |||
1487 | int cw1200_disable_listening(struct cw1200_common *priv) | ||
1488 | { | ||
1489 | int ret; | ||
1490 | struct wsm_reset reset = { | ||
1491 | .reset_statistics = true, | ||
1492 | }; | ||
1493 | ret = wsm_reset(priv, &reset); | ||
1494 | return ret; | ||
1495 | } | ||
1496 | |||
1497 | void cw1200_update_listening(struct cw1200_common *priv, bool enabled) | ||
1498 | { | ||
1499 | if (enabled) { | ||
1500 | if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) { | ||
1501 | if (!cw1200_enable_listening(priv)) | ||
1502 | priv->join_status = CW1200_JOIN_STATUS_MONITOR; | ||
1503 | wsm_set_probe_responder(priv, true); | ||
1504 | } | ||
1505 | } else { | ||
1506 | if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { | ||
1507 | if (!cw1200_disable_listening(priv)) | ||
1508 | priv->join_status = CW1200_JOIN_STATUS_PASSIVE; | ||
1509 | wsm_set_probe_responder(priv, false); | ||
1510 | } | ||
1511 | } | ||
1512 | } | ||
1513 | |||
1514 | int cw1200_set_uapsd_param(struct cw1200_common *priv, | ||
1515 | const struct wsm_edca_params *arg) | ||
1516 | { | ||
1517 | int ret; | ||
1518 | u16 uapsd_flags = 0; | ||
1519 | |||
1520 | /* Here's the mapping AC [queue, bit] | ||
1521 | * VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0] | ||
1522 | */ | ||
1523 | |||
1524 | if (arg->uapsd_enable[0]) | ||
1525 | uapsd_flags |= 1 << 3; | ||
1526 | |||
1527 | if (arg->uapsd_enable[1]) | ||
1528 | uapsd_flags |= 1 << 2; | ||
1529 | |||
1530 | if (arg->uapsd_enable[2]) | ||
1531 | uapsd_flags |= 1 << 1; | ||
1532 | |||
1533 | if (arg->uapsd_enable[3]) | ||
1534 | uapsd_flags |= 1; | ||
1535 | |||
1536 | /* Currently pseudo U-APSD operation is not supported, so setting | ||
1537 | * MinAutoTriggerInterval, MaxAutoTriggerInterval and | ||
1538 | * AutoTriggerStep to 0 | ||
1539 | */ | ||
1540 | |||
1541 | priv->uapsd_info.uapsd_flags = cpu_to_le16(uapsd_flags); | ||
1542 | priv->uapsd_info.min_auto_trigger_interval = 0; | ||
1543 | priv->uapsd_info.max_auto_trigger_interval = 0; | ||
1544 | priv->uapsd_info.auto_trigger_step = 0; | ||
1545 | |||
1546 | ret = wsm_set_uapsd_info(priv, &priv->uapsd_info); | ||
1547 | return ret; | ||
1548 | } | ||
1549 | |||
1550 | /* ******************************************************************** */ | ||
1551 | /* AP API */ | ||
1552 | |||
1553 | int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | ||
1554 | struct ieee80211_sta *sta) | ||
1555 | { | ||
1556 | struct cw1200_common *priv = hw->priv; | ||
1557 | struct cw1200_sta_priv *sta_priv = | ||
1558 | (struct cw1200_sta_priv *)&sta->drv_priv; | ||
1559 | struct cw1200_link_entry *entry; | ||
1560 | struct sk_buff *skb; | ||
1561 | |||
1562 | if (priv->mode != NL80211_IFTYPE_AP) | ||
1563 | return 0; | ||
1564 | |||
1565 | sta_priv->link_id = cw1200_find_link_id(priv, sta->addr); | ||
1566 | if (WARN_ON(!sta_priv->link_id)) { | ||
1567 | wiphy_info(priv->hw->wiphy, | ||
1568 | "[AP] No more link IDs available.\n"); | ||
1569 | return -ENOENT; | ||
1570 | } | ||
1571 | |||
1572 | entry = &priv->link_id_db[sta_priv->link_id - 1]; | ||
1573 | spin_lock_bh(&priv->ps_state_lock); | ||
1574 | if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) == | ||
1575 | IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) | ||
1576 | priv->sta_asleep_mask |= BIT(sta_priv->link_id); | ||
1577 | entry->status = CW1200_LINK_HARD; | ||
1578 | while ((skb = skb_dequeue(&entry->rx_queue))) | ||
1579 | ieee80211_rx_irqsafe(priv->hw, skb); | ||
1580 | spin_unlock_bh(&priv->ps_state_lock); | ||
1581 | return 0; | ||
1582 | } | ||
1583 | |||
1584 | int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | ||
1585 | struct ieee80211_sta *sta) | ||
1586 | { | ||
1587 | struct cw1200_common *priv = hw->priv; | ||
1588 | struct cw1200_sta_priv *sta_priv = | ||
1589 | (struct cw1200_sta_priv *)&sta->drv_priv; | ||
1590 | struct cw1200_link_entry *entry; | ||
1591 | |||
1592 | if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) | ||
1593 | return 0; | ||
1594 | |||
1595 | entry = &priv->link_id_db[sta_priv->link_id - 1]; | ||
1596 | spin_lock_bh(&priv->ps_state_lock); | ||
1597 | entry->status = CW1200_LINK_RESERVE; | ||
1598 | entry->timestamp = jiffies; | ||
1599 | wsm_lock_tx_async(priv); | ||
1600 | if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) | ||
1601 | wsm_unlock_tx(priv); | ||
1602 | spin_unlock_bh(&priv->ps_state_lock); | ||
1603 | flush_workqueue(priv->workqueue); | ||
1604 | return 0; | ||
1605 | } | ||
1606 | |||
1607 | static void __cw1200_sta_notify(struct ieee80211_hw *dev, | ||
1608 | struct ieee80211_vif *vif, | ||
1609 | enum sta_notify_cmd notify_cmd, | ||
1610 | int link_id) | ||
1611 | { | ||
1612 | struct cw1200_common *priv = dev->priv; | ||
1613 | u32 bit, prev; | ||
1614 | |||
1615 | /* Zero link id means "for all link IDs" */ | ||
1616 | if (link_id) | ||
1617 | bit = BIT(link_id); | ||
1618 | else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE)) | ||
1619 | bit = 0; | ||
1620 | else | ||
1621 | bit = priv->link_id_map; | ||
1622 | prev = priv->sta_asleep_mask & bit; | ||
1623 | |||
1624 | switch (notify_cmd) { | ||
1625 | case STA_NOTIFY_SLEEP: | ||
1626 | if (!prev) { | ||
1627 | if (priv->buffered_multicasts && | ||
1628 | !priv->sta_asleep_mask) | ||
1629 | queue_work(priv->workqueue, | ||
1630 | &priv->multicast_start_work); | ||
1631 | priv->sta_asleep_mask |= bit; | ||
1632 | } | ||
1633 | break; | ||
1634 | case STA_NOTIFY_AWAKE: | ||
1635 | if (prev) { | ||
1636 | priv->sta_asleep_mask &= ~bit; | ||
1637 | priv->pspoll_mask &= ~bit; | ||
1638 | if (priv->tx_multicast && link_id && | ||
1639 | !priv->sta_asleep_mask) | ||
1640 | queue_work(priv->workqueue, | ||
1641 | &priv->multicast_stop_work); | ||
1642 | cw1200_bh_wakeup(priv); | ||
1643 | } | ||
1644 | break; | ||
1645 | } | ||
1646 | } | ||
1647 | |||
1648 | void cw1200_sta_notify(struct ieee80211_hw *dev, | ||
1649 | struct ieee80211_vif *vif, | ||
1650 | enum sta_notify_cmd notify_cmd, | ||
1651 | struct ieee80211_sta *sta) | ||
1652 | { | ||
1653 | struct cw1200_common *priv = dev->priv; | ||
1654 | struct cw1200_sta_priv *sta_priv = | ||
1655 | (struct cw1200_sta_priv *)&sta->drv_priv; | ||
1656 | |||
1657 | spin_lock_bh(&priv->ps_state_lock); | ||
1658 | __cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id); | ||
1659 | spin_unlock_bh(&priv->ps_state_lock); | ||
1660 | } | ||
1661 | |||
1662 | static void cw1200_ps_notify(struct cw1200_common *priv, | ||
1663 | int link_id, bool ps) | ||
1664 | { | ||
1665 | if (link_id > CW1200_MAX_STA_IN_AP_MODE) | ||
1666 | return; | ||
1667 | |||
1668 | pr_debug("%s for LinkId: %d. STAs asleep: %.8X\n", | ||
1669 | ps ? "Stop" : "Start", | ||
1670 | link_id, priv->sta_asleep_mask); | ||
1671 | |||
1672 | __cw1200_sta_notify(priv->hw, priv->vif, | ||
1673 | ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id); | ||
1674 | } | ||
1675 | |||
1676 | static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set) | ||
1677 | { | ||
1678 | struct sk_buff *skb; | ||
1679 | struct wsm_update_ie update_ie = { | ||
1680 | .what = WSM_UPDATE_IE_BEACON, | ||
1681 | .count = 1, | ||
1682 | }; | ||
1683 | u16 tim_offset, tim_length; | ||
1684 | |||
1685 | pr_debug("[AP] mcast: %s.\n", aid0_bit_set ? "ena" : "dis"); | ||
1686 | |||
1687 | skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, | ||
1688 | &tim_offset, &tim_length); | ||
1689 | if (!skb) { | ||
1690 | if (!__cw1200_flush(priv, true)) | ||
1691 | wsm_unlock_tx(priv); | ||
1692 | return -ENOENT; | ||
1693 | } | ||
1694 | |||
1695 | if (tim_offset && tim_length >= 6) { | ||
1696 | /* Ignore DTIM count from mac80211: | ||
1697 | * firmware handles DTIM internally. | ||
1698 | */ | ||
1699 | skb->data[tim_offset + 2] = 0; | ||
1700 | |||
1701 | /* Set/reset aid0 bit */ | ||
1702 | if (aid0_bit_set) | ||
1703 | skb->data[tim_offset + 4] |= 1; | ||
1704 | else | ||
1705 | skb->data[tim_offset + 4] &= ~1; | ||
1706 | } | ||
1707 | |||
1708 | update_ie.ies = &skb->data[tim_offset]; | ||
1709 | update_ie.length = tim_length; | ||
1710 | wsm_update_ie(priv, &update_ie); | ||
1711 | |||
1712 | dev_kfree_skb(skb); | ||
1713 | |||
1714 | return 0; | ||
1715 | } | ||
1716 | |||
1717 | void cw1200_set_tim_work(struct work_struct *work) | ||
1718 | { | ||
1719 | struct cw1200_common *priv = | ||
1720 | container_of(work, struct cw1200_common, set_tim_work); | ||
1721 | (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set); | ||
1722 | } | ||
1723 | |||
1724 | int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, | ||
1725 | bool set) | ||
1726 | { | ||
1727 | struct cw1200_common *priv = dev->priv; | ||
1728 | queue_work(priv->workqueue, &priv->set_tim_work); | ||
1729 | return 0; | ||
1730 | } | ||
1731 | |||
1732 | void cw1200_set_cts_work(struct work_struct *work) | ||
1733 | { | ||
1734 | struct cw1200_common *priv = | ||
1735 | container_of(work, struct cw1200_common, set_cts_work); | ||
1736 | |||
1737 | u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0}; | ||
1738 | struct wsm_update_ie update_ie = { | ||
1739 | .what = WSM_UPDATE_IE_BEACON, | ||
1740 | .count = 1, | ||
1741 | .ies = erp_ie, | ||
1742 | .length = 3, | ||
1743 | }; | ||
1744 | u32 erp_info; | ||
1745 | __le32 use_cts_prot; | ||
1746 | mutex_lock(&priv->conf_mutex); | ||
1747 | erp_info = priv->erp_info; | ||
1748 | mutex_unlock(&priv->conf_mutex); | ||
1749 | use_cts_prot = | ||
1750 | erp_info & WLAN_ERP_USE_PROTECTION ? | ||
1751 | __cpu_to_le32(1) : 0; | ||
1752 | |||
1753 | erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info; | ||
1754 | |||
1755 | pr_debug("[STA] ERP information 0x%x\n", erp_info); | ||
1756 | |||
1757 | wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION, | ||
1758 | &use_cts_prot, sizeof(use_cts_prot)); | ||
1759 | wsm_update_ie(priv, &update_ie); | ||
1760 | |||
1761 | return; | ||
1762 | } | ||
1763 | |||
1764 | static int cw1200_set_btcoexinfo(struct cw1200_common *priv) | ||
1765 | { | ||
1766 | struct wsm_override_internal_txrate arg; | ||
1767 | int ret = 0; | ||
1768 | |||
1769 | if (priv->mode == NL80211_IFTYPE_STATION) { | ||
1770 | /* Plumb PSPOLL and NULL template */ | ||
1771 | cw1200_upload_pspoll(priv); | ||
1772 | cw1200_upload_null(priv); | ||
1773 | cw1200_upload_qosnull(priv); | ||
1774 | } else { | ||
1775 | return 0; | ||
1776 | } | ||
1777 | |||
1778 | memset(&arg, 0, sizeof(struct wsm_override_internal_txrate)); | ||
1779 | |||
1780 | if (!priv->vif->p2p) { | ||
1781 | /* STATION mode */ | ||
1782 | if (priv->bss_params.operational_rate_set & ~0xF) { | ||
1783 | pr_debug("[STA] STA has ERP rates\n"); | ||
1784 | /* G or BG mode */ | ||
1785 | arg.internalTxRate = (__ffs( | ||
1786 | priv->bss_params.operational_rate_set & ~0xF)); | ||
1787 | } else { | ||
1788 | pr_debug("[STA] STA has non ERP rates\n"); | ||
1789 | /* B only mode */ | ||
1790 | arg.internalTxRate = (__ffs(priv->association_mode.basic_rate_set)); | ||
1791 | } | ||
1792 | arg.nonErpInternalTxRate = (__ffs(priv->association_mode.basic_rate_set)); | ||
1793 | } else { | ||
1794 | /* P2P mode */ | ||
1795 | arg.internalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF)); | ||
1796 | arg.nonErpInternalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF)); | ||
1797 | } | ||
1798 | |||
1799 | pr_debug("[STA] BTCOEX_INFO MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n", | ||
1800 | priv->mode, | ||
1801 | arg.internalTxRate, | ||
1802 | arg.nonErpInternalTxRate); | ||
1803 | |||
1804 | ret = wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, | ||
1805 | &arg, sizeof(arg)); | ||
1806 | |||
1807 | return ret; | ||
1808 | } | ||
1809 | |||
1810 | void cw1200_bss_info_changed(struct ieee80211_hw *dev, | ||
1811 | struct ieee80211_vif *vif, | ||
1812 | struct ieee80211_bss_conf *info, | ||
1813 | u32 changed) | ||
1814 | { | ||
1815 | struct cw1200_common *priv = dev->priv; | ||
1816 | bool do_join = false; | ||
1817 | |||
1818 | mutex_lock(&priv->conf_mutex); | ||
1819 | |||
1820 | pr_debug("BSS CHANGED: %08x\n", changed); | ||
1821 | |||
1822 | /* TODO: BSS_CHANGED_QOS */ | ||
1823 | /* TODO: BSS_CHANGED_TXPOWER */ | ||
1824 | |||
1825 | if (changed & BSS_CHANGED_ARP_FILTER) { | ||
1826 | struct wsm_mib_arp_ipv4_filter filter = {0}; | ||
1827 | int i; | ||
1828 | |||
1829 | pr_debug("[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n", | ||
1830 | info->arp_addr_cnt); | ||
1831 | |||
1832 | /* Currently only one IP address is supported by firmware. | ||
1833 | * In case of more IPs arp filtering will be disabled. | ||
1834 | */ | ||
1835 | if (info->arp_addr_cnt > 0 && | ||
1836 | info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) { | ||
1837 | for (i = 0; i < info->arp_addr_cnt; i++) { | ||
1838 | filter.ipv4addrs[i] = info->arp_addr_list[i]; | ||
1839 | pr_debug("[STA] addr[%d]: 0x%X\n", | ||
1840 | i, filter.ipv4addrs[i]); | ||
1841 | } | ||
1842 | filter.enable = __cpu_to_le32(1); | ||
1843 | } | ||
1844 | |||
1845 | pr_debug("[STA] arp ip filter enable: %d\n", | ||
1846 | __le32_to_cpu(filter.enable)); | ||
1847 | |||
1848 | wsm_set_arp_ipv4_filter(priv, &filter); | ||
1849 | } | ||
1850 | |||
1851 | if (changed & | ||
1852 | (BSS_CHANGED_BEACON | | ||
1853 | BSS_CHANGED_AP_PROBE_RESP | | ||
1854 | BSS_CHANGED_BSSID | | ||
1855 | BSS_CHANGED_SSID | | ||
1856 | BSS_CHANGED_IBSS)) { | ||
1857 | pr_debug("BSS_CHANGED_BEACON\n"); | ||
1858 | priv->beacon_int = info->beacon_int; | ||
1859 | cw1200_update_beaconing(priv); | ||
1860 | cw1200_upload_beacon(priv); | ||
1861 | } | ||
1862 | |||
1863 | if (changed & BSS_CHANGED_BEACON_ENABLED) { | ||
1864 | pr_debug("BSS_CHANGED_BEACON_ENABLED (%d)\n", info->enable_beacon); | ||
1865 | |||
1866 | if (priv->enable_beacon != info->enable_beacon) { | ||
1867 | cw1200_enable_beaconing(priv, info->enable_beacon); | ||
1868 | priv->enable_beacon = info->enable_beacon; | ||
1869 | } | ||
1870 | } | ||
1871 | |||
1872 | if (changed & BSS_CHANGED_BEACON_INT) { | ||
1873 | pr_debug("CHANGED_BEACON_INT\n"); | ||
1874 | if (info->ibss_joined) | ||
1875 | do_join = true; | ||
1876 | else if (priv->join_status == CW1200_JOIN_STATUS_AP) | ||
1877 | cw1200_update_beaconing(priv); | ||
1878 | } | ||
1879 | |||
1880 | /* assoc/disassoc, or maybe AID changed */ | ||
1881 | if (changed & BSS_CHANGED_ASSOC) { | ||
1882 | wsm_lock_tx(priv); | ||
1883 | priv->wep_default_key_id = -1; | ||
1884 | wsm_unlock_tx(priv); | ||
1885 | } | ||
1886 | |||
1887 | if (changed & BSS_CHANGED_BSSID) { | ||
1888 | pr_debug("BSS_CHANGED_BSSID\n"); | ||
1889 | do_join = true; | ||
1890 | } | ||
1891 | |||
1892 | if (changed & | ||
1893 | (BSS_CHANGED_ASSOC | | ||
1894 | BSS_CHANGED_BSSID | | ||
1895 | BSS_CHANGED_IBSS | | ||
1896 | BSS_CHANGED_BASIC_RATES | | ||
1897 | BSS_CHANGED_HT)) { | ||
1898 | pr_debug("BSS_CHANGED_ASSOC\n"); | ||
1899 | if (info->assoc) { | ||
1900 | if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) { | ||
1901 | ieee80211_connection_loss(vif); | ||
1902 | mutex_unlock(&priv->conf_mutex); | ||
1903 | return; | ||
1904 | } else if (priv->join_status == CW1200_JOIN_STATUS_PRE_STA) { | ||
1905 | priv->join_status = CW1200_JOIN_STATUS_STA; | ||
1906 | } | ||
1907 | } else { | ||
1908 | do_join = true; | ||
1909 | } | ||
1910 | |||
1911 | if (info->assoc || info->ibss_joined) { | ||
1912 | struct ieee80211_sta *sta = NULL; | ||
1913 | u32 val = 0; | ||
1914 | |||
1915 | if (info->dtim_period) | ||
1916 | priv->join_dtim_period = info->dtim_period; | ||
1917 | priv->beacon_int = info->beacon_int; | ||
1918 | |||
1919 | rcu_read_lock(); | ||
1920 | |||
1921 | if (info->bssid && !info->ibss_joined) | ||
1922 | sta = ieee80211_find_sta(vif, info->bssid); | ||
1923 | if (sta) { | ||
1924 | priv->ht_info.ht_cap = sta->ht_cap; | ||
1925 | priv->bss_params.operational_rate_set = | ||
1926 | cw1200_rate_mask_to_wsm(priv, | ||
1927 | sta->supp_rates[priv->channel->band]); | ||
1928 | priv->ht_info.channel_type = cfg80211_get_chandef_type(&dev->conf.chandef); | ||
1929 | priv->ht_info.operation_mode = info->ht_operation_mode; | ||
1930 | } else { | ||
1931 | memset(&priv->ht_info, 0, | ||
1932 | sizeof(priv->ht_info)); | ||
1933 | priv->bss_params.operational_rate_set = -1; | ||
1934 | } | ||
1935 | rcu_read_unlock(); | ||
1936 | |||
1937 | /* Non Greenfield stations present */ | ||
1938 | if (priv->ht_info.operation_mode & | ||
1939 | IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) | ||
1940 | val |= WSM_NON_GREENFIELD_STA_PRESENT; | ||
1941 | |||
1942 | /* Set HT protection method */ | ||
1943 | val |= (priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) << 2; | ||
1944 | |||
1945 | /* TODO: | ||
1946 | * STBC_param.dual_cts | ||
1947 | * STBC_param.LSIG_TXOP_FILL | ||
1948 | */ | ||
1949 | |||
1950 | val = cpu_to_le32(val); | ||
1951 | wsm_write_mib(priv, WSM_MIB_ID_SET_HT_PROTECTION, | ||
1952 | &val, sizeof(val)); | ||
1953 | |||
1954 | priv->association_mode.greenfield = | ||
1955 | cw1200_ht_greenfield(&priv->ht_info); | ||
1956 | priv->association_mode.flags = | ||
1957 | WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES | | ||
1958 | WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE | | ||
1959 | WSM_ASSOCIATION_MODE_USE_HT_MODE | | ||
1960 | WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET | | ||
1961 | WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING; | ||
1962 | priv->association_mode.preamble = | ||
1963 | info->use_short_preamble ? | ||
1964 | WSM_JOIN_PREAMBLE_SHORT : | ||
1965 | WSM_JOIN_PREAMBLE_LONG; | ||
1966 | priv->association_mode.basic_rate_set = __cpu_to_le32( | ||
1967 | cw1200_rate_mask_to_wsm(priv, | ||
1968 | info->basic_rates)); | ||
1969 | priv->association_mode.mpdu_start_spacing = | ||
1970 | cw1200_ht_ampdu_density(&priv->ht_info); | ||
1971 | |||
1972 | cw1200_cqm_bssloss_sm(priv, 0, 0, 0); | ||
1973 | cancel_work_sync(&priv->unjoin_work); | ||
1974 | |||
1975 | priv->bss_params.beacon_lost_count = priv->cqm_beacon_loss_count; | ||
1976 | priv->bss_params.aid = info->aid; | ||
1977 | |||
1978 | if (priv->join_dtim_period < 1) | ||
1979 | priv->join_dtim_period = 1; | ||
1980 | |||
1981 | pr_debug("[STA] DTIM %d, interval: %d\n", | ||
1982 | priv->join_dtim_period, priv->beacon_int); | ||
1983 | pr_debug("[STA] Preamble: %d, Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n", | ||
1984 | priv->association_mode.preamble, | ||
1985 | priv->association_mode.greenfield, | ||
1986 | priv->bss_params.aid, | ||
1987 | priv->bss_params.operational_rate_set, | ||
1988 | priv->association_mode.basic_rate_set); | ||
1989 | wsm_set_association_mode(priv, &priv->association_mode); | ||
1990 | |||
1991 | if (!info->ibss_joined) { | ||
1992 | wsm_keep_alive_period(priv, 30 /* sec */); | ||
1993 | wsm_set_bss_params(priv, &priv->bss_params); | ||
1994 | priv->setbssparams_done = true; | ||
1995 | cw1200_set_beacon_wakeup_period_work(&priv->set_beacon_wakeup_period_work); | ||
1996 | cw1200_set_pm(priv, &priv->powersave_mode); | ||
1997 | } | ||
1998 | if (priv->vif->p2p) { | ||
1999 | pr_debug("[STA] Setting p2p powersave configuration.\n"); | ||
2000 | wsm_set_p2p_ps_modeinfo(priv, | ||
2001 | &priv->p2p_ps_modeinfo); | ||
2002 | } | ||
2003 | if (priv->bt_present) | ||
2004 | cw1200_set_btcoexinfo(priv); | ||
2005 | } else { | ||
2006 | memset(&priv->association_mode, 0, | ||
2007 | sizeof(priv->association_mode)); | ||
2008 | memset(&priv->bss_params, 0, sizeof(priv->bss_params)); | ||
2009 | } | ||
2010 | } | ||
2011 | |||
2012 | /* ERP Protection */ | ||
2013 | if (changed & (BSS_CHANGED_ASSOC | | ||
2014 | BSS_CHANGED_ERP_CTS_PROT | | ||
2015 | BSS_CHANGED_ERP_PREAMBLE)) { | ||
2016 | u32 prev_erp_info = priv->erp_info; | ||
2017 | if (info->use_cts_prot) | ||
2018 | priv->erp_info |= WLAN_ERP_USE_PROTECTION; | ||
2019 | else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT)) | ||
2020 | priv->erp_info &= ~WLAN_ERP_USE_PROTECTION; | ||
2021 | |||
2022 | if (info->use_short_preamble) | ||
2023 | priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE; | ||
2024 | else | ||
2025 | priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE; | ||
2026 | |||
2027 | pr_debug("[STA] ERP Protection: %x\n", priv->erp_info); | ||
2028 | |||
2029 | if (prev_erp_info != priv->erp_info) | ||
2030 | queue_work(priv->workqueue, &priv->set_cts_work); | ||
2031 | } | ||
2032 | |||
2033 | /* ERP Slottime */ | ||
2034 | if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) { | ||
2035 | __le32 slot_time = info->use_short_slot ? | ||
2036 | __cpu_to_le32(9) : __cpu_to_le32(20); | ||
2037 | pr_debug("[STA] Slot time: %d us.\n", | ||
2038 | __le32_to_cpu(slot_time)); | ||
2039 | wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME, | ||
2040 | &slot_time, sizeof(slot_time)); | ||
2041 | } | ||
2042 | |||
2043 | if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { | ||
2044 | struct wsm_rcpi_rssi_threshold threshold = { | ||
2045 | .rollingAverageCount = 8, | ||
2046 | }; | ||
2047 | pr_debug("[CQM] RSSI threshold subscribe: %d +- %d\n", | ||
2048 | info->cqm_rssi_thold, info->cqm_rssi_hyst); | ||
2049 | priv->cqm_rssi_thold = info->cqm_rssi_thold; | ||
2050 | priv->cqm_rssi_hyst = info->cqm_rssi_hyst; | ||
2051 | |||
2052 | if (info->cqm_rssi_thold || info->cqm_rssi_hyst) { | ||
2053 | /* RSSI subscription enabled */ | ||
2054 | /* TODO: It's not a correct way of setting threshold. | ||
2055 | * Upper and lower must be set equal here and adjusted | ||
2056 | * in callback. However current implementation is much | ||
2057 | * more relaible and stable. | ||
2058 | */ | ||
2059 | |||
2060 | /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 | ||
2061 | * RSSI = RCPI / 2 - 110 | ||
2062 | */ | ||
2063 | if (priv->cqm_use_rssi) { | ||
2064 | threshold.upperThreshold = | ||
2065 | info->cqm_rssi_thold + info->cqm_rssi_hyst; | ||
2066 | threshold.lowerThreshold = | ||
2067 | info->cqm_rssi_thold; | ||
2068 | threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI; | ||
2069 | } else { | ||
2070 | threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110) * 2; | ||
2071 | threshold.lowerThreshold = (info->cqm_rssi_thold + 110) * 2; | ||
2072 | } | ||
2073 | threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE; | ||
2074 | } else { | ||
2075 | /* There is a bug in FW, see sta.c. We have to enable | ||
2076 | * dummy subscription to get correct RSSI values. | ||
2077 | */ | ||
2078 | threshold.rssiRcpiMode |= | ||
2079 | WSM_RCPI_RSSI_THRESHOLD_ENABLE | | ||
2080 | WSM_RCPI_RSSI_DONT_USE_UPPER | | ||
2081 | WSM_RCPI_RSSI_DONT_USE_LOWER; | ||
2082 | if (priv->cqm_use_rssi) | ||
2083 | threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI; | ||
2084 | } | ||
2085 | wsm_set_rcpi_rssi_threshold(priv, &threshold); | ||
2086 | } | ||
2087 | mutex_unlock(&priv->conf_mutex); | ||
2088 | |||
2089 | if (do_join) { | ||
2090 | wsm_lock_tx(priv); | ||
2091 | cw1200_do_join(priv); /* Will unlock it for us */ | ||
2092 | } | ||
2093 | } | ||
2094 | |||
2095 | void cw1200_multicast_start_work(struct work_struct *work) | ||
2096 | { | ||
2097 | struct cw1200_common *priv = | ||
2098 | container_of(work, struct cw1200_common, multicast_start_work); | ||
2099 | long tmo = priv->join_dtim_period * | ||
2100 | (priv->beacon_int + 20) * HZ / 1024; | ||
2101 | |||
2102 | cancel_work_sync(&priv->multicast_stop_work); | ||
2103 | |||
2104 | if (!priv->aid0_bit_set) { | ||
2105 | wsm_lock_tx(priv); | ||
2106 | cw1200_set_tim_impl(priv, true); | ||
2107 | priv->aid0_bit_set = true; | ||
2108 | mod_timer(&priv->mcast_timeout, jiffies + tmo); | ||
2109 | wsm_unlock_tx(priv); | ||
2110 | } | ||
2111 | } | ||
2112 | |||
2113 | void cw1200_multicast_stop_work(struct work_struct *work) | ||
2114 | { | ||
2115 | struct cw1200_common *priv = | ||
2116 | container_of(work, struct cw1200_common, multicast_stop_work); | ||
2117 | |||
2118 | if (priv->aid0_bit_set) { | ||
2119 | del_timer_sync(&priv->mcast_timeout); | ||
2120 | wsm_lock_tx(priv); | ||
2121 | priv->aid0_bit_set = false; | ||
2122 | cw1200_set_tim_impl(priv, false); | ||
2123 | wsm_unlock_tx(priv); | ||
2124 | } | ||
2125 | } | ||
2126 | |||
2127 | void cw1200_mcast_timeout(unsigned long arg) | ||
2128 | { | ||
2129 | struct cw1200_common *priv = | ||
2130 | (struct cw1200_common *)arg; | ||
2131 | |||
2132 | wiphy_warn(priv->hw->wiphy, | ||
2133 | "Multicast delivery timeout.\n"); | ||
2134 | spin_lock_bh(&priv->ps_state_lock); | ||
2135 | priv->tx_multicast = priv->aid0_bit_set && | ||
2136 | priv->buffered_multicasts; | ||
2137 | if (priv->tx_multicast) | ||
2138 | cw1200_bh_wakeup(priv); | ||
2139 | spin_unlock_bh(&priv->ps_state_lock); | ||
2140 | } | ||
2141 | |||
2142 | int cw1200_ampdu_action(struct ieee80211_hw *hw, | ||
2143 | struct ieee80211_vif *vif, | ||
2144 | enum ieee80211_ampdu_mlme_action action, | ||
2145 | struct ieee80211_sta *sta, u16 tid, u16 *ssn, | ||
2146 | u8 buf_size) | ||
2147 | { | ||
2148 | /* Aggregation is implemented fully in firmware, | ||
2149 | * including block ack negotiation. Do not allow | ||
2150 | * mac80211 stack to do anything: it interferes with | ||
2151 | * the firmware. | ||
2152 | */ | ||
2153 | |||
2154 | /* Note that we still need this function stubbed. */ | ||
2155 | return -ENOTSUPP; | ||
2156 | } | ||
2157 | |||
2158 | /* ******************************************************************** */ | ||
2159 | /* WSM callback */ | ||
2160 | void cw1200_suspend_resume(struct cw1200_common *priv, | ||
2161 | struct wsm_suspend_resume *arg) | ||
2162 | { | ||
2163 | pr_debug("[AP] %s: %s\n", | ||
2164 | arg->stop ? "stop" : "start", | ||
2165 | arg->multicast ? "broadcast" : "unicast"); | ||
2166 | |||
2167 | if (arg->multicast) { | ||
2168 | bool cancel_tmo = false; | ||
2169 | spin_lock_bh(&priv->ps_state_lock); | ||
2170 | if (arg->stop) { | ||
2171 | priv->tx_multicast = false; | ||
2172 | } else { | ||
2173 | /* Firmware sends this indication every DTIM if there | ||
2174 | * is a STA in powersave connected. There is no reason | ||
2175 | * to suspend, following wakeup will consume much more | ||
2176 | * power than it could be saved. | ||
2177 | */ | ||
2178 | cw1200_pm_stay_awake(&priv->pm_state, | ||
2179 | priv->join_dtim_period * | ||
2180 | (priv->beacon_int + 20) * HZ / 1024); | ||
2181 | priv->tx_multicast = (priv->aid0_bit_set && | ||
2182 | priv->buffered_multicasts); | ||
2183 | if (priv->tx_multicast) { | ||
2184 | cancel_tmo = true; | ||
2185 | cw1200_bh_wakeup(priv); | ||
2186 | } | ||
2187 | } | ||
2188 | spin_unlock_bh(&priv->ps_state_lock); | ||
2189 | if (cancel_tmo) | ||
2190 | del_timer_sync(&priv->mcast_timeout); | ||
2191 | } else { | ||
2192 | spin_lock_bh(&priv->ps_state_lock); | ||
2193 | cw1200_ps_notify(priv, arg->link_id, arg->stop); | ||
2194 | spin_unlock_bh(&priv->ps_state_lock); | ||
2195 | if (!arg->stop) | ||
2196 | cw1200_bh_wakeup(priv); | ||
2197 | } | ||
2198 | return; | ||
2199 | } | ||
2200 | |||
2201 | /* ******************************************************************** */ | ||
2202 | /* AP privates */ | ||
2203 | |||
2204 | static int cw1200_upload_beacon(struct cw1200_common *priv) | ||
2205 | { | ||
2206 | int ret = 0; | ||
2207 | struct ieee80211_mgmt *mgmt; | ||
2208 | struct wsm_template_frame frame = { | ||
2209 | .frame_type = WSM_FRAME_TYPE_BEACON, | ||
2210 | }; | ||
2211 | |||
2212 | u16 tim_offset; | ||
2213 | u16 tim_len; | ||
2214 | |||
2215 | if (priv->mode == NL80211_IFTYPE_STATION || | ||
2216 | priv->mode == NL80211_IFTYPE_MONITOR || | ||
2217 | priv->mode == NL80211_IFTYPE_UNSPECIFIED) | ||
2218 | goto done; | ||
2219 | |||
2220 | if (priv->vif->p2p) | ||
2221 | frame.rate = WSM_TRANSMIT_RATE_6; | ||
2222 | |||
2223 | frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, | ||
2224 | &tim_offset, &tim_len); | ||
2225 | if (!frame.skb) | ||
2226 | return -ENOMEM; | ||
2227 | |||
2228 | ret = wsm_set_template_frame(priv, &frame); | ||
2229 | |||
2230 | if (ret) | ||
2231 | goto done; | ||
2232 | |||
2233 | /* TODO: Distill probe resp; remove TIM | ||
2234 | * and any other beacon-specific IEs | ||
2235 | */ | ||
2236 | mgmt = (void *)frame.skb->data; | ||
2237 | mgmt->frame_control = | ||
2238 | __cpu_to_le16(IEEE80211_FTYPE_MGMT | | ||
2239 | IEEE80211_STYPE_PROBE_RESP); | ||
2240 | |||
2241 | frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE; | ||
2242 | if (priv->vif->p2p) { | ||
2243 | ret = wsm_set_probe_responder(priv, true); | ||
2244 | } else { | ||
2245 | ret = wsm_set_template_frame(priv, &frame); | ||
2246 | wsm_set_probe_responder(priv, false); | ||
2247 | } | ||
2248 | |||
2249 | done: | ||
2250 | dev_kfree_skb(frame.skb); | ||
2251 | |||
2252 | return ret; | ||
2253 | } | ||
2254 | |||
2255 | static int cw1200_upload_pspoll(struct cw1200_common *priv) | ||
2256 | { | ||
2257 | int ret = 0; | ||
2258 | struct wsm_template_frame frame = { | ||
2259 | .frame_type = WSM_FRAME_TYPE_PS_POLL, | ||
2260 | .rate = 0xFF, | ||
2261 | }; | ||
2262 | |||
2263 | |||
2264 | frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif); | ||
2265 | if (!frame.skb) | ||
2266 | return -ENOMEM; | ||
2267 | |||
2268 | ret = wsm_set_template_frame(priv, &frame); | ||
2269 | |||
2270 | dev_kfree_skb(frame.skb); | ||
2271 | |||
2272 | return ret; | ||
2273 | } | ||
2274 | |||
2275 | static int cw1200_upload_null(struct cw1200_common *priv) | ||
2276 | { | ||
2277 | int ret = 0; | ||
2278 | struct wsm_template_frame frame = { | ||
2279 | .frame_type = WSM_FRAME_TYPE_NULL, | ||
2280 | .rate = 0xFF, | ||
2281 | }; | ||
2282 | |||
2283 | frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif); | ||
2284 | if (!frame.skb) | ||
2285 | return -ENOMEM; | ||
2286 | |||
2287 | ret = wsm_set_template_frame(priv, &frame); | ||
2288 | |||
2289 | dev_kfree_skb(frame.skb); | ||
2290 | |||
2291 | return ret; | ||
2292 | } | ||
2293 | |||
2294 | static int cw1200_upload_qosnull(struct cw1200_common *priv) | ||
2295 | { | ||
2296 | int ret = 0; | ||
2297 | /* TODO: This needs to be implemented | ||
2298 | |||
2299 | struct wsm_template_frame frame = { | ||
2300 | .frame_type = WSM_FRAME_TYPE_QOS_NULL, | ||
2301 | .rate = 0xFF, | ||
2302 | }; | ||
2303 | |||
2304 | frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif); | ||
2305 | if (!frame.skb) | ||
2306 | return -ENOMEM; | ||
2307 | |||
2308 | ret = wsm_set_template_frame(priv, &frame); | ||
2309 | |||
2310 | dev_kfree_skb(frame.skb); | ||
2311 | |||
2312 | */ | ||
2313 | return ret; | ||
2314 | } | ||
2315 | |||
2316 | static int cw1200_enable_beaconing(struct cw1200_common *priv, | ||
2317 | bool enable) | ||
2318 | { | ||
2319 | struct wsm_beacon_transmit transmit = { | ||
2320 | .enable_beaconing = enable, | ||
2321 | }; | ||
2322 | |||
2323 | return wsm_beacon_transmit(priv, &transmit); | ||
2324 | } | ||
2325 | |||
2326 | static int cw1200_start_ap(struct cw1200_common *priv) | ||
2327 | { | ||
2328 | int ret; | ||
2329 | struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; | ||
2330 | struct wsm_start start = { | ||
2331 | .mode = priv->vif->p2p ? | ||
2332 | WSM_START_MODE_P2P_GO : WSM_START_MODE_AP, | ||
2333 | .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? | ||
2334 | WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, | ||
2335 | .channel_number = priv->channel->hw_value, | ||
2336 | .beacon_interval = conf->beacon_int, | ||
2337 | .dtim_period = conf->dtim_period, | ||
2338 | .preamble = conf->use_short_preamble ? | ||
2339 | WSM_JOIN_PREAMBLE_SHORT : | ||
2340 | WSM_JOIN_PREAMBLE_LONG, | ||
2341 | .probe_delay = 100, | ||
2342 | .basic_rate_set = cw1200_rate_mask_to_wsm(priv, | ||
2343 | conf->basic_rates), | ||
2344 | }; | ||
2345 | struct wsm_operational_mode mode = { | ||
2346 | .power_mode = cw1200_power_mode, | ||
2347 | .disable_more_flag_usage = true, | ||
2348 | }; | ||
2349 | |||
2350 | memset(start.ssid, 0, sizeof(start.ssid)); | ||
2351 | if (!conf->hidden_ssid) { | ||
2352 | start.ssid_len = conf->ssid_len; | ||
2353 | memcpy(start.ssid, conf->ssid, start.ssid_len); | ||
2354 | } | ||
2355 | |||
2356 | priv->beacon_int = conf->beacon_int; | ||
2357 | priv->join_dtim_period = conf->dtim_period; | ||
2358 | |||
2359 | memset(&priv->link_id_db, 0, sizeof(priv->link_id_db)); | ||
2360 | |||
2361 | pr_debug("[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n", | ||
2362 | start.channel_number, start.band, | ||
2363 | start.beacon_interval, start.dtim_period, | ||
2364 | start.basic_rate_set, | ||
2365 | start.ssid_len, start.ssid); | ||
2366 | ret = wsm_start(priv, &start); | ||
2367 | if (!ret) | ||
2368 | ret = cw1200_upload_keys(priv); | ||
2369 | if (!ret && priv->vif->p2p) { | ||
2370 | pr_debug("[AP] Setting p2p powersave configuration.\n"); | ||
2371 | wsm_set_p2p_ps_modeinfo(priv, &priv->p2p_ps_modeinfo); | ||
2372 | } | ||
2373 | if (!ret) { | ||
2374 | wsm_set_block_ack_policy(priv, 0, 0); | ||
2375 | priv->join_status = CW1200_JOIN_STATUS_AP; | ||
2376 | cw1200_update_filtering(priv); | ||
2377 | } | ||
2378 | wsm_set_operational_mode(priv, &mode); | ||
2379 | return ret; | ||
2380 | } | ||
2381 | |||
2382 | static int cw1200_update_beaconing(struct cw1200_common *priv) | ||
2383 | { | ||
2384 | struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; | ||
2385 | struct wsm_reset reset = { | ||
2386 | .link_id = 0, | ||
2387 | .reset_statistics = true, | ||
2388 | }; | ||
2389 | |||
2390 | if (priv->mode == NL80211_IFTYPE_AP) { | ||
2391 | /* TODO: check if changed channel, band */ | ||
2392 | if (priv->join_status != CW1200_JOIN_STATUS_AP || | ||
2393 | priv->beacon_int != conf->beacon_int) { | ||
2394 | pr_debug("ap restarting\n"); | ||
2395 | wsm_lock_tx(priv); | ||
2396 | if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE) | ||
2397 | wsm_reset(priv, &reset); | ||
2398 | priv->join_status = CW1200_JOIN_STATUS_PASSIVE; | ||
2399 | cw1200_start_ap(priv); | ||
2400 | wsm_unlock_tx(priv); | ||
2401 | } else | ||
2402 | pr_debug("ap started join_status: %d\n", | ||
2403 | priv->join_status); | ||
2404 | } | ||
2405 | return 0; | ||
2406 | } | ||
diff --git a/drivers/net/wireless/cw1200/sta.h b/drivers/net/wireless/cw1200/sta.h new file mode 100644 index 000000000000..35babb62cc6a --- /dev/null +++ b/drivers/net/wireless/cw1200/sta.h | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | * Mac80211 STA interface for ST-Ericsson CW1200 mac80211 drivers | ||
3 | * | ||
4 | * Copyright (c) 2010, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef STA_H_INCLUDED | ||
13 | #define STA_H_INCLUDED | ||
14 | |||
15 | /* ******************************************************************** */ | ||
16 | /* mac80211 API */ | ||
17 | |||
18 | int cw1200_start(struct ieee80211_hw *dev); | ||
19 | void cw1200_stop(struct ieee80211_hw *dev); | ||
20 | int cw1200_add_interface(struct ieee80211_hw *dev, | ||
21 | struct ieee80211_vif *vif); | ||
22 | void cw1200_remove_interface(struct ieee80211_hw *dev, | ||
23 | struct ieee80211_vif *vif); | ||
24 | int cw1200_change_interface(struct ieee80211_hw *dev, | ||
25 | struct ieee80211_vif *vif, | ||
26 | enum nl80211_iftype new_type, | ||
27 | bool p2p); | ||
28 | int cw1200_config(struct ieee80211_hw *dev, u32 changed); | ||
29 | void cw1200_configure_filter(struct ieee80211_hw *dev, | ||
30 | unsigned int changed_flags, | ||
31 | unsigned int *total_flags, | ||
32 | u64 multicast); | ||
33 | int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, | ||
34 | u16 queue, const struct ieee80211_tx_queue_params *params); | ||
35 | int cw1200_get_stats(struct ieee80211_hw *dev, | ||
36 | struct ieee80211_low_level_stats *stats); | ||
37 | int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, | ||
38 | struct ieee80211_vif *vif, struct ieee80211_sta *sta, | ||
39 | struct ieee80211_key_conf *key); | ||
40 | |||
41 | int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value); | ||
42 | |||
43 | void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop); | ||
44 | |||
45 | u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, | ||
46 | struct netdev_hw_addr_list *mc_list); | ||
47 | |||
48 | int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg); | ||
49 | |||
50 | /* ******************************************************************** */ | ||
51 | /* WSM callbacks */ | ||
52 | |||
53 | void cw1200_join_complete_cb(struct cw1200_common *priv, | ||
54 | struct wsm_join_complete *arg); | ||
55 | |||
56 | /* ******************************************************************** */ | ||
57 | /* WSM events */ | ||
58 | |||
59 | void cw1200_free_event_queue(struct cw1200_common *priv); | ||
60 | void cw1200_event_handler(struct work_struct *work); | ||
61 | void cw1200_bss_loss_work(struct work_struct *work); | ||
62 | void cw1200_bss_params_work(struct work_struct *work); | ||
63 | void cw1200_keep_alive_work(struct work_struct *work); | ||
64 | void cw1200_tx_failure_work(struct work_struct *work); | ||
65 | |||
66 | void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, int init, int good, | ||
67 | int bad); | ||
68 | static inline void cw1200_cqm_bssloss_sm(struct cw1200_common *priv, | ||
69 | int init, int good, int bad) | ||
70 | { | ||
71 | spin_lock(&priv->bss_loss_lock); | ||
72 | __cw1200_cqm_bssloss_sm(priv, init, good, bad); | ||
73 | spin_unlock(&priv->bss_loss_lock); | ||
74 | } | ||
75 | |||
76 | /* ******************************************************************** */ | ||
77 | /* Internal API */ | ||
78 | |||
79 | int cw1200_setup_mac(struct cw1200_common *priv); | ||
80 | void cw1200_join_timeout(struct work_struct *work); | ||
81 | void cw1200_unjoin_work(struct work_struct *work); | ||
82 | void cw1200_join_complete_work(struct work_struct *work); | ||
83 | void cw1200_wep_key_work(struct work_struct *work); | ||
84 | void cw1200_update_listening(struct cw1200_common *priv, bool enabled); | ||
85 | void cw1200_update_filtering(struct cw1200_common *priv); | ||
86 | void cw1200_update_filtering_work(struct work_struct *work); | ||
87 | void cw1200_set_beacon_wakeup_period_work(struct work_struct *work); | ||
88 | int cw1200_enable_listening(struct cw1200_common *priv); | ||
89 | int cw1200_disable_listening(struct cw1200_common *priv); | ||
90 | int cw1200_set_uapsd_param(struct cw1200_common *priv, | ||
91 | const struct wsm_edca_params *arg); | ||
92 | void cw1200_ba_work(struct work_struct *work); | ||
93 | void cw1200_ba_timer(unsigned long arg); | ||
94 | |||
95 | /* AP stuffs */ | ||
96 | int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, | ||
97 | bool set); | ||
98 | int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | ||
99 | struct ieee80211_sta *sta); | ||
100 | int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | ||
101 | struct ieee80211_sta *sta); | ||
102 | void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, | ||
103 | enum sta_notify_cmd notify_cmd, | ||
104 | struct ieee80211_sta *sta); | ||
105 | void cw1200_bss_info_changed(struct ieee80211_hw *dev, | ||
106 | struct ieee80211_vif *vif, | ||
107 | struct ieee80211_bss_conf *info, | ||
108 | u32 changed); | ||
109 | int cw1200_ampdu_action(struct ieee80211_hw *hw, | ||
110 | struct ieee80211_vif *vif, | ||
111 | enum ieee80211_ampdu_mlme_action action, | ||
112 | struct ieee80211_sta *sta, u16 tid, u16 *ssn, | ||
113 | u8 buf_size); | ||
114 | |||
115 | void cw1200_suspend_resume(struct cw1200_common *priv, | ||
116 | struct wsm_suspend_resume *arg); | ||
117 | void cw1200_set_tim_work(struct work_struct *work); | ||
118 | void cw1200_set_cts_work(struct work_struct *work); | ||
119 | void cw1200_multicast_start_work(struct work_struct *work); | ||
120 | void cw1200_multicast_stop_work(struct work_struct *work); | ||
121 | void cw1200_mcast_timeout(unsigned long arg); | ||
122 | |||
123 | #endif | ||
diff --git a/drivers/net/wireless/cw1200/txrx.c b/drivers/net/wireless/cw1200/txrx.c new file mode 100644 index 000000000000..0e40890e1993 --- /dev/null +++ b/drivers/net/wireless/cw1200/txrx.c | |||
@@ -0,0 +1,1474 @@ | |||
1 | /* | ||
2 | * Datapath implementation for ST-Ericsson CW1200 mac80211 drivers | ||
3 | * | ||
4 | * Copyright (c) 2010, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <net/mac80211.h> | ||
13 | #include <linux/etherdevice.h> | ||
14 | #include <linux/skbuff.h> | ||
15 | |||
16 | #include "cw1200.h" | ||
17 | #include "wsm.h" | ||
18 | #include "bh.h" | ||
19 | #include "sta.h" | ||
20 | #include "debug.h" | ||
21 | |||
22 | #define CW1200_INVALID_RATE_ID (0xFF) | ||
23 | |||
24 | static int cw1200_handle_action_rx(struct cw1200_common *priv, | ||
25 | struct sk_buff *skb); | ||
26 | static const struct ieee80211_rate * | ||
27 | cw1200_get_tx_rate(const struct cw1200_common *priv, | ||
28 | const struct ieee80211_tx_rate *rate); | ||
29 | |||
30 | /* ******************************************************************** */ | ||
31 | /* TX queue lock / unlock */ | ||
32 | |||
33 | static inline void cw1200_tx_queues_lock(struct cw1200_common *priv) | ||
34 | { | ||
35 | int i; | ||
36 | for (i = 0; i < 4; ++i) | ||
37 | cw1200_queue_lock(&priv->tx_queue[i]); | ||
38 | } | ||
39 | |||
40 | static inline void cw1200_tx_queues_unlock(struct cw1200_common *priv) | ||
41 | { | ||
42 | int i; | ||
43 | for (i = 0; i < 4; ++i) | ||
44 | cw1200_queue_unlock(&priv->tx_queue[i]); | ||
45 | } | ||
46 | |||
47 | /* ******************************************************************** */ | ||
48 | /* TX policy cache implementation */ | ||
49 | |||
50 | static void tx_policy_dump(struct tx_policy *policy) | ||
51 | { | ||
52 | pr_debug("[TX policy] %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n", | ||
53 | policy->raw[0] & 0x0F, policy->raw[0] >> 4, | ||
54 | policy->raw[1] & 0x0F, policy->raw[1] >> 4, | ||
55 | policy->raw[2] & 0x0F, policy->raw[2] >> 4, | ||
56 | policy->raw[3] & 0x0F, policy->raw[3] >> 4, | ||
57 | policy->raw[4] & 0x0F, policy->raw[4] >> 4, | ||
58 | policy->raw[5] & 0x0F, policy->raw[5] >> 4, | ||
59 | policy->raw[6] & 0x0F, policy->raw[6] >> 4, | ||
60 | policy->raw[7] & 0x0F, policy->raw[7] >> 4, | ||
61 | policy->raw[8] & 0x0F, policy->raw[8] >> 4, | ||
62 | policy->raw[9] & 0x0F, policy->raw[9] >> 4, | ||
63 | policy->raw[10] & 0x0F, policy->raw[10] >> 4, | ||
64 | policy->raw[11] & 0x0F, policy->raw[11] >> 4, | ||
65 | policy->defined); | ||
66 | } | ||
67 | |||
68 | static void tx_policy_build(const struct cw1200_common *priv, | ||
69 | /* [out] */ struct tx_policy *policy, | ||
70 | struct ieee80211_tx_rate *rates, size_t count) | ||
71 | { | ||
72 | int i, j; | ||
73 | unsigned limit = priv->short_frame_max_tx_count; | ||
74 | unsigned total = 0; | ||
75 | BUG_ON(rates[0].idx < 0); | ||
76 | memset(policy, 0, sizeof(*policy)); | ||
77 | |||
78 | /* minstrel is buggy a little bit, so distille | ||
79 | * incoming rates first. */ | ||
80 | |||
81 | /* Sort rates in descending order. */ | ||
82 | for (i = 1; i < count; ++i) { | ||
83 | if (rates[i].idx < 0) { | ||
84 | count = i; | ||
85 | break; | ||
86 | } | ||
87 | if (rates[i].idx > rates[i - 1].idx) { | ||
88 | struct ieee80211_tx_rate tmp = rates[i - 1]; | ||
89 | rates[i - 1] = rates[i]; | ||
90 | rates[i] = tmp; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | /* Eliminate duplicates. */ | ||
95 | total = rates[0].count; | ||
96 | for (i = 0, j = 1; j < count; ++j) { | ||
97 | if (rates[j].idx == rates[i].idx) { | ||
98 | rates[i].count += rates[j].count; | ||
99 | } else if (rates[j].idx > rates[i].idx) { | ||
100 | break; | ||
101 | } else { | ||
102 | ++i; | ||
103 | if (i != j) | ||
104 | rates[i] = rates[j]; | ||
105 | } | ||
106 | total += rates[j].count; | ||
107 | } | ||
108 | count = i + 1; | ||
109 | |||
110 | /* Re-fill policy trying to keep every requested rate and with | ||
111 | * respect to the global max tx retransmission count. */ | ||
112 | if (limit < count) | ||
113 | limit = count; | ||
114 | if (total > limit) { | ||
115 | for (i = 0; i < count; ++i) { | ||
116 | int left = count - i - 1; | ||
117 | if (rates[i].count > limit - left) | ||
118 | rates[i].count = limit - left; | ||
119 | limit -= rates[i].count; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | /* HACK!!! Device has problems (at least) switching from | ||
124 | * 54Mbps CTS to 1Mbps. This switch takes enormous amount | ||
125 | * of time (100-200 ms), leading to valuable throughput drop. | ||
126 | * As a workaround, additional g-rates are injected to the | ||
127 | * policy. | ||
128 | */ | ||
129 | if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) && | ||
130 | rates[0].idx > 4 && rates[0].count > 2 && | ||
131 | rates[1].idx < 2) { | ||
132 | /* ">> 1" is an equivalent of "/ 2", but faster */ | ||
133 | int mid_rate = (rates[0].idx + 4) >> 1; | ||
134 | |||
135 | /* Decrease number of retries for the initial rate */ | ||
136 | rates[0].count -= 2; | ||
137 | |||
138 | if (mid_rate != 4) { | ||
139 | /* Keep fallback rate at 1Mbps. */ | ||
140 | rates[3] = rates[1]; | ||
141 | |||
142 | /* Inject 1 transmission on lowest g-rate */ | ||
143 | rates[2].idx = 4; | ||
144 | rates[2].count = 1; | ||
145 | rates[2].flags = rates[1].flags; | ||
146 | |||
147 | /* Inject 1 transmission on mid-rate */ | ||
148 | rates[1].idx = mid_rate; | ||
149 | rates[1].count = 1; | ||
150 | |||
151 | /* Fallback to 1 Mbps is a really bad thing, | ||
152 | * so let's try to increase probability of | ||
153 | * successful transmission on the lowest g rate | ||
154 | * even more */ | ||
155 | if (rates[0].count >= 3) { | ||
156 | --rates[0].count; | ||
157 | ++rates[2].count; | ||
158 | } | ||
159 | |||
160 | /* Adjust amount of rates defined */ | ||
161 | count += 2; | ||
162 | } else { | ||
163 | /* Keep fallback rate at 1Mbps. */ | ||
164 | rates[2] = rates[1]; | ||
165 | |||
166 | /* Inject 2 transmissions on lowest g-rate */ | ||
167 | rates[1].idx = 4; | ||
168 | rates[1].count = 2; | ||
169 | |||
170 | /* Adjust amount of rates defined */ | ||
171 | count += 1; | ||
172 | } | ||
173 | } | ||
174 | |||
175 | policy->defined = cw1200_get_tx_rate(priv, &rates[0])->hw_value + 1; | ||
176 | |||
177 | for (i = 0; i < count; ++i) { | ||
178 | register unsigned rateid, off, shift, retries; | ||
179 | |||
180 | rateid = cw1200_get_tx_rate(priv, &rates[i])->hw_value; | ||
181 | off = rateid >> 3; /* eq. rateid / 8 */ | ||
182 | shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */ | ||
183 | |||
184 | retries = rates[i].count; | ||
185 | if (retries > 0x0F) { | ||
186 | rates[i].count = 0x0f; | ||
187 | retries = 0x0F; | ||
188 | } | ||
189 | policy->tbl[off] |= __cpu_to_le32(retries << shift); | ||
190 | policy->retry_count += retries; | ||
191 | } | ||
192 | |||
193 | pr_debug("[TX policy] Policy (%zu): %d:%d, %d:%d, %d:%d, %d:%d, %d:%d\n", | ||
194 | count, | ||
195 | rates[0].idx, rates[0].count, | ||
196 | rates[1].idx, rates[1].count, | ||
197 | rates[2].idx, rates[2].count, | ||
198 | rates[3].idx, rates[3].count, | ||
199 | rates[4].idx, rates[4].count); | ||
200 | } | ||
201 | |||
202 | static inline bool tx_policy_is_equal(const struct tx_policy *wanted, | ||
203 | const struct tx_policy *cached) | ||
204 | { | ||
205 | size_t count = wanted->defined >> 1; | ||
206 | if (wanted->defined > cached->defined) | ||
207 | return false; | ||
208 | if (count) { | ||
209 | if (memcmp(wanted->raw, cached->raw, count)) | ||
210 | return false; | ||
211 | } | ||
212 | if (wanted->defined & 1) { | ||
213 | if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F)) | ||
214 | return false; | ||
215 | } | ||
216 | return true; | ||
217 | } | ||
218 | |||
219 | static int tx_policy_find(struct tx_policy_cache *cache, | ||
220 | const struct tx_policy *wanted) | ||
221 | { | ||
222 | /* O(n) complexity. Not so good, but there's only 8 entries in | ||
223 | * the cache. | ||
224 | * Also lru helps to reduce search time. */ | ||
225 | struct tx_policy_cache_entry *it; | ||
226 | /* First search for policy in "used" list */ | ||
227 | list_for_each_entry(it, &cache->used, link) { | ||
228 | if (tx_policy_is_equal(wanted, &it->policy)) | ||
229 | return it - cache->cache; | ||
230 | } | ||
231 | /* Then - in "free list" */ | ||
232 | list_for_each_entry(it, &cache->free, link) { | ||
233 | if (tx_policy_is_equal(wanted, &it->policy)) | ||
234 | return it - cache->cache; | ||
235 | } | ||
236 | return -1; | ||
237 | } | ||
238 | |||
239 | static inline void tx_policy_use(struct tx_policy_cache *cache, | ||
240 | struct tx_policy_cache_entry *entry) | ||
241 | { | ||
242 | ++entry->policy.usage_count; | ||
243 | list_move(&entry->link, &cache->used); | ||
244 | } | ||
245 | |||
246 | static inline int tx_policy_release(struct tx_policy_cache *cache, | ||
247 | struct tx_policy_cache_entry *entry) | ||
248 | { | ||
249 | int ret = --entry->policy.usage_count; | ||
250 | if (!ret) | ||
251 | list_move(&entry->link, &cache->free); | ||
252 | return ret; | ||
253 | } | ||
254 | |||
255 | void tx_policy_clean(struct cw1200_common *priv) | ||
256 | { | ||
257 | int idx, locked; | ||
258 | struct tx_policy_cache *cache = &priv->tx_policy_cache; | ||
259 | struct tx_policy_cache_entry *entry; | ||
260 | |||
261 | cw1200_tx_queues_lock(priv); | ||
262 | spin_lock_bh(&cache->lock); | ||
263 | locked = list_empty(&cache->free); | ||
264 | |||
265 | for (idx = 0; idx < TX_POLICY_CACHE_SIZE; idx++) { | ||
266 | entry = &cache->cache[idx]; | ||
267 | /* Policy usage count should be 0 at this time as all queues | ||
268 | should be empty */ | ||
269 | if (WARN_ON(entry->policy.usage_count)) { | ||
270 | entry->policy.usage_count = 0; | ||
271 | list_move(&entry->link, &cache->free); | ||
272 | } | ||
273 | memset(&entry->policy, 0, sizeof(entry->policy)); | ||
274 | } | ||
275 | if (locked) | ||
276 | cw1200_tx_queues_unlock(priv); | ||
277 | |||
278 | cw1200_tx_queues_unlock(priv); | ||
279 | spin_unlock_bh(&cache->lock); | ||
280 | } | ||
281 | |||
282 | /* ******************************************************************** */ | ||
283 | /* External TX policy cache API */ | ||
284 | |||
285 | void tx_policy_init(struct cw1200_common *priv) | ||
286 | { | ||
287 | struct tx_policy_cache *cache = &priv->tx_policy_cache; | ||
288 | int i; | ||
289 | |||
290 | memset(cache, 0, sizeof(*cache)); | ||
291 | |||
292 | spin_lock_init(&cache->lock); | ||
293 | INIT_LIST_HEAD(&cache->used); | ||
294 | INIT_LIST_HEAD(&cache->free); | ||
295 | |||
296 | for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) | ||
297 | list_add(&cache->cache[i].link, &cache->free); | ||
298 | } | ||
299 | |||
300 | static int tx_policy_get(struct cw1200_common *priv, | ||
301 | struct ieee80211_tx_rate *rates, | ||
302 | size_t count, bool *renew) | ||
303 | { | ||
304 | int idx; | ||
305 | struct tx_policy_cache *cache = &priv->tx_policy_cache; | ||
306 | struct tx_policy wanted; | ||
307 | |||
308 | tx_policy_build(priv, &wanted, rates, count); | ||
309 | |||
310 | spin_lock_bh(&cache->lock); | ||
311 | if (WARN_ON_ONCE(list_empty(&cache->free))) { | ||
312 | spin_unlock_bh(&cache->lock); | ||
313 | return CW1200_INVALID_RATE_ID; | ||
314 | } | ||
315 | idx = tx_policy_find(cache, &wanted); | ||
316 | if (idx >= 0) { | ||
317 | pr_debug("[TX policy] Used TX policy: %d\n", idx); | ||
318 | *renew = false; | ||
319 | } else { | ||
320 | struct tx_policy_cache_entry *entry; | ||
321 | *renew = true; | ||
322 | /* If policy is not found create a new one | ||
323 | * using the oldest entry in "free" list */ | ||
324 | entry = list_entry(cache->free.prev, | ||
325 | struct tx_policy_cache_entry, link); | ||
326 | entry->policy = wanted; | ||
327 | idx = entry - cache->cache; | ||
328 | pr_debug("[TX policy] New TX policy: %d\n", idx); | ||
329 | tx_policy_dump(&entry->policy); | ||
330 | } | ||
331 | tx_policy_use(cache, &cache->cache[idx]); | ||
332 | if (list_empty(&cache->free)) { | ||
333 | /* Lock TX queues. */ | ||
334 | cw1200_tx_queues_lock(priv); | ||
335 | } | ||
336 | spin_unlock_bh(&cache->lock); | ||
337 | return idx; | ||
338 | } | ||
339 | |||
340 | static void tx_policy_put(struct cw1200_common *priv, int idx) | ||
341 | { | ||
342 | int usage, locked; | ||
343 | struct tx_policy_cache *cache = &priv->tx_policy_cache; | ||
344 | |||
345 | spin_lock_bh(&cache->lock); | ||
346 | locked = list_empty(&cache->free); | ||
347 | usage = tx_policy_release(cache, &cache->cache[idx]); | ||
348 | if (locked && !usage) { | ||
349 | /* Unlock TX queues. */ | ||
350 | cw1200_tx_queues_unlock(priv); | ||
351 | } | ||
352 | spin_unlock_bh(&cache->lock); | ||
353 | } | ||
354 | |||
355 | static int tx_policy_upload(struct cw1200_common *priv) | ||
356 | { | ||
357 | struct tx_policy_cache *cache = &priv->tx_policy_cache; | ||
358 | int i; | ||
359 | struct wsm_set_tx_rate_retry_policy arg = { | ||
360 | .num = 0, | ||
361 | }; | ||
362 | spin_lock_bh(&cache->lock); | ||
363 | |||
364 | /* Upload only modified entries. */ | ||
365 | for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) { | ||
366 | struct tx_policy *src = &cache->cache[i].policy; | ||
367 | if (src->retry_count && !src->uploaded) { | ||
368 | struct wsm_tx_rate_retry_policy *dst = | ||
369 | &arg.tbl[arg.num]; | ||
370 | dst->index = i; | ||
371 | dst->short_retries = priv->short_frame_max_tx_count; | ||
372 | dst->long_retries = priv->long_frame_max_tx_count; | ||
373 | |||
374 | dst->flags = WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED | | ||
375 | WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT; | ||
376 | memcpy(dst->rate_count_indices, src->tbl, | ||
377 | sizeof(dst->rate_count_indices)); | ||
378 | src->uploaded = 1; | ||
379 | ++arg.num; | ||
380 | } | ||
381 | } | ||
382 | spin_unlock_bh(&cache->lock); | ||
383 | cw1200_debug_tx_cache_miss(priv); | ||
384 | pr_debug("[TX policy] Upload %d policies\n", arg.num); | ||
385 | return wsm_set_tx_rate_retry_policy(priv, &arg); | ||
386 | } | ||
387 | |||
388 | void tx_policy_upload_work(struct work_struct *work) | ||
389 | { | ||
390 | struct cw1200_common *priv = | ||
391 | container_of(work, struct cw1200_common, tx_policy_upload_work); | ||
392 | |||
393 | pr_debug("[TX] TX policy upload.\n"); | ||
394 | tx_policy_upload(priv); | ||
395 | |||
396 | wsm_unlock_tx(priv); | ||
397 | cw1200_tx_queues_unlock(priv); | ||
398 | } | ||
399 | |||
400 | /* ******************************************************************** */ | ||
401 | /* cw1200 TX implementation */ | ||
402 | |||
403 | struct cw1200_txinfo { | ||
404 | struct sk_buff *skb; | ||
405 | unsigned queue; | ||
406 | struct ieee80211_tx_info *tx_info; | ||
407 | const struct ieee80211_rate *rate; | ||
408 | struct ieee80211_hdr *hdr; | ||
409 | size_t hdrlen; | ||
410 | const u8 *da; | ||
411 | struct cw1200_sta_priv *sta_priv; | ||
412 | struct ieee80211_sta *sta; | ||
413 | struct cw1200_txpriv txpriv; | ||
414 | }; | ||
415 | |||
416 | u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates) | ||
417 | { | ||
418 | u32 ret = 0; | ||
419 | int i; | ||
420 | for (i = 0; i < 32; ++i) { | ||
421 | if (rates & BIT(i)) | ||
422 | ret |= BIT(priv->rates[i].hw_value); | ||
423 | } | ||
424 | return ret; | ||
425 | } | ||
426 | |||
427 | static const struct ieee80211_rate * | ||
428 | cw1200_get_tx_rate(const struct cw1200_common *priv, | ||
429 | const struct ieee80211_tx_rate *rate) | ||
430 | { | ||
431 | if (rate->idx < 0) | ||
432 | return NULL; | ||
433 | if (rate->flags & IEEE80211_TX_RC_MCS) | ||
434 | return &priv->mcs_rates[rate->idx]; | ||
435 | return &priv->hw->wiphy->bands[priv->channel->band]-> | ||
436 | bitrates[rate->idx]; | ||
437 | } | ||
438 | |||
439 | static int | ||
440 | cw1200_tx_h_calc_link_ids(struct cw1200_common *priv, | ||
441 | struct cw1200_txinfo *t) | ||
442 | { | ||
443 | if (t->sta && t->sta_priv->link_id) | ||
444 | t->txpriv.raw_link_id = | ||
445 | t->txpriv.link_id = | ||
446 | t->sta_priv->link_id; | ||
447 | else if (priv->mode != NL80211_IFTYPE_AP) | ||
448 | t->txpriv.raw_link_id = | ||
449 | t->txpriv.link_id = 0; | ||
450 | else if (is_multicast_ether_addr(t->da)) { | ||
451 | if (priv->enable_beacon) { | ||
452 | t->txpriv.raw_link_id = 0; | ||
453 | t->txpriv.link_id = CW1200_LINK_ID_AFTER_DTIM; | ||
454 | } else { | ||
455 | t->txpriv.raw_link_id = 0; | ||
456 | t->txpriv.link_id = 0; | ||
457 | } | ||
458 | } else { | ||
459 | t->txpriv.link_id = cw1200_find_link_id(priv, t->da); | ||
460 | if (!t->txpriv.link_id) | ||
461 | t->txpriv.link_id = cw1200_alloc_link_id(priv, t->da); | ||
462 | if (!t->txpriv.link_id) { | ||
463 | wiphy_err(priv->hw->wiphy, | ||
464 | "No more link IDs available.\n"); | ||
465 | return -ENOENT; | ||
466 | } | ||
467 | t->txpriv.raw_link_id = t->txpriv.link_id; | ||
468 | } | ||
469 | if (t->txpriv.raw_link_id) | ||
470 | priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp = | ||
471 | jiffies; | ||
472 | if (t->sta && (t->sta->uapsd_queues & BIT(t->queue))) | ||
473 | t->txpriv.link_id = CW1200_LINK_ID_UAPSD; | ||
474 | return 0; | ||
475 | } | ||
476 | |||
477 | static void | ||
478 | cw1200_tx_h_pm(struct cw1200_common *priv, | ||
479 | struct cw1200_txinfo *t) | ||
480 | { | ||
481 | if (ieee80211_is_auth(t->hdr->frame_control)) { | ||
482 | u32 mask = ~BIT(t->txpriv.raw_link_id); | ||
483 | spin_lock_bh(&priv->ps_state_lock); | ||
484 | priv->sta_asleep_mask &= mask; | ||
485 | priv->pspoll_mask &= mask; | ||
486 | spin_unlock_bh(&priv->ps_state_lock); | ||
487 | } | ||
488 | } | ||
489 | |||
490 | static void | ||
491 | cw1200_tx_h_calc_tid(struct cw1200_common *priv, | ||
492 | struct cw1200_txinfo *t) | ||
493 | { | ||
494 | if (ieee80211_is_data_qos(t->hdr->frame_control)) { | ||
495 | u8 *qos = ieee80211_get_qos_ctl(t->hdr); | ||
496 | t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK; | ||
497 | } else if (ieee80211_is_data(t->hdr->frame_control)) { | ||
498 | t->txpriv.tid = 0; | ||
499 | } | ||
500 | } | ||
501 | |||
502 | static int | ||
503 | cw1200_tx_h_crypt(struct cw1200_common *priv, | ||
504 | struct cw1200_txinfo *t) | ||
505 | { | ||
506 | if (!t->tx_info->control.hw_key || | ||
507 | !ieee80211_has_protected(t->hdr->frame_control)) | ||
508 | return 0; | ||
509 | |||
510 | t->hdrlen += t->tx_info->control.hw_key->iv_len; | ||
511 | skb_put(t->skb, t->tx_info->control.hw_key->icv_len); | ||
512 | |||
513 | if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) | ||
514 | skb_put(t->skb, 8); /* MIC space */ | ||
515 | |||
516 | return 0; | ||
517 | } | ||
518 | |||
519 | static int | ||
520 | cw1200_tx_h_align(struct cw1200_common *priv, | ||
521 | struct cw1200_txinfo *t, | ||
522 | u8 *flags) | ||
523 | { | ||
524 | size_t offset = (size_t)t->skb->data & 3; | ||
525 | |||
526 | if (!offset) | ||
527 | return 0; | ||
528 | |||
529 | if (offset & 1) { | ||
530 | wiphy_err(priv->hw->wiphy, | ||
531 | "Bug: attempt to transmit a frame with wrong alignment: %zu\n", | ||
532 | offset); | ||
533 | return -EINVAL; | ||
534 | } | ||
535 | |||
536 | if (skb_headroom(t->skb) < offset) { | ||
537 | wiphy_err(priv->hw->wiphy, | ||
538 | "Bug: no space allocated for DMA alignment. headroom: %d\n", | ||
539 | skb_headroom(t->skb)); | ||
540 | return -ENOMEM; | ||
541 | } | ||
542 | skb_push(t->skb, offset); | ||
543 | t->hdrlen += offset; | ||
544 | t->txpriv.offset += offset; | ||
545 | *flags |= WSM_TX_2BYTES_SHIFT; | ||
546 | cw1200_debug_tx_align(priv); | ||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | static int | ||
551 | cw1200_tx_h_action(struct cw1200_common *priv, | ||
552 | struct cw1200_txinfo *t) | ||
553 | { | ||
554 | struct ieee80211_mgmt *mgmt = | ||
555 | (struct ieee80211_mgmt *)t->hdr; | ||
556 | if (ieee80211_is_action(t->hdr->frame_control) && | ||
557 | mgmt->u.action.category == WLAN_CATEGORY_BACK) | ||
558 | return 1; | ||
559 | else | ||
560 | return 0; | ||
561 | } | ||
562 | |||
563 | /* Add WSM header */ | ||
564 | static struct wsm_tx * | ||
565 | cw1200_tx_h_wsm(struct cw1200_common *priv, | ||
566 | struct cw1200_txinfo *t) | ||
567 | { | ||
568 | struct wsm_tx *wsm; | ||
569 | |||
570 | if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) { | ||
571 | wiphy_err(priv->hw->wiphy, | ||
572 | "Bug: no space allocated for WSM header. headroom: %d\n", | ||
573 | skb_headroom(t->skb)); | ||
574 | return NULL; | ||
575 | } | ||
576 | |||
577 | wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx)); | ||
578 | t->txpriv.offset += sizeof(struct wsm_tx); | ||
579 | memset(wsm, 0, sizeof(*wsm)); | ||
580 | wsm->hdr.len = __cpu_to_le16(t->skb->len); | ||
581 | wsm->hdr.id = __cpu_to_le16(0x0004); | ||
582 | wsm->queue_id = wsm_queue_id_to_wsm(t->queue); | ||
583 | return wsm; | ||
584 | } | ||
585 | |||
586 | /* BT Coex specific handling */ | ||
587 | static void | ||
588 | cw1200_tx_h_bt(struct cw1200_common *priv, | ||
589 | struct cw1200_txinfo *t, | ||
590 | struct wsm_tx *wsm) | ||
591 | { | ||
592 | u8 priority = 0; | ||
593 | |||
594 | if (!priv->bt_present) | ||
595 | return; | ||
596 | |||
597 | if (ieee80211_is_nullfunc(t->hdr->frame_control)) { | ||
598 | priority = WSM_EPTA_PRIORITY_MGT; | ||
599 | } else if (ieee80211_is_data(t->hdr->frame_control)) { | ||
600 | /* Skip LLC SNAP header (+6) */ | ||
601 | u8 *payload = &t->skb->data[t->hdrlen]; | ||
602 | u16 *ethertype = (u16 *)&payload[6]; | ||
603 | if (*ethertype == __be16_to_cpu(ETH_P_PAE)) | ||
604 | priority = WSM_EPTA_PRIORITY_EAPOL; | ||
605 | } else if (ieee80211_is_assoc_req(t->hdr->frame_control) || | ||
606 | ieee80211_is_reassoc_req(t->hdr->frame_control)) { | ||
607 | struct ieee80211_mgmt *mgt_frame = | ||
608 | (struct ieee80211_mgmt *)t->hdr; | ||
609 | |||
610 | if (mgt_frame->u.assoc_req.listen_interval < | ||
611 | priv->listen_interval) { | ||
612 | pr_debug("Modified Listen Interval to %d from %d\n", | ||
613 | priv->listen_interval, | ||
614 | mgt_frame->u.assoc_req.listen_interval); | ||
615 | /* Replace listen interval derieved from | ||
616 | * the one read from SDD */ | ||
617 | mgt_frame->u.assoc_req.listen_interval = | ||
618 | priv->listen_interval; | ||
619 | } | ||
620 | } | ||
621 | |||
622 | if (!priority) { | ||
623 | if (ieee80211_is_action(t->hdr->frame_control)) | ||
624 | priority = WSM_EPTA_PRIORITY_ACTION; | ||
625 | else if (ieee80211_is_mgmt(t->hdr->frame_control)) | ||
626 | priority = WSM_EPTA_PRIORITY_MGT; | ||
627 | else if ((wsm->queue_id == WSM_QUEUE_VOICE)) | ||
628 | priority = WSM_EPTA_PRIORITY_VOICE; | ||
629 | else if ((wsm->queue_id == WSM_QUEUE_VIDEO)) | ||
630 | priority = WSM_EPTA_PRIORITY_VIDEO; | ||
631 | else | ||
632 | priority = WSM_EPTA_PRIORITY_DATA; | ||
633 | } | ||
634 | |||
635 | pr_debug("[TX] EPTA priority %d.\n", priority); | ||
636 | |||
637 | wsm->flags |= priority << 1; | ||
638 | } | ||
639 | |||
640 | static int | ||
641 | cw1200_tx_h_rate_policy(struct cw1200_common *priv, | ||
642 | struct cw1200_txinfo *t, | ||
643 | struct wsm_tx *wsm) | ||
644 | { | ||
645 | bool tx_policy_renew = false; | ||
646 | |||
647 | t->txpriv.rate_id = tx_policy_get(priv, | ||
648 | t->tx_info->control.rates, IEEE80211_TX_MAX_RATES, | ||
649 | &tx_policy_renew); | ||
650 | if (t->txpriv.rate_id == CW1200_INVALID_RATE_ID) | ||
651 | return -EFAULT; | ||
652 | |||
653 | wsm->flags |= t->txpriv.rate_id << 4; | ||
654 | |||
655 | t->rate = cw1200_get_tx_rate(priv, | ||
656 | &t->tx_info->control.rates[0]), | ||
657 | wsm->max_tx_rate = t->rate->hw_value; | ||
658 | if (t->rate->flags & IEEE80211_TX_RC_MCS) { | ||
659 | if (cw1200_ht_greenfield(&priv->ht_info)) | ||
660 | wsm->ht_tx_parameters |= | ||
661 | __cpu_to_le32(WSM_HT_TX_GREENFIELD); | ||
662 | else | ||
663 | wsm->ht_tx_parameters |= | ||
664 | __cpu_to_le32(WSM_HT_TX_MIXED); | ||
665 | } | ||
666 | |||
667 | if (tx_policy_renew) { | ||
668 | pr_debug("[TX] TX policy renew.\n"); | ||
669 | /* It's not so optimal to stop TX queues every now and then. | ||
670 | * Better to reimplement task scheduling with | ||
671 | * a counter. TODO. */ | ||
672 | wsm_lock_tx_async(priv); | ||
673 | cw1200_tx_queues_lock(priv); | ||
674 | if (queue_work(priv->workqueue, | ||
675 | &priv->tx_policy_upload_work) <= 0) { | ||
676 | cw1200_tx_queues_unlock(priv); | ||
677 | wsm_unlock_tx(priv); | ||
678 | } | ||
679 | } | ||
680 | return 0; | ||
681 | } | ||
682 | |||
683 | static bool | ||
684 | cw1200_tx_h_pm_state(struct cw1200_common *priv, | ||
685 | struct cw1200_txinfo *t) | ||
686 | { | ||
687 | int was_buffered = 1; | ||
688 | |||
689 | if (t->txpriv.link_id == CW1200_LINK_ID_AFTER_DTIM && | ||
690 | !priv->buffered_multicasts) { | ||
691 | priv->buffered_multicasts = true; | ||
692 | if (priv->sta_asleep_mask) | ||
693 | queue_work(priv->workqueue, | ||
694 | &priv->multicast_start_work); | ||
695 | } | ||
696 | |||
697 | if (t->txpriv.raw_link_id && t->txpriv.tid < CW1200_MAX_TID) | ||
698 | was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1].buffered[t->txpriv.tid]++; | ||
699 | |||
700 | return !was_buffered; | ||
701 | } | ||
702 | |||
703 | /* ******************************************************************** */ | ||
704 | |||
705 | void cw1200_tx(struct ieee80211_hw *dev, | ||
706 | struct ieee80211_tx_control *control, | ||
707 | struct sk_buff *skb) | ||
708 | { | ||
709 | struct cw1200_common *priv = dev->priv; | ||
710 | struct cw1200_txinfo t = { | ||
711 | .skb = skb, | ||
712 | .queue = skb_get_queue_mapping(skb), | ||
713 | .tx_info = IEEE80211_SKB_CB(skb), | ||
714 | .hdr = (struct ieee80211_hdr *)skb->data, | ||
715 | .txpriv.tid = CW1200_MAX_TID, | ||
716 | .txpriv.rate_id = CW1200_INVALID_RATE_ID, | ||
717 | }; | ||
718 | struct ieee80211_sta *sta; | ||
719 | struct wsm_tx *wsm; | ||
720 | bool tid_update = 0; | ||
721 | u8 flags = 0; | ||
722 | int ret; | ||
723 | |||
724 | if (priv->bh_error) | ||
725 | goto drop; | ||
726 | |||
727 | t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control); | ||
728 | t.da = ieee80211_get_DA(t.hdr); | ||
729 | if (control) { | ||
730 | t.sta = control->sta; | ||
731 | t.sta_priv = (struct cw1200_sta_priv *)&t.sta->drv_priv; | ||
732 | } | ||
733 | |||
734 | if (WARN_ON(t.queue >= 4)) | ||
735 | goto drop; | ||
736 | |||
737 | ret = cw1200_tx_h_calc_link_ids(priv, &t); | ||
738 | if (ret) | ||
739 | goto drop; | ||
740 | |||
741 | pr_debug("[TX] TX %d bytes (queue: %d, link_id: %d (%d)).\n", | ||
742 | skb->len, t.queue, t.txpriv.link_id, | ||
743 | t.txpriv.raw_link_id); | ||
744 | |||
745 | cw1200_tx_h_pm(priv, &t); | ||
746 | cw1200_tx_h_calc_tid(priv, &t); | ||
747 | ret = cw1200_tx_h_crypt(priv, &t); | ||
748 | if (ret) | ||
749 | goto drop; | ||
750 | ret = cw1200_tx_h_align(priv, &t, &flags); | ||
751 | if (ret) | ||
752 | goto drop; | ||
753 | ret = cw1200_tx_h_action(priv, &t); | ||
754 | if (ret) | ||
755 | goto drop; | ||
756 | wsm = cw1200_tx_h_wsm(priv, &t); | ||
757 | if (!wsm) { | ||
758 | ret = -ENOMEM; | ||
759 | goto drop; | ||
760 | } | ||
761 | wsm->flags |= flags; | ||
762 | cw1200_tx_h_bt(priv, &t, wsm); | ||
763 | ret = cw1200_tx_h_rate_policy(priv, &t, wsm); | ||
764 | if (ret) | ||
765 | goto drop; | ||
766 | |||
767 | rcu_read_lock(); | ||
768 | sta = rcu_dereference(t.sta); | ||
769 | |||
770 | spin_lock_bh(&priv->ps_state_lock); | ||
771 | { | ||
772 | tid_update = cw1200_tx_h_pm_state(priv, &t); | ||
773 | BUG_ON(cw1200_queue_put(&priv->tx_queue[t.queue], | ||
774 | t.skb, &t.txpriv)); | ||
775 | } | ||
776 | spin_unlock_bh(&priv->ps_state_lock); | ||
777 | |||
778 | if (tid_update && sta) | ||
779 | ieee80211_sta_set_buffered(sta, t.txpriv.tid, true); | ||
780 | |||
781 | rcu_read_unlock(); | ||
782 | |||
783 | cw1200_bh_wakeup(priv); | ||
784 | |||
785 | return; | ||
786 | |||
787 | drop: | ||
788 | cw1200_skb_dtor(priv, skb, &t.txpriv); | ||
789 | return; | ||
790 | } | ||
791 | |||
792 | /* ******************************************************************** */ | ||
793 | |||
794 | static int cw1200_handle_action_rx(struct cw1200_common *priv, | ||
795 | struct sk_buff *skb) | ||
796 | { | ||
797 | struct ieee80211_mgmt *mgmt = (void *)skb->data; | ||
798 | |||
799 | /* Filter block ACK negotiation: fully controlled by firmware */ | ||
800 | if (mgmt->u.action.category == WLAN_CATEGORY_BACK) | ||
801 | return 1; | ||
802 | |||
803 | return 0; | ||
804 | } | ||
805 | |||
806 | static int cw1200_handle_pspoll(struct cw1200_common *priv, | ||
807 | struct sk_buff *skb) | ||
808 | { | ||
809 | struct ieee80211_sta *sta; | ||
810 | struct ieee80211_pspoll *pspoll = (struct ieee80211_pspoll *)skb->data; | ||
811 | int link_id = 0; | ||
812 | u32 pspoll_mask = 0; | ||
813 | int drop = 1; | ||
814 | int i; | ||
815 | |||
816 | if (priv->join_status != CW1200_JOIN_STATUS_AP) | ||
817 | goto done; | ||
818 | if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN)) | ||
819 | goto done; | ||
820 | |||
821 | rcu_read_lock(); | ||
822 | sta = ieee80211_find_sta(priv->vif, pspoll->ta); | ||
823 | if (sta) { | ||
824 | struct cw1200_sta_priv *sta_priv; | ||
825 | sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; | ||
826 | link_id = sta_priv->link_id; | ||
827 | pspoll_mask = BIT(sta_priv->link_id); | ||
828 | } | ||
829 | rcu_read_unlock(); | ||
830 | if (!link_id) | ||
831 | goto done; | ||
832 | |||
833 | priv->pspoll_mask |= pspoll_mask; | ||
834 | drop = 0; | ||
835 | |||
836 | /* Do not report pspols if data for given link id is | ||
837 | * queued already. */ | ||
838 | for (i = 0; i < 4; ++i) { | ||
839 | if (cw1200_queue_get_num_queued(&priv->tx_queue[i], | ||
840 | pspoll_mask)) { | ||
841 | cw1200_bh_wakeup(priv); | ||
842 | drop = 1; | ||
843 | break; | ||
844 | } | ||
845 | } | ||
846 | pr_debug("[RX] PSPOLL: %s\n", drop ? "local" : "fwd"); | ||
847 | done: | ||
848 | return drop; | ||
849 | } | ||
850 | |||
851 | /* ******************************************************************** */ | ||
852 | |||
853 | void cw1200_tx_confirm_cb(struct cw1200_common *priv, | ||
854 | int link_id, | ||
855 | struct wsm_tx_confirm *arg) | ||
856 | { | ||
857 | u8 queue_id = cw1200_queue_get_queue_id(arg->packet_id); | ||
858 | struct cw1200_queue *queue = &priv->tx_queue[queue_id]; | ||
859 | struct sk_buff *skb; | ||
860 | const struct cw1200_txpriv *txpriv; | ||
861 | |||
862 | pr_debug("[TX] TX confirm: %d, %d.\n", | ||
863 | arg->status, arg->ack_failures); | ||
864 | |||
865 | if (cw1200_itp_tx_running(priv)) | ||
866 | return; | ||
867 | |||
868 | if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { | ||
869 | /* STA is stopped. */ | ||
870 | return; | ||
871 | } | ||
872 | |||
873 | if (WARN_ON(queue_id >= 4)) | ||
874 | return; | ||
875 | |||
876 | if (arg->status) | ||
877 | pr_debug("TX failed: %d.\n", arg->status); | ||
878 | |||
879 | if ((arg->status == WSM_REQUEUE) && | ||
880 | (arg->flags & WSM_TX_STATUS_REQUEUE)) { | ||
881 | /* "Requeue" means "implicit suspend" */ | ||
882 | struct wsm_suspend_resume suspend = { | ||
883 | .link_id = link_id, | ||
884 | .stop = 1, | ||
885 | .multicast = !link_id, | ||
886 | }; | ||
887 | cw1200_suspend_resume(priv, &suspend); | ||
888 | wiphy_warn(priv->hw->wiphy, "Requeue for link_id %d (try %d). STAs asleep: 0x%.8X\n", | ||
889 | link_id, | ||
890 | cw1200_queue_get_generation(arg->packet_id) + 1, | ||
891 | priv->sta_asleep_mask); | ||
892 | cw1200_queue_requeue(queue, arg->packet_id); | ||
893 | spin_lock_bh(&priv->ps_state_lock); | ||
894 | if (!link_id) { | ||
895 | priv->buffered_multicasts = true; | ||
896 | if (priv->sta_asleep_mask) { | ||
897 | queue_work(priv->workqueue, | ||
898 | &priv->multicast_start_work); | ||
899 | } | ||
900 | } | ||
901 | spin_unlock_bh(&priv->ps_state_lock); | ||
902 | } else if (!cw1200_queue_get_skb(queue, arg->packet_id, | ||
903 | &skb, &txpriv)) { | ||
904 | struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); | ||
905 | int tx_count = arg->ack_failures; | ||
906 | u8 ht_flags = 0; | ||
907 | int i; | ||
908 | |||
909 | if (cw1200_ht_greenfield(&priv->ht_info)) | ||
910 | ht_flags |= IEEE80211_TX_RC_GREEN_FIELD; | ||
911 | |||
912 | spin_lock(&priv->bss_loss_lock); | ||
913 | if (priv->bss_loss_state && | ||
914 | arg->packet_id == priv->bss_loss_confirm_id) { | ||
915 | if (arg->status) { | ||
916 | /* Recovery failed */ | ||
917 | __cw1200_cqm_bssloss_sm(priv, 0, 0, 1); | ||
918 | } else { | ||
919 | /* Recovery succeeded */ | ||
920 | __cw1200_cqm_bssloss_sm(priv, 0, 1, 0); | ||
921 | } | ||
922 | } | ||
923 | spin_unlock(&priv->bss_loss_lock); | ||
924 | |||
925 | if (!arg->status) { | ||
926 | tx->flags |= IEEE80211_TX_STAT_ACK; | ||
927 | ++tx_count; | ||
928 | cw1200_debug_txed(priv); | ||
929 | if (arg->flags & WSM_TX_STATUS_AGGREGATION) { | ||
930 | /* Do not report aggregation to mac80211: | ||
931 | * it confuses minstrel a lot. */ | ||
932 | /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */ | ||
933 | cw1200_debug_txed_agg(priv); | ||
934 | } | ||
935 | } else { | ||
936 | if (tx_count) | ||
937 | ++tx_count; | ||
938 | } | ||
939 | |||
940 | for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) { | ||
941 | if (tx->status.rates[i].count >= tx_count) { | ||
942 | tx->status.rates[i].count = tx_count; | ||
943 | break; | ||
944 | } | ||
945 | tx_count -= tx->status.rates[i].count; | ||
946 | if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS) | ||
947 | tx->status.rates[i].flags |= ht_flags; | ||
948 | } | ||
949 | |||
950 | for (++i; i < IEEE80211_TX_MAX_RATES; ++i) { | ||
951 | tx->status.rates[i].count = 0; | ||
952 | tx->status.rates[i].idx = -1; | ||
953 | } | ||
954 | |||
955 | /* Pull off any crypto trailers that we added on */ | ||
956 | if (tx->control.hw_key) { | ||
957 | skb_trim(skb, skb->len - tx->control.hw_key->icv_len); | ||
958 | if (tx->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) | ||
959 | skb_trim(skb, skb->len - 8); /* MIC space */ | ||
960 | } | ||
961 | cw1200_queue_remove(queue, arg->packet_id); | ||
962 | } | ||
963 | /* XXX TODO: Only wake if there are pending transmits.. */ | ||
964 | cw1200_bh_wakeup(priv); | ||
965 | } | ||
966 | |||
967 | static void cw1200_notify_buffered_tx(struct cw1200_common *priv, | ||
968 | struct sk_buff *skb, int link_id, int tid) | ||
969 | { | ||
970 | struct ieee80211_sta *sta; | ||
971 | struct ieee80211_hdr *hdr; | ||
972 | u8 *buffered; | ||
973 | u8 still_buffered = 0; | ||
974 | |||
975 | if (link_id && tid < CW1200_MAX_TID) { | ||
976 | buffered = priv->link_id_db | ||
977 | [link_id - 1].buffered; | ||
978 | |||
979 | spin_lock_bh(&priv->ps_state_lock); | ||
980 | if (!WARN_ON(!buffered[tid])) | ||
981 | still_buffered = --buffered[tid]; | ||
982 | spin_unlock_bh(&priv->ps_state_lock); | ||
983 | |||
984 | if (!still_buffered && tid < CW1200_MAX_TID) { | ||
985 | hdr = (struct ieee80211_hdr *)skb->data; | ||
986 | rcu_read_lock(); | ||
987 | sta = ieee80211_find_sta(priv->vif, hdr->addr1); | ||
988 | if (sta) | ||
989 | ieee80211_sta_set_buffered(sta, tid, false); | ||
990 | rcu_read_unlock(); | ||
991 | } | ||
992 | } | ||
993 | } | ||
994 | |||
995 | void cw1200_skb_dtor(struct cw1200_common *priv, | ||
996 | struct sk_buff *skb, | ||
997 | const struct cw1200_txpriv *txpriv) | ||
998 | { | ||
999 | skb_pull(skb, txpriv->offset); | ||
1000 | if (txpriv->rate_id != CW1200_INVALID_RATE_ID) { | ||
1001 | cw1200_notify_buffered_tx(priv, skb, | ||
1002 | txpriv->raw_link_id, txpriv->tid); | ||
1003 | tx_policy_put(priv, txpriv->rate_id); | ||
1004 | } | ||
1005 | if (!cw1200_is_itp(priv)) | ||
1006 | ieee80211_tx_status(priv->hw, skb); | ||
1007 | } | ||
1008 | |||
1009 | void cw1200_rx_cb(struct cw1200_common *priv, | ||
1010 | struct wsm_rx *arg, | ||
1011 | int link_id, | ||
1012 | struct sk_buff **skb_p) | ||
1013 | { | ||
1014 | struct sk_buff *skb = *skb_p; | ||
1015 | struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); | ||
1016 | struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; | ||
1017 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; | ||
1018 | struct cw1200_link_entry *entry = NULL; | ||
1019 | unsigned long grace_period; | ||
1020 | |||
1021 | bool early_data = false; | ||
1022 | bool p2p = priv->vif && priv->vif->p2p; | ||
1023 | size_t hdrlen; | ||
1024 | hdr->flag = 0; | ||
1025 | |||
1026 | if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { | ||
1027 | /* STA is stopped. */ | ||
1028 | goto drop; | ||
1029 | } | ||
1030 | |||
1031 | if (link_id && link_id <= CW1200_MAX_STA_IN_AP_MODE) { | ||
1032 | entry = &priv->link_id_db[link_id - 1]; | ||
1033 | if (entry->status == CW1200_LINK_SOFT && | ||
1034 | ieee80211_is_data(frame->frame_control)) | ||
1035 | early_data = true; | ||
1036 | entry->timestamp = jiffies; | ||
1037 | } else if (p2p && | ||
1038 | ieee80211_is_action(frame->frame_control) && | ||
1039 | (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { | ||
1040 | pr_debug("[RX] Going to MAP&RESET link ID\n"); | ||
1041 | WARN_ON(work_pending(&priv->linkid_reset_work)); | ||
1042 | memcpy(&priv->action_frame_sa[0], | ||
1043 | ieee80211_get_SA(frame), ETH_ALEN); | ||
1044 | priv->action_linkid = 0; | ||
1045 | schedule_work(&priv->linkid_reset_work); | ||
1046 | } | ||
1047 | |||
1048 | if (link_id && p2p && | ||
1049 | ieee80211_is_action(frame->frame_control) && | ||
1050 | (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { | ||
1051 | /* Link ID already exists for the ACTION frame. | ||
1052 | * Reset and Remap */ | ||
1053 | WARN_ON(work_pending(&priv->linkid_reset_work)); | ||
1054 | memcpy(&priv->action_frame_sa[0], | ||
1055 | ieee80211_get_SA(frame), ETH_ALEN); | ||
1056 | priv->action_linkid = link_id; | ||
1057 | schedule_work(&priv->linkid_reset_work); | ||
1058 | } | ||
1059 | if (arg->status) { | ||
1060 | if (arg->status == WSM_STATUS_MICFAILURE) { | ||
1061 | pr_debug("[RX] MIC failure.\n"); | ||
1062 | hdr->flag |= RX_FLAG_MMIC_ERROR; | ||
1063 | } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) { | ||
1064 | pr_debug("[RX] No key found.\n"); | ||
1065 | goto drop; | ||
1066 | } else { | ||
1067 | pr_debug("[RX] Receive failure: %d.\n", | ||
1068 | arg->status); | ||
1069 | goto drop; | ||
1070 | } | ||
1071 | } | ||
1072 | |||
1073 | if (skb->len < sizeof(struct ieee80211_pspoll)) { | ||
1074 | wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. Size is lesser than IEEE header.\n"); | ||
1075 | goto drop; | ||
1076 | } | ||
1077 | |||
1078 | if (ieee80211_is_pspoll(frame->frame_control)) | ||
1079 | if (cw1200_handle_pspoll(priv, skb)) | ||
1080 | goto drop; | ||
1081 | |||
1082 | hdr->mactime = 0; /* Not supported by WSM */ | ||
1083 | hdr->band = ((arg->channel_number & 0xff00) || | ||
1084 | (arg->channel_number > 14)) ? | ||
1085 | IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; | ||
1086 | hdr->freq = ieee80211_channel_to_frequency( | ||
1087 | arg->channel_number, | ||
1088 | hdr->band); | ||
1089 | |||
1090 | if (arg->rx_rate >= 14) { | ||
1091 | hdr->flag |= RX_FLAG_HT; | ||
1092 | hdr->rate_idx = arg->rx_rate - 14; | ||
1093 | } else if (arg->rx_rate >= 4) { | ||
1094 | hdr->rate_idx = arg->rx_rate - 2; | ||
1095 | } else { | ||
1096 | hdr->rate_idx = arg->rx_rate; | ||
1097 | } | ||
1098 | |||
1099 | hdr->signal = (s8)arg->rcpi_rssi; | ||
1100 | hdr->antenna = 0; | ||
1101 | |||
1102 | hdrlen = ieee80211_hdrlen(frame->frame_control); | ||
1103 | |||
1104 | if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { | ||
1105 | size_t iv_len = 0, icv_len = 0; | ||
1106 | |||
1107 | hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED; | ||
1108 | |||
1109 | /* Oops... There is no fast way to ask mac80211 about | ||
1110 | * IV/ICV lengths. Even defineas are not exposed.*/ | ||
1111 | switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { | ||
1112 | case WSM_RX_STATUS_WEP: | ||
1113 | iv_len = 4 /* WEP_IV_LEN */; | ||
1114 | icv_len = 4 /* WEP_ICV_LEN */; | ||
1115 | break; | ||
1116 | case WSM_RX_STATUS_TKIP: | ||
1117 | iv_len = 8 /* TKIP_IV_LEN */; | ||
1118 | icv_len = 4 /* TKIP_ICV_LEN */ | ||
1119 | + 8 /*MICHAEL_MIC_LEN*/; | ||
1120 | hdr->flag |= RX_FLAG_MMIC_STRIPPED; | ||
1121 | break; | ||
1122 | case WSM_RX_STATUS_AES: | ||
1123 | iv_len = 8 /* CCMP_HDR_LEN */; | ||
1124 | icv_len = 8 /* CCMP_MIC_LEN */; | ||
1125 | break; | ||
1126 | case WSM_RX_STATUS_WAPI: | ||
1127 | iv_len = 18 /* WAPI_HDR_LEN */; | ||
1128 | icv_len = 16 /* WAPI_MIC_LEN */; | ||
1129 | break; | ||
1130 | default: | ||
1131 | pr_warn("Unknown encryption type %d\n", | ||
1132 | WSM_RX_STATUS_ENCRYPTION(arg->flags)); | ||
1133 | goto drop; | ||
1134 | } | ||
1135 | |||
1136 | /* Firmware strips ICV in case of MIC failure. */ | ||
1137 | if (arg->status == WSM_STATUS_MICFAILURE) | ||
1138 | icv_len = 0; | ||
1139 | |||
1140 | if (skb->len < hdrlen + iv_len + icv_len) { | ||
1141 | wiphy_warn(priv->hw->wiphy, "Malformed SDU rx'ed. Size is lesser than crypto headers.\n"); | ||
1142 | goto drop; | ||
1143 | } | ||
1144 | |||
1145 | /* Remove IV, ICV and MIC */ | ||
1146 | skb_trim(skb, skb->len - icv_len); | ||
1147 | memmove(skb->data + iv_len, skb->data, hdrlen); | ||
1148 | skb_pull(skb, iv_len); | ||
1149 | } | ||
1150 | |||
1151 | /* Remove TSF from the end of frame */ | ||
1152 | if (arg->flags & WSM_RX_STATUS_TSF_INCLUDED) { | ||
1153 | memcpy(&hdr->mactime, skb->data + skb->len - 8, 8); | ||
1154 | hdr->mactime = le64_to_cpu(hdr->mactime); | ||
1155 | if (skb->len >= 8) | ||
1156 | skb_trim(skb, skb->len - 8); | ||
1157 | } | ||
1158 | |||
1159 | cw1200_debug_rxed(priv); | ||
1160 | if (arg->flags & WSM_RX_STATUS_AGGREGATE) | ||
1161 | cw1200_debug_rxed_agg(priv); | ||
1162 | |||
1163 | if (ieee80211_is_action(frame->frame_control) && | ||
1164 | (arg->flags & WSM_RX_STATUS_ADDRESS1)) { | ||
1165 | if (cw1200_handle_action_rx(priv, skb)) | ||
1166 | return; | ||
1167 | } else if (ieee80211_is_beacon(frame->frame_control) && | ||
1168 | !arg->status && | ||
1169 | !memcmp(ieee80211_get_SA(frame), priv->vif->bss_conf.bssid, | ||
1170 | ETH_ALEN)) { | ||
1171 | const u8 *tim_ie; | ||
1172 | u8 *ies = ((struct ieee80211_mgmt *) | ||
1173 | (skb->data))->u.beacon.variable; | ||
1174 | size_t ies_len = skb->len - (ies - (u8 *)(skb->data)); | ||
1175 | |||
1176 | tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies, ies_len); | ||
1177 | if (tim_ie) { | ||
1178 | struct ieee80211_tim_ie *tim = | ||
1179 | (struct ieee80211_tim_ie *)&tim_ie[2]; | ||
1180 | |||
1181 | if (priv->join_dtim_period != tim->dtim_period) { | ||
1182 | priv->join_dtim_period = tim->dtim_period; | ||
1183 | queue_work(priv->workqueue, | ||
1184 | &priv->set_beacon_wakeup_period_work); | ||
1185 | } | ||
1186 | } | ||
1187 | |||
1188 | /* Disable beacon filter once we're associated... */ | ||
1189 | if (priv->disable_beacon_filter && | ||
1190 | (priv->vif->bss_conf.assoc || | ||
1191 | priv->vif->bss_conf.ibss_joined)) { | ||
1192 | priv->disable_beacon_filter = false; | ||
1193 | queue_work(priv->workqueue, | ||
1194 | &priv->update_filtering_work); | ||
1195 | } | ||
1196 | } | ||
1197 | |||
1198 | /* Stay awake after frame is received to give | ||
1199 | * userspace chance to react and acquire appropriate | ||
1200 | * wakelock. */ | ||
1201 | if (ieee80211_is_auth(frame->frame_control)) | ||
1202 | grace_period = 5 * HZ; | ||
1203 | else if (ieee80211_is_deauth(frame->frame_control)) | ||
1204 | grace_period = 5 * HZ; | ||
1205 | else | ||
1206 | grace_period = 1 * HZ; | ||
1207 | cw1200_pm_stay_awake(&priv->pm_state, grace_period); | ||
1208 | |||
1209 | if (cw1200_itp_rxed(priv, skb)) { | ||
1210 | consume_skb(skb); | ||
1211 | } else if (early_data) { | ||
1212 | spin_lock_bh(&priv->ps_state_lock); | ||
1213 | /* Double-check status with lock held */ | ||
1214 | if (entry->status == CW1200_LINK_SOFT) | ||
1215 | skb_queue_tail(&entry->rx_queue, skb); | ||
1216 | else | ||
1217 | ieee80211_rx_irqsafe(priv->hw, skb); | ||
1218 | spin_unlock_bh(&priv->ps_state_lock); | ||
1219 | } else { | ||
1220 | ieee80211_rx_irqsafe(priv->hw, skb); | ||
1221 | } | ||
1222 | *skb_p = NULL; | ||
1223 | |||
1224 | return; | ||
1225 | |||
1226 | drop: | ||
1227 | /* TODO: update failure counters */ | ||
1228 | return; | ||
1229 | } | ||
1230 | |||
1231 | /* ******************************************************************** */ | ||
1232 | /* Security */ | ||
1233 | |||
1234 | int cw1200_alloc_key(struct cw1200_common *priv) | ||
1235 | { | ||
1236 | int idx; | ||
1237 | |||
1238 | idx = ffs(~priv->key_map) - 1; | ||
1239 | if (idx < 0 || idx > WSM_KEY_MAX_INDEX) | ||
1240 | return -1; | ||
1241 | |||
1242 | priv->key_map |= BIT(idx); | ||
1243 | priv->keys[idx].index = idx; | ||
1244 | return idx; | ||
1245 | } | ||
1246 | |||
1247 | void cw1200_free_key(struct cw1200_common *priv, int idx) | ||
1248 | { | ||
1249 | BUG_ON(!(priv->key_map & BIT(idx))); | ||
1250 | memset(&priv->keys[idx], 0, sizeof(priv->keys[idx])); | ||
1251 | priv->key_map &= ~BIT(idx); | ||
1252 | } | ||
1253 | |||
1254 | void cw1200_free_keys(struct cw1200_common *priv) | ||
1255 | { | ||
1256 | memset(&priv->keys, 0, sizeof(priv->keys)); | ||
1257 | priv->key_map = 0; | ||
1258 | } | ||
1259 | |||
1260 | int cw1200_upload_keys(struct cw1200_common *priv) | ||
1261 | { | ||
1262 | int idx, ret = 0; | ||
1263 | for (idx = 0; idx <= WSM_KEY_MAX_INDEX; ++idx) | ||
1264 | if (priv->key_map & BIT(idx)) { | ||
1265 | ret = wsm_add_key(priv, &priv->keys[idx]); | ||
1266 | if (ret < 0) | ||
1267 | break; | ||
1268 | } | ||
1269 | return ret; | ||
1270 | } | ||
1271 | |||
1272 | /* Workaround for WFD test case 6.1.10 */ | ||
1273 | void cw1200_link_id_reset(struct work_struct *work) | ||
1274 | { | ||
1275 | struct cw1200_common *priv = | ||
1276 | container_of(work, struct cw1200_common, linkid_reset_work); | ||
1277 | int temp_linkid; | ||
1278 | |||
1279 | if (!priv->action_linkid) { | ||
1280 | /* In GO mode we can receive ACTION frames without a linkID */ | ||
1281 | temp_linkid = cw1200_alloc_link_id(priv, | ||
1282 | &priv->action_frame_sa[0]); | ||
1283 | WARN_ON(!temp_linkid); | ||
1284 | if (temp_linkid) { | ||
1285 | /* Make sure we execute the WQ */ | ||
1286 | flush_workqueue(priv->workqueue); | ||
1287 | /* Release the link ID */ | ||
1288 | spin_lock_bh(&priv->ps_state_lock); | ||
1289 | priv->link_id_db[temp_linkid - 1].prev_status = | ||
1290 | priv->link_id_db[temp_linkid - 1].status; | ||
1291 | priv->link_id_db[temp_linkid - 1].status = | ||
1292 | CW1200_LINK_RESET; | ||
1293 | spin_unlock_bh(&priv->ps_state_lock); | ||
1294 | wsm_lock_tx_async(priv); | ||
1295 | if (queue_work(priv->workqueue, | ||
1296 | &priv->link_id_work) <= 0) | ||
1297 | wsm_unlock_tx(priv); | ||
1298 | } | ||
1299 | } else { | ||
1300 | spin_lock_bh(&priv->ps_state_lock); | ||
1301 | priv->link_id_db[priv->action_linkid - 1].prev_status = | ||
1302 | priv->link_id_db[priv->action_linkid - 1].status; | ||
1303 | priv->link_id_db[priv->action_linkid - 1].status = | ||
1304 | CW1200_LINK_RESET_REMAP; | ||
1305 | spin_unlock_bh(&priv->ps_state_lock); | ||
1306 | wsm_lock_tx_async(priv); | ||
1307 | if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) | ||
1308 | wsm_unlock_tx(priv); | ||
1309 | flush_workqueue(priv->workqueue); | ||
1310 | } | ||
1311 | } | ||
1312 | |||
1313 | int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac) | ||
1314 | { | ||
1315 | int i, ret = 0; | ||
1316 | spin_lock_bh(&priv->ps_state_lock); | ||
1317 | for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { | ||
1318 | if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) && | ||
1319 | priv->link_id_db[i].status) { | ||
1320 | priv->link_id_db[i].timestamp = jiffies; | ||
1321 | ret = i + 1; | ||
1322 | break; | ||
1323 | } | ||
1324 | } | ||
1325 | spin_unlock_bh(&priv->ps_state_lock); | ||
1326 | return ret; | ||
1327 | } | ||
1328 | |||
1329 | int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac) | ||
1330 | { | ||
1331 | int i, ret = 0; | ||
1332 | unsigned long max_inactivity = 0; | ||
1333 | unsigned long now = jiffies; | ||
1334 | |||
1335 | spin_lock_bh(&priv->ps_state_lock); | ||
1336 | for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { | ||
1337 | if (!priv->link_id_db[i].status) { | ||
1338 | ret = i + 1; | ||
1339 | break; | ||
1340 | } else if (priv->link_id_db[i].status != CW1200_LINK_HARD && | ||
1341 | !priv->tx_queue_stats.link_map_cache[i + 1]) { | ||
1342 | unsigned long inactivity = | ||
1343 | now - priv->link_id_db[i].timestamp; | ||
1344 | if (inactivity < max_inactivity) | ||
1345 | continue; | ||
1346 | max_inactivity = inactivity; | ||
1347 | ret = i + 1; | ||
1348 | } | ||
1349 | } | ||
1350 | if (ret) { | ||
1351 | struct cw1200_link_entry *entry = &priv->link_id_db[ret - 1]; | ||
1352 | pr_debug("[AP] STA added, link_id: %d\n", ret); | ||
1353 | entry->status = CW1200_LINK_RESERVE; | ||
1354 | memcpy(&entry->mac, mac, ETH_ALEN); | ||
1355 | memset(&entry->buffered, 0, CW1200_MAX_TID); | ||
1356 | skb_queue_head_init(&entry->rx_queue); | ||
1357 | wsm_lock_tx_async(priv); | ||
1358 | if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) | ||
1359 | wsm_unlock_tx(priv); | ||
1360 | } else { | ||
1361 | wiphy_info(priv->hw->wiphy, | ||
1362 | "[AP] Early: no more link IDs available.\n"); | ||
1363 | } | ||
1364 | |||
1365 | spin_unlock_bh(&priv->ps_state_lock); | ||
1366 | return ret; | ||
1367 | } | ||
1368 | |||
1369 | void cw1200_link_id_work(struct work_struct *work) | ||
1370 | { | ||
1371 | struct cw1200_common *priv = | ||
1372 | container_of(work, struct cw1200_common, link_id_work); | ||
1373 | wsm_flush_tx(priv); | ||
1374 | cw1200_link_id_gc_work(&priv->link_id_gc_work.work); | ||
1375 | wsm_unlock_tx(priv); | ||
1376 | } | ||
1377 | |||
1378 | void cw1200_link_id_gc_work(struct work_struct *work) | ||
1379 | { | ||
1380 | struct cw1200_common *priv = | ||
1381 | container_of(work, struct cw1200_common, link_id_gc_work.work); | ||
1382 | struct wsm_reset reset = { | ||
1383 | .reset_statistics = false, | ||
1384 | }; | ||
1385 | struct wsm_map_link map_link = { | ||
1386 | .link_id = 0, | ||
1387 | }; | ||
1388 | unsigned long now = jiffies; | ||
1389 | unsigned long next_gc = -1; | ||
1390 | long ttl; | ||
1391 | bool need_reset; | ||
1392 | u32 mask; | ||
1393 | int i; | ||
1394 | |||
1395 | if (priv->join_status != CW1200_JOIN_STATUS_AP) | ||
1396 | return; | ||
1397 | |||
1398 | wsm_lock_tx(priv); | ||
1399 | spin_lock_bh(&priv->ps_state_lock); | ||
1400 | for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { | ||
1401 | need_reset = false; | ||
1402 | mask = BIT(i + 1); | ||
1403 | if (priv->link_id_db[i].status == CW1200_LINK_RESERVE || | ||
1404 | (priv->link_id_db[i].status == CW1200_LINK_HARD && | ||
1405 | !(priv->link_id_map & mask))) { | ||
1406 | if (priv->link_id_map & mask) { | ||
1407 | priv->sta_asleep_mask &= ~mask; | ||
1408 | priv->pspoll_mask &= ~mask; | ||
1409 | need_reset = true; | ||
1410 | } | ||
1411 | priv->link_id_map |= mask; | ||
1412 | if (priv->link_id_db[i].status != CW1200_LINK_HARD) | ||
1413 | priv->link_id_db[i].status = CW1200_LINK_SOFT; | ||
1414 | memcpy(map_link.mac_addr, priv->link_id_db[i].mac, | ||
1415 | ETH_ALEN); | ||
1416 | spin_unlock_bh(&priv->ps_state_lock); | ||
1417 | if (need_reset) { | ||
1418 | reset.link_id = i + 1; | ||
1419 | wsm_reset(priv, &reset); | ||
1420 | } | ||
1421 | map_link.link_id = i + 1; | ||
1422 | wsm_map_link(priv, &map_link); | ||
1423 | next_gc = min(next_gc, CW1200_LINK_ID_GC_TIMEOUT); | ||
1424 | spin_lock_bh(&priv->ps_state_lock); | ||
1425 | } else if (priv->link_id_db[i].status == CW1200_LINK_SOFT) { | ||
1426 | ttl = priv->link_id_db[i].timestamp - now + | ||
1427 | CW1200_LINK_ID_GC_TIMEOUT; | ||
1428 | if (ttl <= 0) { | ||
1429 | need_reset = true; | ||
1430 | priv->link_id_db[i].status = CW1200_LINK_OFF; | ||
1431 | priv->link_id_map &= ~mask; | ||
1432 | priv->sta_asleep_mask &= ~mask; | ||
1433 | priv->pspoll_mask &= ~mask; | ||
1434 | memset(map_link.mac_addr, 0, ETH_ALEN); | ||
1435 | spin_unlock_bh(&priv->ps_state_lock); | ||
1436 | reset.link_id = i + 1; | ||
1437 | wsm_reset(priv, &reset); | ||
1438 | spin_lock_bh(&priv->ps_state_lock); | ||
1439 | } else { | ||
1440 | next_gc = min_t(unsigned long, next_gc, ttl); | ||
1441 | } | ||
1442 | } else if (priv->link_id_db[i].status == CW1200_LINK_RESET || | ||
1443 | priv->link_id_db[i].status == | ||
1444 | CW1200_LINK_RESET_REMAP) { | ||
1445 | int status = priv->link_id_db[i].status; | ||
1446 | priv->link_id_db[i].status = | ||
1447 | priv->link_id_db[i].prev_status; | ||
1448 | priv->link_id_db[i].timestamp = now; | ||
1449 | reset.link_id = i + 1; | ||
1450 | spin_unlock_bh(&priv->ps_state_lock); | ||
1451 | wsm_reset(priv, &reset); | ||
1452 | if (status == CW1200_LINK_RESET_REMAP) { | ||
1453 | memcpy(map_link.mac_addr, | ||
1454 | priv->link_id_db[i].mac, | ||
1455 | ETH_ALEN); | ||
1456 | map_link.link_id = i + 1; | ||
1457 | wsm_map_link(priv, &map_link); | ||
1458 | next_gc = min(next_gc, | ||
1459 | CW1200_LINK_ID_GC_TIMEOUT); | ||
1460 | } | ||
1461 | spin_lock_bh(&priv->ps_state_lock); | ||
1462 | } | ||
1463 | if (need_reset) { | ||
1464 | skb_queue_purge(&priv->link_id_db[i].rx_queue); | ||
1465 | pr_debug("[AP] STA removed, link_id: %d\n", | ||
1466 | reset.link_id); | ||
1467 | } | ||
1468 | } | ||
1469 | spin_unlock_bh(&priv->ps_state_lock); | ||
1470 | if (next_gc != -1) | ||
1471 | queue_delayed_work(priv->workqueue, | ||
1472 | &priv->link_id_gc_work, next_gc); | ||
1473 | wsm_unlock_tx(priv); | ||
1474 | } | ||
diff --git a/drivers/net/wireless/cw1200/txrx.h b/drivers/net/wireless/cw1200/txrx.h new file mode 100644 index 000000000000..492a4e14213b --- /dev/null +++ b/drivers/net/wireless/cw1200/txrx.h | |||
@@ -0,0 +1,106 @@ | |||
1 | /* | ||
2 | * Datapath interface for ST-Ericsson CW1200 mac80211 drivers | ||
3 | * | ||
4 | * Copyright (c) 2010, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef CW1200_TXRX_H | ||
13 | #define CW1200_TXRX_H | ||
14 | |||
15 | #include <linux/list.h> | ||
16 | |||
17 | /* extern */ struct ieee80211_hw; | ||
18 | /* extern */ struct sk_buff; | ||
19 | /* extern */ struct wsm_tx; | ||
20 | /* extern */ struct wsm_rx; | ||
21 | /* extern */ struct wsm_tx_confirm; | ||
22 | /* extern */ struct cw1200_txpriv; | ||
23 | |||
24 | struct tx_policy { | ||
25 | union { | ||
26 | __le32 tbl[3]; | ||
27 | u8 raw[12]; | ||
28 | }; | ||
29 | u8 defined; | ||
30 | u8 usage_count; | ||
31 | u8 retry_count; | ||
32 | u8 uploaded; | ||
33 | }; | ||
34 | |||
35 | struct tx_policy_cache_entry { | ||
36 | struct tx_policy policy; | ||
37 | struct list_head link; | ||
38 | }; | ||
39 | |||
40 | #define TX_POLICY_CACHE_SIZE (8) | ||
41 | struct tx_policy_cache { | ||
42 | struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE]; | ||
43 | struct list_head used; | ||
44 | struct list_head free; | ||
45 | spinlock_t lock; /* Protect policy cache */ | ||
46 | }; | ||
47 | |||
48 | /* ******************************************************************** */ | ||
49 | /* TX policy cache */ | ||
50 | /* Intention of TX policy cache is an overcomplicated WSM API. | ||
51 | * Device does not accept per-PDU tx retry sequence. | ||
52 | * It uses "tx retry policy id" instead, so driver code has to sync | ||
53 | * linux tx retry sequences with a retry policy table in the device. | ||
54 | */ | ||
55 | void tx_policy_init(struct cw1200_common *priv); | ||
56 | void tx_policy_upload_work(struct work_struct *work); | ||
57 | void tx_policy_clean(struct cw1200_common *priv); | ||
58 | |||
59 | /* ******************************************************************** */ | ||
60 | /* TX implementation */ | ||
61 | |||
62 | u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, | ||
63 | u32 rates); | ||
64 | void cw1200_tx(struct ieee80211_hw *dev, | ||
65 | struct ieee80211_tx_control *control, | ||
66 | struct sk_buff *skb); | ||
67 | void cw1200_skb_dtor(struct cw1200_common *priv, | ||
68 | struct sk_buff *skb, | ||
69 | const struct cw1200_txpriv *txpriv); | ||
70 | |||
71 | /* ******************************************************************** */ | ||
72 | /* WSM callbacks */ | ||
73 | |||
74 | void cw1200_tx_confirm_cb(struct cw1200_common *priv, | ||
75 | int link_id, | ||
76 | struct wsm_tx_confirm *arg); | ||
77 | void cw1200_rx_cb(struct cw1200_common *priv, | ||
78 | struct wsm_rx *arg, | ||
79 | int link_id, | ||
80 | struct sk_buff **skb_p); | ||
81 | |||
82 | /* ******************************************************************** */ | ||
83 | /* Timeout */ | ||
84 | |||
85 | void cw1200_tx_timeout(struct work_struct *work); | ||
86 | |||
87 | /* ******************************************************************** */ | ||
88 | /* Security */ | ||
89 | int cw1200_alloc_key(struct cw1200_common *priv); | ||
90 | void cw1200_free_key(struct cw1200_common *priv, int idx); | ||
91 | void cw1200_free_keys(struct cw1200_common *priv); | ||
92 | int cw1200_upload_keys(struct cw1200_common *priv); | ||
93 | |||
94 | /* ******************************************************************** */ | ||
95 | /* Workaround for WFD test case 6.1.10 */ | ||
96 | void cw1200_link_id_reset(struct work_struct *work); | ||
97 | |||
98 | #define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ)) | ||
99 | |||
100 | int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac); | ||
101 | int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac); | ||
102 | void cw1200_link_id_work(struct work_struct *work); | ||
103 | void cw1200_link_id_gc_work(struct work_struct *work); | ||
104 | |||
105 | |||
106 | #endif /* CW1200_TXRX_H */ | ||
diff --git a/drivers/net/wireless/cw1200/wsm.c b/drivers/net/wireless/cw1200/wsm.c new file mode 100644 index 000000000000..4db6cc16879c --- /dev/null +++ b/drivers/net/wireless/cw1200/wsm.c | |||
@@ -0,0 +1,1884 @@ | |||
1 | /* | ||
2 | * WSM host interface (HI) implementation for | ||
3 | * ST-Ericsson CW1200 mac80211 drivers. | ||
4 | * | ||
5 | * Copyright (c) 2010, ST-Ericsson | ||
6 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/skbuff.h> | ||
14 | #include <linux/wait.h> | ||
15 | #include <linux/skbuff.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/random.h> | ||
19 | |||
20 | #include "cw1200.h" | ||
21 | #include "wsm.h" | ||
22 | #include "bh.h" | ||
23 | #include "sta.h" | ||
24 | #include "debug.h" | ||
25 | #include "itp.h" | ||
26 | |||
27 | #define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */ | ||
28 | #define WSM_CMD_START_TIMEOUT (7 * HZ) | ||
29 | #define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */ | ||
30 | #define WSM_CMD_MAX_TIMEOUT (3 * HZ) | ||
31 | |||
32 | #define WSM_SKIP(buf, size) \ | ||
33 | do { \ | ||
34 | if ((buf)->data + size > (buf)->end) \ | ||
35 | goto underflow; \ | ||
36 | (buf)->data += size; \ | ||
37 | } while (0) | ||
38 | |||
39 | #define WSM_GET(buf, ptr, size) \ | ||
40 | do { \ | ||
41 | if ((buf)->data + size > (buf)->end) \ | ||
42 | goto underflow; \ | ||
43 | memcpy(ptr, (buf)->data, size); \ | ||
44 | (buf)->data += size; \ | ||
45 | } while (0) | ||
46 | |||
47 | #define __WSM_GET(buf, type, cvt) \ | ||
48 | ({ \ | ||
49 | type val; \ | ||
50 | if ((buf)->data + sizeof(type) > (buf)->end) \ | ||
51 | goto underflow; \ | ||
52 | val = cvt(*(type *)(buf)->data); \ | ||
53 | (buf)->data += sizeof(type); \ | ||
54 | val; \ | ||
55 | }) | ||
56 | |||
57 | #define WSM_GET8(buf) __WSM_GET(buf, u8, (u8)) | ||
58 | #define WSM_GET16(buf) __WSM_GET(buf, u16, __le16_to_cpu) | ||
59 | #define WSM_GET32(buf) __WSM_GET(buf, u32, __le32_to_cpu) | ||
60 | |||
61 | #define WSM_PUT(buf, ptr, size) \ | ||
62 | do { \ | ||
63 | if ((buf)->data + size > (buf)->end) \ | ||
64 | if (wsm_buf_reserve((buf), size)) \ | ||
65 | goto nomem; \ | ||
66 | memcpy((buf)->data, ptr, size); \ | ||
67 | (buf)->data += size; \ | ||
68 | } while (0) | ||
69 | |||
70 | #define __WSM_PUT(buf, val, type, cvt) \ | ||
71 | do { \ | ||
72 | if ((buf)->data + sizeof(type) > (buf)->end) \ | ||
73 | if (wsm_buf_reserve((buf), sizeof(type))) \ | ||
74 | goto nomem; \ | ||
75 | *(type *)(buf)->data = cvt(val); \ | ||
76 | (buf)->data += sizeof(type); \ | ||
77 | } while (0) | ||
78 | |||
79 | #define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, (u8)) | ||
80 | #define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __cpu_to_le16) | ||
81 | #define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __cpu_to_le32) | ||
82 | |||
83 | static void wsm_buf_reset(struct wsm_buf *buf); | ||
84 | static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size); | ||
85 | |||
86 | static int wsm_cmd_send(struct cw1200_common *priv, | ||
87 | struct wsm_buf *buf, | ||
88 | void *arg, u16 cmd, long tmo); | ||
89 | |||
90 | #define wsm_cmd_lock(__priv) mutex_lock(&((__priv)->wsm_cmd_mux)) | ||
91 | #define wsm_cmd_unlock(__priv) mutex_unlock(&((__priv)->wsm_cmd_mux)) | ||
92 | |||
93 | /* ******************************************************************** */ | ||
94 | /* WSM API implementation */ | ||
95 | |||
96 | static int wsm_generic_confirm(struct cw1200_common *priv, | ||
97 | void *arg, | ||
98 | struct wsm_buf *buf) | ||
99 | { | ||
100 | u32 status = WSM_GET32(buf); | ||
101 | if (status != WSM_STATUS_SUCCESS) | ||
102 | return -EINVAL; | ||
103 | return 0; | ||
104 | |||
105 | underflow: | ||
106 | WARN_ON(1); | ||
107 | return -EINVAL; | ||
108 | } | ||
109 | |||
110 | int wsm_configuration(struct cw1200_common *priv, struct wsm_configuration *arg) | ||
111 | { | ||
112 | int ret; | ||
113 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
114 | |||
115 | wsm_cmd_lock(priv); | ||
116 | |||
117 | WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime); | ||
118 | WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime); | ||
119 | WSM_PUT32(buf, arg->dot11RtsThreshold); | ||
120 | |||
121 | /* DPD block. */ | ||
122 | WSM_PUT16(buf, arg->dpdData_size + 12); | ||
123 | WSM_PUT16(buf, 1); /* DPD version */ | ||
124 | WSM_PUT(buf, arg->dot11StationId, ETH_ALEN); | ||
125 | WSM_PUT16(buf, 5); /* DPD flags */ | ||
126 | WSM_PUT(buf, arg->dpdData, arg->dpdData_size); | ||
127 | |||
128 | ret = wsm_cmd_send(priv, buf, arg, | ||
129 | WSM_CONFIGURATION_REQ_ID, WSM_CMD_TIMEOUT); | ||
130 | |||
131 | wsm_cmd_unlock(priv); | ||
132 | return ret; | ||
133 | |||
134 | nomem: | ||
135 | wsm_cmd_unlock(priv); | ||
136 | return -ENOMEM; | ||
137 | } | ||
138 | |||
139 | static int wsm_configuration_confirm(struct cw1200_common *priv, | ||
140 | struct wsm_configuration *arg, | ||
141 | struct wsm_buf *buf) | ||
142 | { | ||
143 | int i; | ||
144 | int status; | ||
145 | |||
146 | status = WSM_GET32(buf); | ||
147 | if (WARN_ON(status != WSM_STATUS_SUCCESS)) | ||
148 | return -EINVAL; | ||
149 | |||
150 | WSM_GET(buf, arg->dot11StationId, ETH_ALEN); | ||
151 | arg->dot11FrequencyBandsSupported = WSM_GET8(buf); | ||
152 | WSM_SKIP(buf, 1); | ||
153 | arg->supportedRateMask = WSM_GET32(buf); | ||
154 | for (i = 0; i < 2; ++i) { | ||
155 | arg->txPowerRange[i].min_power_level = WSM_GET32(buf); | ||
156 | arg->txPowerRange[i].max_power_level = WSM_GET32(buf); | ||
157 | arg->txPowerRange[i].stepping = WSM_GET32(buf); | ||
158 | } | ||
159 | return 0; | ||
160 | |||
161 | underflow: | ||
162 | WARN_ON(1); | ||
163 | return -EINVAL; | ||
164 | } | ||
165 | |||
166 | /* ******************************************************************** */ | ||
167 | |||
168 | int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg) | ||
169 | { | ||
170 | int ret; | ||
171 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
172 | u16 cmd = WSM_RESET_REQ_ID | WSM_TX_LINK_ID(arg->link_id); | ||
173 | |||
174 | wsm_cmd_lock(priv); | ||
175 | |||
176 | WSM_PUT32(buf, arg->reset_statistics ? 0 : 1); | ||
177 | ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT); | ||
178 | wsm_cmd_unlock(priv); | ||
179 | return ret; | ||
180 | |||
181 | nomem: | ||
182 | wsm_cmd_unlock(priv); | ||
183 | return -ENOMEM; | ||
184 | } | ||
185 | |||
186 | /* ******************************************************************** */ | ||
187 | |||
188 | struct wsm_mib { | ||
189 | u16 mib_id; | ||
190 | void *buf; | ||
191 | size_t buf_size; | ||
192 | }; | ||
193 | |||
194 | int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *_buf, | ||
195 | size_t buf_size) | ||
196 | { | ||
197 | int ret; | ||
198 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
199 | struct wsm_mib mib_buf = { | ||
200 | .mib_id = mib_id, | ||
201 | .buf = _buf, | ||
202 | .buf_size = buf_size, | ||
203 | }; | ||
204 | wsm_cmd_lock(priv); | ||
205 | |||
206 | WSM_PUT16(buf, mib_id); | ||
207 | WSM_PUT16(buf, 0); | ||
208 | |||
209 | ret = wsm_cmd_send(priv, buf, &mib_buf, | ||
210 | WSM_READ_MIB_REQ_ID, WSM_CMD_TIMEOUT); | ||
211 | wsm_cmd_unlock(priv); | ||
212 | return ret; | ||
213 | |||
214 | nomem: | ||
215 | wsm_cmd_unlock(priv); | ||
216 | return -ENOMEM; | ||
217 | } | ||
218 | |||
219 | static int wsm_read_mib_confirm(struct cw1200_common *priv, | ||
220 | struct wsm_mib *arg, | ||
221 | struct wsm_buf *buf) | ||
222 | { | ||
223 | u16 size; | ||
224 | if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) | ||
225 | return -EINVAL; | ||
226 | |||
227 | if (WARN_ON(WSM_GET16(buf) != arg->mib_id)) | ||
228 | return -EINVAL; | ||
229 | |||
230 | size = WSM_GET16(buf); | ||
231 | if (size > arg->buf_size) | ||
232 | size = arg->buf_size; | ||
233 | |||
234 | WSM_GET(buf, arg->buf, size); | ||
235 | arg->buf_size = size; | ||
236 | return 0; | ||
237 | |||
238 | underflow: | ||
239 | WARN_ON(1); | ||
240 | return -EINVAL; | ||
241 | } | ||
242 | |||
243 | /* ******************************************************************** */ | ||
244 | |||
245 | int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *_buf, | ||
246 | size_t buf_size) | ||
247 | { | ||
248 | int ret; | ||
249 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
250 | struct wsm_mib mib_buf = { | ||
251 | .mib_id = mib_id, | ||
252 | .buf = _buf, | ||
253 | .buf_size = buf_size, | ||
254 | }; | ||
255 | |||
256 | wsm_cmd_lock(priv); | ||
257 | |||
258 | WSM_PUT16(buf, mib_id); | ||
259 | WSM_PUT16(buf, buf_size); | ||
260 | WSM_PUT(buf, _buf, buf_size); | ||
261 | |||
262 | ret = wsm_cmd_send(priv, buf, &mib_buf, | ||
263 | WSM_WRITE_MIB_REQ_ID, WSM_CMD_TIMEOUT); | ||
264 | wsm_cmd_unlock(priv); | ||
265 | return ret; | ||
266 | |||
267 | nomem: | ||
268 | wsm_cmd_unlock(priv); | ||
269 | return -ENOMEM; | ||
270 | } | ||
271 | |||
272 | static int wsm_write_mib_confirm(struct cw1200_common *priv, | ||
273 | struct wsm_mib *arg, | ||
274 | struct wsm_buf *buf) | ||
275 | { | ||
276 | int ret; | ||
277 | |||
278 | ret = wsm_generic_confirm(priv, arg, buf); | ||
279 | if (ret) | ||
280 | return ret; | ||
281 | |||
282 | if (arg->mib_id == WSM_MIB_ID_OPERATIONAL_POWER_MODE) { | ||
283 | /* OperationalMode: update PM status. */ | ||
284 | const char *p = arg->buf; | ||
285 | cw1200_enable_powersave(priv, (p[0] & 0x0F) ? true : false); | ||
286 | } | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | /* ******************************************************************** */ | ||
291 | |||
292 | int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg) | ||
293 | { | ||
294 | int i; | ||
295 | int ret; | ||
296 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
297 | |||
298 | if (arg->num_channels > 48) | ||
299 | return -EINVAL; | ||
300 | |||
301 | if (arg->num_ssids > 2) | ||
302 | return -EINVAL; | ||
303 | |||
304 | if (arg->band > 1) | ||
305 | return -EINVAL; | ||
306 | |||
307 | wsm_cmd_lock(priv); | ||
308 | |||
309 | WSM_PUT8(buf, arg->band); | ||
310 | WSM_PUT8(buf, arg->type); | ||
311 | WSM_PUT8(buf, arg->flags); | ||
312 | WSM_PUT8(buf, arg->max_tx_rate); | ||
313 | WSM_PUT32(buf, arg->auto_scan_interval); | ||
314 | WSM_PUT8(buf, arg->num_probes); | ||
315 | WSM_PUT8(buf, arg->num_channels); | ||
316 | WSM_PUT8(buf, arg->num_ssids); | ||
317 | WSM_PUT8(buf, arg->probe_delay); | ||
318 | |||
319 | for (i = 0; i < arg->num_channels; ++i) { | ||
320 | WSM_PUT16(buf, arg->ch[i].number); | ||
321 | WSM_PUT16(buf, 0); | ||
322 | WSM_PUT32(buf, arg->ch[i].min_chan_time); | ||
323 | WSM_PUT32(buf, arg->ch[i].max_chan_time); | ||
324 | WSM_PUT32(buf, 0); | ||
325 | } | ||
326 | |||
327 | for (i = 0; i < arg->num_ssids; ++i) { | ||
328 | WSM_PUT32(buf, arg->ssids[i].length); | ||
329 | WSM_PUT(buf, &arg->ssids[i].ssid[0], | ||
330 | sizeof(arg->ssids[i].ssid)); | ||
331 | } | ||
332 | |||
333 | ret = wsm_cmd_send(priv, buf, NULL, | ||
334 | WSM_START_SCAN_REQ_ID, WSM_CMD_TIMEOUT); | ||
335 | wsm_cmd_unlock(priv); | ||
336 | return ret; | ||
337 | |||
338 | nomem: | ||
339 | wsm_cmd_unlock(priv); | ||
340 | return -ENOMEM; | ||
341 | } | ||
342 | |||
343 | /* ******************************************************************** */ | ||
344 | |||
345 | int wsm_stop_scan(struct cw1200_common *priv) | ||
346 | { | ||
347 | int ret; | ||
348 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
349 | wsm_cmd_lock(priv); | ||
350 | ret = wsm_cmd_send(priv, buf, NULL, | ||
351 | WSM_STOP_SCAN_REQ_ID, WSM_CMD_TIMEOUT); | ||
352 | wsm_cmd_unlock(priv); | ||
353 | return ret; | ||
354 | } | ||
355 | |||
356 | |||
357 | static int wsm_tx_confirm(struct cw1200_common *priv, | ||
358 | struct wsm_buf *buf, | ||
359 | int link_id) | ||
360 | { | ||
361 | struct wsm_tx_confirm tx_confirm; | ||
362 | |||
363 | tx_confirm.packet_id = WSM_GET32(buf); | ||
364 | tx_confirm.status = WSM_GET32(buf); | ||
365 | tx_confirm.tx_rate = WSM_GET8(buf); | ||
366 | tx_confirm.ack_failures = WSM_GET8(buf); | ||
367 | tx_confirm.flags = WSM_GET16(buf); | ||
368 | tx_confirm.media_delay = WSM_GET32(buf); | ||
369 | tx_confirm.tx_queue_delay = WSM_GET32(buf); | ||
370 | |||
371 | cw1200_tx_confirm_cb(priv, link_id, &tx_confirm); | ||
372 | return 0; | ||
373 | |||
374 | underflow: | ||
375 | WARN_ON(1); | ||
376 | return -EINVAL; | ||
377 | } | ||
378 | |||
379 | static int wsm_multi_tx_confirm(struct cw1200_common *priv, | ||
380 | struct wsm_buf *buf, int link_id) | ||
381 | { | ||
382 | int ret; | ||
383 | int count; | ||
384 | int i; | ||
385 | |||
386 | count = WSM_GET32(buf); | ||
387 | if (WARN_ON(count <= 0)) | ||
388 | return -EINVAL; | ||
389 | |||
390 | if (count > 1) { | ||
391 | /* We already released one buffer, now for the rest */ | ||
392 | ret = wsm_release_tx_buffer(priv, count - 1); | ||
393 | if (ret < 0) | ||
394 | return ret; | ||
395 | else if (ret > 0) | ||
396 | cw1200_bh_wakeup(priv); | ||
397 | } | ||
398 | |||
399 | cw1200_debug_txed_multi(priv, count); | ||
400 | for (i = 0; i < count; ++i) { | ||
401 | ret = wsm_tx_confirm(priv, buf, link_id); | ||
402 | if (ret) | ||
403 | return ret; | ||
404 | } | ||
405 | return ret; | ||
406 | |||
407 | underflow: | ||
408 | WARN_ON(1); | ||
409 | return -EINVAL; | ||
410 | } | ||
411 | |||
412 | /* ******************************************************************** */ | ||
413 | |||
414 | static int wsm_join_confirm(struct cw1200_common *priv, | ||
415 | struct wsm_join_cnf *arg, | ||
416 | struct wsm_buf *buf) | ||
417 | { | ||
418 | arg->status = WSM_GET32(buf); | ||
419 | if (WARN_ON(arg->status) != WSM_STATUS_SUCCESS) | ||
420 | return -EINVAL; | ||
421 | |||
422 | arg->min_power_level = WSM_GET32(buf); | ||
423 | arg->max_power_level = WSM_GET32(buf); | ||
424 | |||
425 | return 0; | ||
426 | |||
427 | underflow: | ||
428 | WARN_ON(1); | ||
429 | return -EINVAL; | ||
430 | } | ||
431 | |||
432 | int wsm_join(struct cw1200_common *priv, struct wsm_join *arg) | ||
433 | { | ||
434 | int ret; | ||
435 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
436 | struct wsm_join_cnf resp; | ||
437 | wsm_cmd_lock(priv); | ||
438 | |||
439 | WSM_PUT8(buf, arg->mode); | ||
440 | WSM_PUT8(buf, arg->band); | ||
441 | WSM_PUT16(buf, arg->channel_number); | ||
442 | WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid)); | ||
443 | WSM_PUT16(buf, arg->atim_window); | ||
444 | WSM_PUT8(buf, arg->preamble_type); | ||
445 | WSM_PUT8(buf, arg->probe_for_join); | ||
446 | WSM_PUT8(buf, arg->dtim_period); | ||
447 | WSM_PUT8(buf, arg->flags); | ||
448 | WSM_PUT32(buf, arg->ssid_len); | ||
449 | WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid)); | ||
450 | WSM_PUT32(buf, arg->beacon_interval); | ||
451 | WSM_PUT32(buf, arg->basic_rate_set); | ||
452 | |||
453 | priv->tx_burst_idx = -1; | ||
454 | ret = wsm_cmd_send(priv, buf, &resp, | ||
455 | WSM_JOIN_REQ_ID, WSM_CMD_TIMEOUT); | ||
456 | /* TODO: Update state based on resp.min|max_power_level */ | ||
457 | |||
458 | priv->join_complete_status = resp.status; | ||
459 | |||
460 | wsm_cmd_unlock(priv); | ||
461 | return ret; | ||
462 | |||
463 | nomem: | ||
464 | wsm_cmd_unlock(priv); | ||
465 | return -ENOMEM; | ||
466 | } | ||
467 | |||
468 | /* ******************************************************************** */ | ||
469 | |||
470 | int wsm_set_bss_params(struct cw1200_common *priv, | ||
471 | const struct wsm_set_bss_params *arg) | ||
472 | { | ||
473 | int ret; | ||
474 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
475 | |||
476 | wsm_cmd_lock(priv); | ||
477 | |||
478 | WSM_PUT8(buf, (arg->reset_beacon_loss ? 0x1 : 0)); | ||
479 | WSM_PUT8(buf, arg->beacon_lost_count); | ||
480 | WSM_PUT16(buf, arg->aid); | ||
481 | WSM_PUT32(buf, arg->operational_rate_set); | ||
482 | |||
483 | ret = wsm_cmd_send(priv, buf, NULL, | ||
484 | WSM_SET_BSS_PARAMS_REQ_ID, WSM_CMD_TIMEOUT); | ||
485 | |||
486 | wsm_cmd_unlock(priv); | ||
487 | return ret; | ||
488 | |||
489 | nomem: | ||
490 | wsm_cmd_unlock(priv); | ||
491 | return -ENOMEM; | ||
492 | } | ||
493 | |||
494 | /* ******************************************************************** */ | ||
495 | |||
496 | int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg) | ||
497 | { | ||
498 | int ret; | ||
499 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
500 | |||
501 | wsm_cmd_lock(priv); | ||
502 | |||
503 | WSM_PUT(buf, arg, sizeof(*arg)); | ||
504 | |||
505 | ret = wsm_cmd_send(priv, buf, NULL, | ||
506 | WSM_ADD_KEY_REQ_ID, WSM_CMD_TIMEOUT); | ||
507 | |||
508 | wsm_cmd_unlock(priv); | ||
509 | return ret; | ||
510 | |||
511 | nomem: | ||
512 | wsm_cmd_unlock(priv); | ||
513 | return -ENOMEM; | ||
514 | } | ||
515 | |||
516 | /* ******************************************************************** */ | ||
517 | |||
518 | int wsm_remove_key(struct cw1200_common *priv, const struct wsm_remove_key *arg) | ||
519 | { | ||
520 | int ret; | ||
521 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
522 | |||
523 | wsm_cmd_lock(priv); | ||
524 | |||
525 | WSM_PUT8(buf, arg->index); | ||
526 | WSM_PUT8(buf, 0); | ||
527 | WSM_PUT16(buf, 0); | ||
528 | |||
529 | ret = wsm_cmd_send(priv, buf, NULL, | ||
530 | WSM_REMOVE_KEY_REQ_ID, WSM_CMD_TIMEOUT); | ||
531 | |||
532 | wsm_cmd_unlock(priv); | ||
533 | return ret; | ||
534 | |||
535 | nomem: | ||
536 | wsm_cmd_unlock(priv); | ||
537 | return -ENOMEM; | ||
538 | } | ||
539 | |||
540 | /* ******************************************************************** */ | ||
541 | |||
542 | int wsm_set_tx_queue_params(struct cw1200_common *priv, | ||
543 | const struct wsm_set_tx_queue_params *arg, u8 id) | ||
544 | { | ||
545 | int ret; | ||
546 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
547 | u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1}; | ||
548 | |||
549 | wsm_cmd_lock(priv); | ||
550 | |||
551 | WSM_PUT8(buf, queue_id_to_wmm_aci[id]); | ||
552 | WSM_PUT8(buf, 0); | ||
553 | WSM_PUT8(buf, arg->ackPolicy); | ||
554 | WSM_PUT8(buf, 0); | ||
555 | WSM_PUT32(buf, arg->maxTransmitLifetime); | ||
556 | WSM_PUT16(buf, arg->allowedMediumTime); | ||
557 | WSM_PUT16(buf, 0); | ||
558 | |||
559 | ret = wsm_cmd_send(priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT); | ||
560 | |||
561 | wsm_cmd_unlock(priv); | ||
562 | return ret; | ||
563 | |||
564 | nomem: | ||
565 | wsm_cmd_unlock(priv); | ||
566 | return -ENOMEM; | ||
567 | } | ||
568 | |||
569 | /* ******************************************************************** */ | ||
570 | |||
571 | int wsm_set_edca_params(struct cw1200_common *priv, | ||
572 | const struct wsm_edca_params *arg) | ||
573 | { | ||
574 | int ret; | ||
575 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
576 | |||
577 | wsm_cmd_lock(priv); | ||
578 | |||
579 | /* Implemented according to specification. */ | ||
580 | |||
581 | WSM_PUT16(buf, arg->params[3].cwmin); | ||
582 | WSM_PUT16(buf, arg->params[2].cwmin); | ||
583 | WSM_PUT16(buf, arg->params[1].cwmin); | ||
584 | WSM_PUT16(buf, arg->params[0].cwmin); | ||
585 | |||
586 | WSM_PUT16(buf, arg->params[3].cwmax); | ||
587 | WSM_PUT16(buf, arg->params[2].cwmax); | ||
588 | WSM_PUT16(buf, arg->params[1].cwmax); | ||
589 | WSM_PUT16(buf, arg->params[0].cwmax); | ||
590 | |||
591 | WSM_PUT8(buf, arg->params[3].aifns); | ||
592 | WSM_PUT8(buf, arg->params[2].aifns); | ||
593 | WSM_PUT8(buf, arg->params[1].aifns); | ||
594 | WSM_PUT8(buf, arg->params[0].aifns); | ||
595 | |||
596 | WSM_PUT16(buf, arg->params[3].txop_limit); | ||
597 | WSM_PUT16(buf, arg->params[2].txop_limit); | ||
598 | WSM_PUT16(buf, arg->params[1].txop_limit); | ||
599 | WSM_PUT16(buf, arg->params[0].txop_limit); | ||
600 | |||
601 | WSM_PUT32(buf, arg->params[3].max_rx_lifetime); | ||
602 | WSM_PUT32(buf, arg->params[2].max_rx_lifetime); | ||
603 | WSM_PUT32(buf, arg->params[1].max_rx_lifetime); | ||
604 | WSM_PUT32(buf, arg->params[0].max_rx_lifetime); | ||
605 | |||
606 | ret = wsm_cmd_send(priv, buf, NULL, | ||
607 | WSM_EDCA_PARAMS_REQ_ID, WSM_CMD_TIMEOUT); | ||
608 | wsm_cmd_unlock(priv); | ||
609 | return ret; | ||
610 | |||
611 | nomem: | ||
612 | wsm_cmd_unlock(priv); | ||
613 | return -ENOMEM; | ||
614 | } | ||
615 | |||
616 | /* ******************************************************************** */ | ||
617 | |||
618 | int wsm_switch_channel(struct cw1200_common *priv, | ||
619 | const struct wsm_switch_channel *arg) | ||
620 | { | ||
621 | int ret; | ||
622 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
623 | |||
624 | wsm_cmd_lock(priv); | ||
625 | |||
626 | WSM_PUT8(buf, arg->mode); | ||
627 | WSM_PUT8(buf, arg->switch_count); | ||
628 | WSM_PUT16(buf, arg->channel_number); | ||
629 | |||
630 | priv->channel_switch_in_progress = 1; | ||
631 | |||
632 | ret = wsm_cmd_send(priv, buf, NULL, | ||
633 | WSM_SWITCH_CHANNEL_REQ_ID, WSM_CMD_TIMEOUT); | ||
634 | if (ret) | ||
635 | priv->channel_switch_in_progress = 0; | ||
636 | |||
637 | wsm_cmd_unlock(priv); | ||
638 | return ret; | ||
639 | |||
640 | nomem: | ||
641 | wsm_cmd_unlock(priv); | ||
642 | return -ENOMEM; | ||
643 | } | ||
644 | |||
645 | /* ******************************************************************** */ | ||
646 | |||
647 | int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) | ||
648 | { | ||
649 | int ret; | ||
650 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
651 | priv->ps_mode_switch_in_progress = 1; | ||
652 | |||
653 | wsm_cmd_lock(priv); | ||
654 | |||
655 | WSM_PUT8(buf, arg->mode); | ||
656 | WSM_PUT8(buf, arg->fast_psm_idle_period); | ||
657 | WSM_PUT8(buf, arg->ap_psm_change_period); | ||
658 | WSM_PUT8(buf, arg->min_auto_pspoll_period); | ||
659 | |||
660 | ret = wsm_cmd_send(priv, buf, NULL, | ||
661 | WSM_SET_PM_REQ_ID, WSM_CMD_TIMEOUT); | ||
662 | |||
663 | wsm_cmd_unlock(priv); | ||
664 | return ret; | ||
665 | |||
666 | nomem: | ||
667 | wsm_cmd_unlock(priv); | ||
668 | return -ENOMEM; | ||
669 | } | ||
670 | |||
671 | /* ******************************************************************** */ | ||
672 | |||
673 | int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg) | ||
674 | { | ||
675 | int ret; | ||
676 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
677 | |||
678 | wsm_cmd_lock(priv); | ||
679 | |||
680 | WSM_PUT8(buf, arg->mode); | ||
681 | WSM_PUT8(buf, arg->band); | ||
682 | WSM_PUT16(buf, arg->channel_number); | ||
683 | WSM_PUT32(buf, arg->ct_window); | ||
684 | WSM_PUT32(buf, arg->beacon_interval); | ||
685 | WSM_PUT8(buf, arg->dtim_period); | ||
686 | WSM_PUT8(buf, arg->preamble); | ||
687 | WSM_PUT8(buf, arg->probe_delay); | ||
688 | WSM_PUT8(buf, arg->ssid_len); | ||
689 | WSM_PUT(buf, arg->ssid, sizeof(arg->ssid)); | ||
690 | WSM_PUT32(buf, arg->basic_rate_set); | ||
691 | |||
692 | priv->tx_burst_idx = -1; | ||
693 | ret = wsm_cmd_send(priv, buf, NULL, | ||
694 | WSM_START_REQ_ID, WSM_CMD_START_TIMEOUT); | ||
695 | |||
696 | wsm_cmd_unlock(priv); | ||
697 | return ret; | ||
698 | |||
699 | nomem: | ||
700 | wsm_cmd_unlock(priv); | ||
701 | return -ENOMEM; | ||
702 | } | ||
703 | |||
704 | /* ******************************************************************** */ | ||
705 | |||
706 | int wsm_beacon_transmit(struct cw1200_common *priv, | ||
707 | const struct wsm_beacon_transmit *arg) | ||
708 | { | ||
709 | int ret; | ||
710 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
711 | |||
712 | wsm_cmd_lock(priv); | ||
713 | |||
714 | WSM_PUT32(buf, arg->enable_beaconing ? 1 : 0); | ||
715 | |||
716 | ret = wsm_cmd_send(priv, buf, NULL, | ||
717 | WSM_BEACON_TRANSMIT_REQ_ID, WSM_CMD_TIMEOUT); | ||
718 | |||
719 | wsm_cmd_unlock(priv); | ||
720 | return ret; | ||
721 | |||
722 | nomem: | ||
723 | wsm_cmd_unlock(priv); | ||
724 | return -ENOMEM; | ||
725 | } | ||
726 | |||
727 | /* ******************************************************************** */ | ||
728 | |||
729 | int wsm_start_find(struct cw1200_common *priv) | ||
730 | { | ||
731 | int ret; | ||
732 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
733 | |||
734 | wsm_cmd_lock(priv); | ||
735 | ret = wsm_cmd_send(priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT); | ||
736 | wsm_cmd_unlock(priv); | ||
737 | return ret; | ||
738 | } | ||
739 | |||
740 | /* ******************************************************************** */ | ||
741 | |||
742 | int wsm_stop_find(struct cw1200_common *priv) | ||
743 | { | ||
744 | int ret; | ||
745 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
746 | |||
747 | wsm_cmd_lock(priv); | ||
748 | ret = wsm_cmd_send(priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT); | ||
749 | wsm_cmd_unlock(priv); | ||
750 | return ret; | ||
751 | } | ||
752 | |||
753 | /* ******************************************************************** */ | ||
754 | |||
755 | int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg) | ||
756 | { | ||
757 | int ret; | ||
758 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
759 | u16 cmd = 0x001C | WSM_TX_LINK_ID(arg->link_id); | ||
760 | |||
761 | wsm_cmd_lock(priv); | ||
762 | |||
763 | WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr)); | ||
764 | WSM_PUT16(buf, 0); | ||
765 | |||
766 | ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT); | ||
767 | |||
768 | wsm_cmd_unlock(priv); | ||
769 | return ret; | ||
770 | |||
771 | nomem: | ||
772 | wsm_cmd_unlock(priv); | ||
773 | return -ENOMEM; | ||
774 | } | ||
775 | |||
776 | /* ******************************************************************** */ | ||
777 | |||
778 | int wsm_update_ie(struct cw1200_common *priv, | ||
779 | const struct wsm_update_ie *arg) | ||
780 | { | ||
781 | int ret; | ||
782 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
783 | |||
784 | wsm_cmd_lock(priv); | ||
785 | |||
786 | WSM_PUT16(buf, arg->what); | ||
787 | WSM_PUT16(buf, arg->count); | ||
788 | WSM_PUT(buf, arg->ies, arg->length); | ||
789 | |||
790 | ret = wsm_cmd_send(priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT); | ||
791 | |||
792 | wsm_cmd_unlock(priv); | ||
793 | return ret; | ||
794 | |||
795 | nomem: | ||
796 | wsm_cmd_unlock(priv); | ||
797 | return -ENOMEM; | ||
798 | } | ||
799 | |||
800 | /* ******************************************************************** */ | ||
801 | int wsm_set_probe_responder(struct cw1200_common *priv, bool enable) | ||
802 | { | ||
803 | priv->rx_filter.probeResponder = enable; | ||
804 | return wsm_set_rx_filter(priv, &priv->rx_filter); | ||
805 | } | ||
806 | |||
807 | /* ******************************************************************** */ | ||
808 | /* WSM indication events implementation */ | ||
809 | const char * const cw1200_fw_types[] = { | ||
810 | "ETF", | ||
811 | "WFM", | ||
812 | "WSM", | ||
813 | "HI test", | ||
814 | "Platform test" | ||
815 | }; | ||
816 | |||
817 | static int wsm_startup_indication(struct cw1200_common *priv, | ||
818 | struct wsm_buf *buf) | ||
819 | { | ||
820 | priv->wsm_caps.input_buffers = WSM_GET16(buf); | ||
821 | priv->wsm_caps.input_buffer_size = WSM_GET16(buf); | ||
822 | priv->wsm_caps.hw_id = WSM_GET16(buf); | ||
823 | priv->wsm_caps.hw_subid = WSM_GET16(buf); | ||
824 | priv->wsm_caps.status = WSM_GET16(buf); | ||
825 | priv->wsm_caps.fw_cap = WSM_GET16(buf); | ||
826 | priv->wsm_caps.fw_type = WSM_GET16(buf); | ||
827 | priv->wsm_caps.fw_api = WSM_GET16(buf); | ||
828 | priv->wsm_caps.fw_build = WSM_GET16(buf); | ||
829 | priv->wsm_caps.fw_ver = WSM_GET16(buf); | ||
830 | WSM_GET(buf, priv->wsm_caps.fw_label, sizeof(priv->wsm_caps.fw_label)); | ||
831 | priv->wsm_caps.fw_label[sizeof(priv->wsm_caps.fw_label) - 1] = 0; /* Do not trust FW too much... */ | ||
832 | |||
833 | if (WARN_ON(priv->wsm_caps.status)) | ||
834 | return -EINVAL; | ||
835 | |||
836 | if (WARN_ON(priv->wsm_caps.fw_type > 4)) | ||
837 | return -EINVAL; | ||
838 | |||
839 | pr_info("CW1200 WSM init done.\n" | ||
840 | " Input buffers: %d x %d bytes\n" | ||
841 | " Hardware: %d.%d\n" | ||
842 | " %s firmware [%s], ver: %d, build: %d," | ||
843 | " api: %d, cap: 0x%.4X\n", | ||
844 | priv->wsm_caps.input_buffers, | ||
845 | priv->wsm_caps.input_buffer_size, | ||
846 | priv->wsm_caps.hw_id, priv->wsm_caps.hw_subid, | ||
847 | cw1200_fw_types[priv->wsm_caps.fw_type], | ||
848 | priv->wsm_caps.fw_label, priv->wsm_caps.fw_ver, | ||
849 | priv->wsm_caps.fw_build, | ||
850 | priv->wsm_caps.fw_api, priv->wsm_caps.fw_cap); | ||
851 | |||
852 | /* Disable unsupported frequency bands */ | ||
853 | if (!(priv->wsm_caps.fw_cap & 0x1)) | ||
854 | priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; | ||
855 | if (!(priv->wsm_caps.fw_cap & 0x2)) | ||
856 | priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; | ||
857 | |||
858 | priv->firmware_ready = 1; | ||
859 | wake_up(&priv->wsm_startup_done); | ||
860 | return 0; | ||
861 | |||
862 | underflow: | ||
863 | WARN_ON(1); | ||
864 | return -EINVAL; | ||
865 | } | ||
866 | |||
867 | static int wsm_receive_indication(struct cw1200_common *priv, | ||
868 | int link_id, | ||
869 | struct wsm_buf *buf, | ||
870 | struct sk_buff **skb_p) | ||
871 | { | ||
872 | struct wsm_rx rx; | ||
873 | struct ieee80211_hdr *hdr; | ||
874 | size_t hdr_len; | ||
875 | __le16 fctl; | ||
876 | |||
877 | rx.status = WSM_GET32(buf); | ||
878 | rx.channel_number = WSM_GET16(buf); | ||
879 | rx.rx_rate = WSM_GET8(buf); | ||
880 | rx.rcpi_rssi = WSM_GET8(buf); | ||
881 | rx.flags = WSM_GET32(buf); | ||
882 | |||
883 | /* FW Workaround: Drop probe resp or | ||
884 | beacon when RSSI is 0 | ||
885 | */ | ||
886 | hdr = (struct ieee80211_hdr *)(*skb_p)->data; | ||
887 | |||
888 | if (!rx.rcpi_rssi && | ||
889 | (ieee80211_is_probe_resp(hdr->frame_control) || | ||
890 | ieee80211_is_beacon(hdr->frame_control))) | ||
891 | return 0; | ||
892 | |||
893 | /* If no RSSI subscription has been made, | ||
894 | * convert RCPI to RSSI here | ||
895 | */ | ||
896 | if (!priv->cqm_use_rssi) | ||
897 | rx.rcpi_rssi = rx.rcpi_rssi / 2 - 110; | ||
898 | |||
899 | fctl = *(__le16 *)buf->data; | ||
900 | hdr_len = buf->data - buf->begin; | ||
901 | skb_pull(*skb_p, hdr_len); | ||
902 | if (!rx.status && ieee80211_is_deauth(fctl)) { | ||
903 | if (priv->join_status == CW1200_JOIN_STATUS_STA) { | ||
904 | /* Shedule unjoin work */ | ||
905 | pr_debug("[WSM] Issue unjoin command (RX).\n"); | ||
906 | wsm_lock_tx_async(priv); | ||
907 | if (queue_work(priv->workqueue, | ||
908 | &priv->unjoin_work) <= 0) | ||
909 | wsm_unlock_tx(priv); | ||
910 | } | ||
911 | } | ||
912 | cw1200_rx_cb(priv, &rx, link_id, skb_p); | ||
913 | if (*skb_p) | ||
914 | skb_push(*skb_p, hdr_len); | ||
915 | |||
916 | return 0; | ||
917 | |||
918 | underflow: | ||
919 | return -EINVAL; | ||
920 | } | ||
921 | |||
922 | static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf) | ||
923 | { | ||
924 | int first; | ||
925 | struct cw1200_wsm_event *event; | ||
926 | |||
927 | if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { | ||
928 | /* STA is stopped. */ | ||
929 | return 0; | ||
930 | } | ||
931 | |||
932 | event = kzalloc(sizeof(struct cw1200_wsm_event), GFP_KERNEL); | ||
933 | |||
934 | event->evt.id = __le32_to_cpu(WSM_GET32(buf)); | ||
935 | event->evt.data = __le32_to_cpu(WSM_GET32(buf)); | ||
936 | |||
937 | pr_debug("[WSM] Event: %d(%d)\n", | ||
938 | event->evt.id, event->evt.data); | ||
939 | |||
940 | spin_lock(&priv->event_queue_lock); | ||
941 | first = list_empty(&priv->event_queue); | ||
942 | list_add_tail(&event->link, &priv->event_queue); | ||
943 | spin_unlock(&priv->event_queue_lock); | ||
944 | |||
945 | if (first) | ||
946 | queue_work(priv->workqueue, &priv->event_handler); | ||
947 | |||
948 | return 0; | ||
949 | |||
950 | underflow: | ||
951 | kfree(event); | ||
952 | return -EINVAL; | ||
953 | } | ||
954 | |||
955 | static int wsm_channel_switch_indication(struct cw1200_common *priv, | ||
956 | struct wsm_buf *buf) | ||
957 | { | ||
958 | WARN_ON(WSM_GET32(buf)); | ||
959 | |||
960 | priv->channel_switch_in_progress = 0; | ||
961 | wake_up(&priv->channel_switch_done); | ||
962 | |||
963 | wsm_unlock_tx(priv); | ||
964 | |||
965 | return 0; | ||
966 | |||
967 | underflow: | ||
968 | return -EINVAL; | ||
969 | } | ||
970 | |||
971 | static int wsm_set_pm_indication(struct cw1200_common *priv, | ||
972 | struct wsm_buf *buf) | ||
973 | { | ||
974 | /* TODO: Check buf (struct wsm_set_pm_complete) for validity */ | ||
975 | if (priv->ps_mode_switch_in_progress) { | ||
976 | priv->ps_mode_switch_in_progress = 0; | ||
977 | wake_up(&priv->ps_mode_switch_done); | ||
978 | } | ||
979 | return 0; | ||
980 | } | ||
981 | |||
982 | static int wsm_scan_started(struct cw1200_common *priv, void *arg, | ||
983 | struct wsm_buf *buf) | ||
984 | { | ||
985 | u32 status = WSM_GET32(buf); | ||
986 | if (status != WSM_STATUS_SUCCESS) { | ||
987 | cw1200_scan_failed_cb(priv); | ||
988 | return -EINVAL; | ||
989 | } | ||
990 | return 0; | ||
991 | |||
992 | underflow: | ||
993 | WARN_ON(1); | ||
994 | return -EINVAL; | ||
995 | } | ||
996 | |||
997 | static int wsm_scan_complete_indication(struct cw1200_common *priv, | ||
998 | struct wsm_buf *buf) | ||
999 | { | ||
1000 | struct wsm_scan_complete arg; | ||
1001 | arg.status = WSM_GET32(buf); | ||
1002 | arg.psm = WSM_GET8(buf); | ||
1003 | arg.num_channels = WSM_GET8(buf); | ||
1004 | cw1200_scan_complete_cb(priv, &arg); | ||
1005 | |||
1006 | return 0; | ||
1007 | |||
1008 | underflow: | ||
1009 | return -EINVAL; | ||
1010 | } | ||
1011 | |||
1012 | static int wsm_join_complete_indication(struct cw1200_common *priv, | ||
1013 | struct wsm_buf *buf) | ||
1014 | { | ||
1015 | struct wsm_join_complete arg; | ||
1016 | arg.status = WSM_GET32(buf); | ||
1017 | pr_debug("[WSM] Join complete indication, status: %d\n", arg.status); | ||
1018 | cw1200_join_complete_cb(priv, &arg); | ||
1019 | |||
1020 | return 0; | ||
1021 | |||
1022 | underflow: | ||
1023 | return -EINVAL; | ||
1024 | } | ||
1025 | |||
1026 | static int wsm_find_complete_indication(struct cw1200_common *priv, | ||
1027 | struct wsm_buf *buf) | ||
1028 | { | ||
1029 | pr_warn("Implement find_complete_indication\n"); | ||
1030 | return 0; | ||
1031 | } | ||
1032 | |||
1033 | static int wsm_ba_timeout_indication(struct cw1200_common *priv, | ||
1034 | struct wsm_buf *buf) | ||
1035 | { | ||
1036 | u32 dummy; | ||
1037 | u8 tid; | ||
1038 | u8 dummy2; | ||
1039 | u8 addr[ETH_ALEN]; | ||
1040 | |||
1041 | dummy = WSM_GET32(buf); | ||
1042 | tid = WSM_GET8(buf); | ||
1043 | dummy2 = WSM_GET8(buf); | ||
1044 | WSM_GET(buf, addr, ETH_ALEN); | ||
1045 | |||
1046 | pr_info("BlockACK timeout, tid %d, addr %pM\n", | ||
1047 | tid, addr); | ||
1048 | |||
1049 | return 0; | ||
1050 | |||
1051 | underflow: | ||
1052 | return -EINVAL; | ||
1053 | } | ||
1054 | |||
1055 | static int wsm_suspend_resume_indication(struct cw1200_common *priv, | ||
1056 | int link_id, struct wsm_buf *buf) | ||
1057 | { | ||
1058 | u32 flags; | ||
1059 | struct wsm_suspend_resume arg; | ||
1060 | |||
1061 | flags = WSM_GET32(buf); | ||
1062 | arg.link_id = link_id; | ||
1063 | arg.stop = !(flags & 1); | ||
1064 | arg.multicast = !!(flags & 8); | ||
1065 | arg.queue = (flags >> 1) & 3; | ||
1066 | |||
1067 | cw1200_suspend_resume(priv, &arg); | ||
1068 | |||
1069 | return 0; | ||
1070 | |||
1071 | underflow: | ||
1072 | return -EINVAL; | ||
1073 | } | ||
1074 | |||
1075 | |||
1076 | /* ******************************************************************** */ | ||
1077 | /* WSM TX */ | ||
1078 | |||
1079 | static int wsm_cmd_send(struct cw1200_common *priv, | ||
1080 | struct wsm_buf *buf, | ||
1081 | void *arg, u16 cmd, long tmo) | ||
1082 | { | ||
1083 | size_t buf_len = buf->data - buf->begin; | ||
1084 | int ret; | ||
1085 | |||
1086 | /* Don't bother if we're dead. */ | ||
1087 | if (priv->bh_error) { | ||
1088 | ret = 0; | ||
1089 | goto done; | ||
1090 | } | ||
1091 | |||
1092 | /* Block until the cmd buffer is completed. Tortuous. */ | ||
1093 | spin_lock(&priv->wsm_cmd.lock); | ||
1094 | while (!priv->wsm_cmd.done) { | ||
1095 | spin_unlock(&priv->wsm_cmd.lock); | ||
1096 | spin_lock(&priv->wsm_cmd.lock); | ||
1097 | } | ||
1098 | priv->wsm_cmd.done = 0; | ||
1099 | spin_unlock(&priv->wsm_cmd.lock); | ||
1100 | |||
1101 | if (cmd == WSM_WRITE_MIB_REQ_ID || | ||
1102 | cmd == WSM_READ_MIB_REQ_ID) | ||
1103 | pr_debug("[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%zu)\n", | ||
1104 | cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]), | ||
1105 | buf_len); | ||
1106 | else | ||
1107 | pr_debug("[WSM] >>> 0x%.4X (%zu)\n", cmd, buf_len); | ||
1108 | |||
1109 | /* | ||
1110 | * Due to buggy SPI on CW1200, we need to | ||
1111 | * pad the message by a few bytes to ensure | ||
1112 | * that it's completely received. | ||
1113 | */ | ||
1114 | #ifdef CONFIG_CW1200_ETF | ||
1115 | if (!etf_mode) | ||
1116 | #endif | ||
1117 | buf_len += 4; | ||
1118 | |||
1119 | /* Fill HI message header */ | ||
1120 | /* BH will add sequence number */ | ||
1121 | ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); | ||
1122 | ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd); | ||
1123 | |||
1124 | spin_lock(&priv->wsm_cmd.lock); | ||
1125 | BUG_ON(priv->wsm_cmd.ptr); | ||
1126 | priv->wsm_cmd.ptr = buf->begin; | ||
1127 | priv->wsm_cmd.len = buf_len; | ||
1128 | priv->wsm_cmd.arg = arg; | ||
1129 | priv->wsm_cmd.cmd = cmd; | ||
1130 | spin_unlock(&priv->wsm_cmd.lock); | ||
1131 | |||
1132 | cw1200_bh_wakeup(priv); | ||
1133 | |||
1134 | /* Wait for command completion */ | ||
1135 | ret = wait_event_timeout(priv->wsm_cmd_wq, | ||
1136 | priv->wsm_cmd.done, tmo); | ||
1137 | |||
1138 | if (!ret && !priv->wsm_cmd.done) { | ||
1139 | spin_lock(&priv->wsm_cmd.lock); | ||
1140 | priv->wsm_cmd.done = 1; | ||
1141 | priv->wsm_cmd.ptr = NULL; | ||
1142 | spin_unlock(&priv->wsm_cmd.lock); | ||
1143 | if (priv->bh_error) { | ||
1144 | /* Return ok to help system cleanup */ | ||
1145 | ret = 0; | ||
1146 | } else { | ||
1147 | pr_err("CMD req (0x%04x) stuck in firmware, killing BH\n", priv->wsm_cmd.cmd); | ||
1148 | print_hex_dump_bytes("REQDUMP: ", DUMP_PREFIX_NONE, | ||
1149 | buf->begin, buf_len); | ||
1150 | pr_err("Outstanding outgoing frames: %d\n", priv->hw_bufs_used); | ||
1151 | |||
1152 | /* Kill BH thread to report the error to the top layer. */ | ||
1153 | atomic_add(1, &priv->bh_term); | ||
1154 | wake_up(&priv->bh_wq); | ||
1155 | ret = -ETIMEDOUT; | ||
1156 | } | ||
1157 | } else { | ||
1158 | spin_lock(&priv->wsm_cmd.lock); | ||
1159 | BUG_ON(!priv->wsm_cmd.done); | ||
1160 | ret = priv->wsm_cmd.ret; | ||
1161 | spin_unlock(&priv->wsm_cmd.lock); | ||
1162 | } | ||
1163 | done: | ||
1164 | wsm_buf_reset(buf); | ||
1165 | return ret; | ||
1166 | } | ||
1167 | |||
1168 | #ifdef CONFIG_CW1200_ETF | ||
1169 | int wsm_raw_cmd(struct cw1200_common *priv, u8 *data, size_t len) | ||
1170 | { | ||
1171 | struct wsm_buf *buf = &priv->wsm_cmd_buf; | ||
1172 | int ret; | ||
1173 | |||
1174 | u16 *cmd = (u16 *)(data + 2); | ||
1175 | |||
1176 | wsm_cmd_lock(priv); | ||
1177 | |||
1178 | WSM_PUT(buf, data + 4, len - 4); /* Skip over header (u16+u16) */ | ||
1179 | |||
1180 | ret = wsm_cmd_send(priv, buf, NULL, __le16_to_cpu(*cmd), WSM_CMD_TIMEOUT); | ||
1181 | |||
1182 | wsm_cmd_unlock(priv); | ||
1183 | return ret; | ||
1184 | |||
1185 | nomem: | ||
1186 | wsm_cmd_unlock(priv); | ||
1187 | return -ENOMEM; | ||
1188 | } | ||
1189 | #endif /* CONFIG_CW1200_ETF */ | ||
1190 | |||
1191 | /* ******************************************************************** */ | ||
1192 | /* WSM TX port control */ | ||
1193 | |||
1194 | void wsm_lock_tx(struct cw1200_common *priv) | ||
1195 | { | ||
1196 | wsm_cmd_lock(priv); | ||
1197 | if (atomic_add_return(1, &priv->tx_lock) == 1) { | ||
1198 | if (wsm_flush_tx(priv)) | ||
1199 | pr_debug("[WSM] TX is locked.\n"); | ||
1200 | } | ||
1201 | wsm_cmd_unlock(priv); | ||
1202 | } | ||
1203 | |||
1204 | void wsm_lock_tx_async(struct cw1200_common *priv) | ||
1205 | { | ||
1206 | if (atomic_add_return(1, &priv->tx_lock) == 1) | ||
1207 | pr_debug("[WSM] TX is locked (async).\n"); | ||
1208 | } | ||
1209 | |||
1210 | bool wsm_flush_tx(struct cw1200_common *priv) | ||
1211 | { | ||
1212 | unsigned long timestamp = jiffies; | ||
1213 | bool pending = false; | ||
1214 | long timeout; | ||
1215 | int i; | ||
1216 | |||
1217 | /* Flush must be called with TX lock held. */ | ||
1218 | BUG_ON(!atomic_read(&priv->tx_lock)); | ||
1219 | |||
1220 | /* First check if we really need to do something. | ||
1221 | * It is safe to use unprotected access, as hw_bufs_used | ||
1222 | * can only decrements. | ||
1223 | */ | ||
1224 | if (!priv->hw_bufs_used) | ||
1225 | return true; | ||
1226 | |||
1227 | if (priv->bh_error) { | ||
1228 | /* In case of failure do not wait for magic. */ | ||
1229 | pr_err("[WSM] Fatal error occured, will not flush TX.\n"); | ||
1230 | return false; | ||
1231 | } else { | ||
1232 | /* Get a timestamp of "oldest" frame */ | ||
1233 | for (i = 0; i < 4; ++i) | ||
1234 | pending |= cw1200_queue_get_xmit_timestamp( | ||
1235 | &priv->tx_queue[i], | ||
1236 | ×tamp, 0xffffffff); | ||
1237 | /* If there's nothing pending, we're good */ | ||
1238 | if (!pending) | ||
1239 | return true; | ||
1240 | |||
1241 | timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies; | ||
1242 | if (timeout < 0 || wait_event_timeout(priv->bh_evt_wq, | ||
1243 | !priv->hw_bufs_used, | ||
1244 | timeout) <= 0) { | ||
1245 | /* Hmmm... Not good. Frame had stuck in firmware. */ | ||
1246 | priv->bh_error = 1; | ||
1247 | wiphy_err(priv->hw->wiphy, "[WSM] TX Frames (%d) stuck in firmware, killing BH\n", priv->hw_bufs_used); | ||
1248 | wake_up(&priv->bh_wq); | ||
1249 | return false; | ||
1250 | } | ||
1251 | |||
1252 | /* Ok, everything is flushed. */ | ||
1253 | return true; | ||
1254 | } | ||
1255 | } | ||
1256 | |||
1257 | void wsm_unlock_tx(struct cw1200_common *priv) | ||
1258 | { | ||
1259 | int tx_lock; | ||
1260 | tx_lock = atomic_sub_return(1, &priv->tx_lock); | ||
1261 | BUG_ON(tx_lock < 0); | ||
1262 | |||
1263 | if (tx_lock == 0) { | ||
1264 | if (!priv->bh_error) | ||
1265 | cw1200_bh_wakeup(priv); | ||
1266 | pr_debug("[WSM] TX is unlocked.\n"); | ||
1267 | } | ||
1268 | } | ||
1269 | |||
1270 | /* ******************************************************************** */ | ||
1271 | /* WSM RX */ | ||
1272 | |||
1273 | int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len) | ||
1274 | { | ||
1275 | struct wsm_buf buf; | ||
1276 | u32 reason; | ||
1277 | u32 reg[18]; | ||
1278 | char fname[48]; | ||
1279 | unsigned int i; | ||
1280 | |||
1281 | static const char * const reason_str[] = { | ||
1282 | "undefined instruction", | ||
1283 | "prefetch abort", | ||
1284 | "data abort", | ||
1285 | "unknown error", | ||
1286 | }; | ||
1287 | |||
1288 | buf.begin = buf.data = data; | ||
1289 | buf.end = &buf.begin[len]; | ||
1290 | |||
1291 | reason = WSM_GET32(&buf); | ||
1292 | for (i = 0; i < ARRAY_SIZE(reg); ++i) | ||
1293 | reg[i] = WSM_GET32(&buf); | ||
1294 | WSM_GET(&buf, fname, sizeof(fname)); | ||
1295 | |||
1296 | if (reason < 4) | ||
1297 | wiphy_err(priv->hw->wiphy, | ||
1298 | "Firmware exception: %s.\n", | ||
1299 | reason_str[reason]); | ||
1300 | else | ||
1301 | wiphy_err(priv->hw->wiphy, | ||
1302 | "Firmware assert at %.*s, line %d\n", | ||
1303 | (int) sizeof(fname), fname, reg[1]); | ||
1304 | |||
1305 | for (i = 0; i < 12; i += 4) | ||
1306 | wiphy_err(priv->hw->wiphy, | ||
1307 | "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n", | ||
1308 | i + 0, reg[i + 0], i + 1, reg[i + 1], | ||
1309 | i + 2, reg[i + 2], i + 3, reg[i + 3]); | ||
1310 | wiphy_err(priv->hw->wiphy, | ||
1311 | "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n", | ||
1312 | reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]); | ||
1313 | i += 4; | ||
1314 | wiphy_err(priv->hw->wiphy, | ||
1315 | "CPSR: 0x%.8X, SPSR: 0x%.8X\n", | ||
1316 | reg[i + 0], reg[i + 1]); | ||
1317 | |||
1318 | print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE, | ||
1319 | fname, sizeof(fname)); | ||
1320 | return 0; | ||
1321 | |||
1322 | underflow: | ||
1323 | wiphy_err(priv->hw->wiphy, "Firmware exception.\n"); | ||
1324 | print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE, | ||
1325 | data, len); | ||
1326 | return -EINVAL; | ||
1327 | } | ||
1328 | |||
1329 | int wsm_handle_rx(struct cw1200_common *priv, u16 id, | ||
1330 | struct wsm_hdr *wsm, struct sk_buff **skb_p) | ||
1331 | { | ||
1332 | int ret = 0; | ||
1333 | struct wsm_buf wsm_buf; | ||
1334 | int link_id = (id >> 6) & 0x0F; | ||
1335 | |||
1336 | /* Strip link id. */ | ||
1337 | id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); | ||
1338 | |||
1339 | wsm_buf.begin = (u8 *)&wsm[0]; | ||
1340 | wsm_buf.data = (u8 *)&wsm[1]; | ||
1341 | wsm_buf.end = &wsm_buf.begin[__le32_to_cpu(wsm->len)]; | ||
1342 | |||
1343 | pr_debug("[WSM] <<< 0x%.4X (%td)\n", id, | ||
1344 | wsm_buf.end - wsm_buf.begin); | ||
1345 | |||
1346 | #ifdef CONFIG_CW1200_ETF | ||
1347 | if (etf_mode) { | ||
1348 | struct sk_buff *skb = alloc_skb(wsm_buf.end - wsm_buf.begin, GFP_KERNEL); | ||
1349 | |||
1350 | /* Strip out Sequence num before passing up */ | ||
1351 | wsm->id = __le16_to_cpu(wsm->id); | ||
1352 | wsm->id &= 0x0FFF; | ||
1353 | wsm->id = __cpu_to_le16(wsm->id); | ||
1354 | |||
1355 | memcpy(skb_put(skb, wsm_buf.end - wsm_buf.begin), | ||
1356 | wsm_buf.begin, | ||
1357 | wsm_buf.end - wsm_buf.begin); | ||
1358 | skb_queue_tail(&priv->etf_q, skb); | ||
1359 | |||
1360 | /* Special case for startup */ | ||
1361 | if (id == WSM_STARTUP_IND_ID) { | ||
1362 | wsm_startup_indication(priv, &wsm_buf); | ||
1363 | } else if (id & 0x0400) { | ||
1364 | spin_lock(&priv->wsm_cmd.lock); | ||
1365 | priv->wsm_cmd.done = 1; | ||
1366 | spin_unlock(&priv->wsm_cmd.lock); | ||
1367 | wake_up(&priv->wsm_cmd_wq); | ||
1368 | } | ||
1369 | |||
1370 | goto out; | ||
1371 | } | ||
1372 | #endif | ||
1373 | |||
1374 | if (id == WSM_TX_CONFIRM_IND_ID) { | ||
1375 | ret = wsm_tx_confirm(priv, &wsm_buf, link_id); | ||
1376 | } else if (id == WSM_MULTI_TX_CONFIRM_ID) { | ||
1377 | ret = wsm_multi_tx_confirm(priv, &wsm_buf, link_id); | ||
1378 | } else if (id & 0x0400) { | ||
1379 | void *wsm_arg; | ||
1380 | u16 wsm_cmd; | ||
1381 | |||
1382 | /* Do not trust FW too much. Protection against repeated | ||
1383 | * response and race condition removal (see above). | ||
1384 | */ | ||
1385 | spin_lock(&priv->wsm_cmd.lock); | ||
1386 | wsm_arg = priv->wsm_cmd.arg; | ||
1387 | wsm_cmd = priv->wsm_cmd.cmd & | ||
1388 | ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); | ||
1389 | priv->wsm_cmd.cmd = 0xFFFF; | ||
1390 | spin_unlock(&priv->wsm_cmd.lock); | ||
1391 | |||
1392 | if (WARN_ON((id & ~0x0400) != wsm_cmd)) { | ||
1393 | /* Note that any non-zero is a fatal retcode. */ | ||
1394 | ret = -EINVAL; | ||
1395 | goto out; | ||
1396 | } | ||
1397 | |||
1398 | /* Note that wsm_arg can be NULL in case of timeout in | ||
1399 | * wsm_cmd_send(). | ||
1400 | */ | ||
1401 | |||
1402 | switch (id) { | ||
1403 | case WSM_READ_MIB_RESP_ID: | ||
1404 | if (wsm_arg) | ||
1405 | ret = wsm_read_mib_confirm(priv, wsm_arg, | ||
1406 | &wsm_buf); | ||
1407 | break; | ||
1408 | case WSM_WRITE_MIB_RESP_ID: | ||
1409 | if (wsm_arg) | ||
1410 | ret = wsm_write_mib_confirm(priv, wsm_arg, | ||
1411 | &wsm_buf); | ||
1412 | break; | ||
1413 | case WSM_START_SCAN_RESP_ID: | ||
1414 | if (wsm_arg) | ||
1415 | ret = wsm_scan_started(priv, wsm_arg, &wsm_buf); | ||
1416 | break; | ||
1417 | case WSM_CONFIGURATION_RESP_ID: | ||
1418 | if (wsm_arg) | ||
1419 | ret = wsm_configuration_confirm(priv, wsm_arg, | ||
1420 | &wsm_buf); | ||
1421 | break; | ||
1422 | case WSM_JOIN_RESP_ID: | ||
1423 | if (wsm_arg) | ||
1424 | ret = wsm_join_confirm(priv, wsm_arg, &wsm_buf); | ||
1425 | break; | ||
1426 | case WSM_STOP_SCAN_RESP_ID: | ||
1427 | case WSM_RESET_RESP_ID: | ||
1428 | case WSM_ADD_KEY_RESP_ID: | ||
1429 | case WSM_REMOVE_KEY_RESP_ID: | ||
1430 | case WSM_SET_PM_RESP_ID: | ||
1431 | case WSM_SET_BSS_PARAMS_RESP_ID: | ||
1432 | case 0x0412: /* set_tx_queue_params */ | ||
1433 | case WSM_EDCA_PARAMS_RESP_ID: | ||
1434 | case WSM_SWITCH_CHANNEL_RESP_ID: | ||
1435 | case WSM_START_RESP_ID: | ||
1436 | case WSM_BEACON_TRANSMIT_RESP_ID: | ||
1437 | case 0x0419: /* start_find */ | ||
1438 | case 0x041A: /* stop_find */ | ||
1439 | case 0x041B: /* update_ie */ | ||
1440 | case 0x041C: /* map_link */ | ||
1441 | WARN_ON(wsm_arg != NULL); | ||
1442 | ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf); | ||
1443 | if (ret) { | ||
1444 | wiphy_warn(priv->hw->wiphy, | ||
1445 | "wsm_generic_confirm failed for request 0x%04x.\n", | ||
1446 | id & ~0x0400); | ||
1447 | |||
1448 | /* often 0x407 and 0x410 occur, this means we're dead.. */ | ||
1449 | if (priv->join_status >= CW1200_JOIN_STATUS_JOINING) { | ||
1450 | wsm_lock_tx(priv); | ||
1451 | if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) | ||
1452 | wsm_unlock_tx(priv); | ||
1453 | } | ||
1454 | } | ||
1455 | break; | ||
1456 | default: | ||
1457 | wiphy_warn(priv->hw->wiphy, | ||
1458 | "Unrecognized confirmation 0x%04x\n", | ||
1459 | id & ~0x0400); | ||
1460 | } | ||
1461 | |||
1462 | spin_lock(&priv->wsm_cmd.lock); | ||
1463 | priv->wsm_cmd.ret = ret; | ||
1464 | priv->wsm_cmd.done = 1; | ||
1465 | spin_unlock(&priv->wsm_cmd.lock); | ||
1466 | |||
1467 | ret = 0; /* Error response from device should ne stop BH. */ | ||
1468 | |||
1469 | wake_up(&priv->wsm_cmd_wq); | ||
1470 | } else if (id & 0x0800) { | ||
1471 | switch (id) { | ||
1472 | case WSM_STARTUP_IND_ID: | ||
1473 | ret = wsm_startup_indication(priv, &wsm_buf); | ||
1474 | break; | ||
1475 | case WSM_RECEIVE_IND_ID: | ||
1476 | ret = wsm_receive_indication(priv, link_id, | ||
1477 | &wsm_buf, skb_p); | ||
1478 | break; | ||
1479 | case 0x0805: | ||
1480 | ret = wsm_event_indication(priv, &wsm_buf); | ||
1481 | break; | ||
1482 | case WSM_SCAN_COMPLETE_IND_ID: | ||
1483 | ret = wsm_scan_complete_indication(priv, &wsm_buf); | ||
1484 | break; | ||
1485 | case 0x0808: | ||
1486 | ret = wsm_ba_timeout_indication(priv, &wsm_buf); | ||
1487 | break; | ||
1488 | case 0x0809: | ||
1489 | ret = wsm_set_pm_indication(priv, &wsm_buf); | ||
1490 | break; | ||
1491 | case 0x080A: | ||
1492 | ret = wsm_channel_switch_indication(priv, &wsm_buf); | ||
1493 | break; | ||
1494 | case 0x080B: | ||
1495 | ret = wsm_find_complete_indication(priv, &wsm_buf); | ||
1496 | break; | ||
1497 | case 0x080C: | ||
1498 | ret = wsm_suspend_resume_indication(priv, | ||
1499 | link_id, &wsm_buf); | ||
1500 | break; | ||
1501 | case 0x080F: | ||
1502 | ret = wsm_join_complete_indication(priv, &wsm_buf); | ||
1503 | break; | ||
1504 | default: | ||
1505 | pr_warn("Unrecognised WSM ID %04x\n", id); | ||
1506 | } | ||
1507 | } else { | ||
1508 | WARN_ON(1); | ||
1509 | ret = -EINVAL; | ||
1510 | } | ||
1511 | out: | ||
1512 | return ret; | ||
1513 | } | ||
1514 | |||
1515 | static bool wsm_handle_tx_data(struct cw1200_common *priv, | ||
1516 | struct wsm_tx *wsm, | ||
1517 | const struct ieee80211_tx_info *tx_info, | ||
1518 | const struct cw1200_txpriv *txpriv, | ||
1519 | struct cw1200_queue *queue) | ||
1520 | { | ||
1521 | bool handled = false; | ||
1522 | const struct ieee80211_hdr *frame = | ||
1523 | (struct ieee80211_hdr *)&((u8 *)wsm)[txpriv->offset]; | ||
1524 | __le16 fctl = frame->frame_control; | ||
1525 | enum { | ||
1526 | do_probe, | ||
1527 | do_drop, | ||
1528 | do_wep, | ||
1529 | do_tx, | ||
1530 | } action = do_tx; | ||
1531 | |||
1532 | switch (priv->mode) { | ||
1533 | case NL80211_IFTYPE_STATION: | ||
1534 | if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) | ||
1535 | action = do_tx; | ||
1536 | else if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) | ||
1537 | action = do_drop; | ||
1538 | break; | ||
1539 | case NL80211_IFTYPE_AP: | ||
1540 | if (!priv->join_status) { | ||
1541 | action = do_drop; | ||
1542 | } else if (!(BIT(txpriv->raw_link_id) & | ||
1543 | (BIT(0) | priv->link_id_map))) { | ||
1544 | wiphy_warn(priv->hw->wiphy, | ||
1545 | "A frame with expired link id is dropped.\n"); | ||
1546 | action = do_drop; | ||
1547 | } | ||
1548 | if (cw1200_queue_get_generation(wsm->packet_id) > | ||
1549 | CW1200_MAX_REQUEUE_ATTEMPTS) { | ||
1550 | /* HACK!!! WSM324 firmware has tendency to requeue | ||
1551 | * multicast frames in a loop, causing performance | ||
1552 | * drop and high power consumption of the driver. | ||
1553 | * In this situation it is better just to drop | ||
1554 | * the problematic frame. | ||
1555 | */ | ||
1556 | wiphy_warn(priv->hw->wiphy, | ||
1557 | "Too many attempts to requeue a frame; dropped.\n"); | ||
1558 | action = do_drop; | ||
1559 | } | ||
1560 | break; | ||
1561 | case NL80211_IFTYPE_ADHOC: | ||
1562 | if (priv->join_status != CW1200_JOIN_STATUS_IBSS) | ||
1563 | action = do_drop; | ||
1564 | break; | ||
1565 | case NL80211_IFTYPE_MESH_POINT: | ||
1566 | action = do_tx; /* TODO: Test me! */ | ||
1567 | break; | ||
1568 | case NL80211_IFTYPE_MONITOR: | ||
1569 | default: | ||
1570 | action = do_drop; | ||
1571 | break; | ||
1572 | } | ||
1573 | |||
1574 | if (action == do_tx) { | ||
1575 | if (ieee80211_is_nullfunc(fctl)) { | ||
1576 | spin_lock(&priv->bss_loss_lock); | ||
1577 | if (priv->bss_loss_state) { | ||
1578 | priv->bss_loss_confirm_id = wsm->packet_id; | ||
1579 | wsm->queue_id = WSM_QUEUE_VOICE; | ||
1580 | } | ||
1581 | spin_unlock(&priv->bss_loss_lock); | ||
1582 | } else if (ieee80211_is_probe_req(fctl)) { | ||
1583 | action = do_probe; | ||
1584 | } else if (ieee80211_is_deauth(fctl) && | ||
1585 | priv->mode != NL80211_IFTYPE_AP) { | ||
1586 | pr_debug("[WSM] Issue unjoin command due to tx deauth.\n"); | ||
1587 | wsm_lock_tx_async(priv); | ||
1588 | if (queue_work(priv->workqueue, | ||
1589 | &priv->unjoin_work) <= 0) | ||
1590 | wsm_unlock_tx(priv); | ||
1591 | } else if (ieee80211_has_protected(fctl) && | ||
1592 | tx_info->control.hw_key && | ||
1593 | tx_info->control.hw_key->keyidx != priv->wep_default_key_id && | ||
1594 | (tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP40 || | ||
1595 | tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP104)) { | ||
1596 | action = do_wep; | ||
1597 | } | ||
1598 | } | ||
1599 | |||
1600 | switch (action) { | ||
1601 | case do_probe: | ||
1602 | /* An interesting FW "feature". Device filters probe responses. | ||
1603 | * The easiest way to get it back is to convert | ||
1604 | * probe request into WSM start_scan command. | ||
1605 | */ | ||
1606 | pr_debug("[WSM] Convert probe request to scan.\n"); | ||
1607 | wsm_lock_tx_async(priv); | ||
1608 | priv->pending_frame_id = __le32_to_cpu(wsm->packet_id); | ||
1609 | if (queue_delayed_work(priv->workqueue, | ||
1610 | &priv->scan.probe_work, 0) <= 0) | ||
1611 | wsm_unlock_tx(priv); | ||
1612 | handled = true; | ||
1613 | break; | ||
1614 | case do_drop: | ||
1615 | pr_debug("[WSM] Drop frame (0x%.4X).\n", fctl); | ||
1616 | BUG_ON(cw1200_queue_remove(queue, | ||
1617 | __le32_to_cpu(wsm->packet_id))); | ||
1618 | handled = true; | ||
1619 | break; | ||
1620 | case do_wep: | ||
1621 | pr_debug("[WSM] Issue set_default_wep_key.\n"); | ||
1622 | wsm_lock_tx_async(priv); | ||
1623 | priv->wep_default_key_id = tx_info->control.hw_key->keyidx; | ||
1624 | priv->pending_frame_id = __le32_to_cpu(wsm->packet_id); | ||
1625 | if (queue_work(priv->workqueue, &priv->wep_key_work) <= 0) | ||
1626 | wsm_unlock_tx(priv); | ||
1627 | handled = true; | ||
1628 | break; | ||
1629 | case do_tx: | ||
1630 | pr_debug("[WSM] Transmit frame.\n"); | ||
1631 | break; | ||
1632 | default: | ||
1633 | /* Do nothing */ | ||
1634 | break; | ||
1635 | } | ||
1636 | return handled; | ||
1637 | } | ||
1638 | |||
1639 | static int cw1200_get_prio_queue(struct cw1200_common *priv, | ||
1640 | u32 link_id_map, int *total) | ||
1641 | { | ||
1642 | static const int urgent = BIT(CW1200_LINK_ID_AFTER_DTIM) | | ||
1643 | BIT(CW1200_LINK_ID_UAPSD); | ||
1644 | struct wsm_edca_queue_params *edca; | ||
1645 | unsigned score, best = -1; | ||
1646 | int winner = -1; | ||
1647 | int queued; | ||
1648 | int i; | ||
1649 | |||
1650 | /* search for a winner using edca params */ | ||
1651 | for (i = 0; i < 4; ++i) { | ||
1652 | queued = cw1200_queue_get_num_queued(&priv->tx_queue[i], | ||
1653 | link_id_map); | ||
1654 | if (!queued) | ||
1655 | continue; | ||
1656 | *total += queued; | ||
1657 | edca = &priv->edca.params[i]; | ||
1658 | score = ((edca->aifns + edca->cwmin) << 16) + | ||
1659 | ((edca->cwmax - edca->cwmin) * | ||
1660 | (get_random_int() & 0xFFFF)); | ||
1661 | if (score < best && (winner < 0 || i != 3)) { | ||
1662 | best = score; | ||
1663 | winner = i; | ||
1664 | } | ||
1665 | } | ||
1666 | |||
1667 | /* override winner if bursting */ | ||
1668 | if (winner >= 0 && priv->tx_burst_idx >= 0 && | ||
1669 | winner != priv->tx_burst_idx && | ||
1670 | !cw1200_queue_get_num_queued( | ||
1671 | &priv->tx_queue[winner], | ||
1672 | link_id_map & urgent) && | ||
1673 | cw1200_queue_get_num_queued( | ||
1674 | &priv->tx_queue[priv->tx_burst_idx], | ||
1675 | link_id_map)) | ||
1676 | winner = priv->tx_burst_idx; | ||
1677 | |||
1678 | return winner; | ||
1679 | } | ||
1680 | |||
1681 | static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, | ||
1682 | struct cw1200_queue **queue_p, | ||
1683 | u32 *tx_allowed_mask_p, | ||
1684 | bool *more) | ||
1685 | { | ||
1686 | int idx; | ||
1687 | u32 tx_allowed_mask; | ||
1688 | int total = 0; | ||
1689 | |||
1690 | /* Search for a queue with multicast frames buffered */ | ||
1691 | if (priv->tx_multicast) { | ||
1692 | tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM); | ||
1693 | idx = cw1200_get_prio_queue(priv, | ||
1694 | tx_allowed_mask, &total); | ||
1695 | if (idx >= 0) { | ||
1696 | *more = total > 1; | ||
1697 | goto found; | ||
1698 | } | ||
1699 | } | ||
1700 | |||
1701 | /* Search for unicast traffic */ | ||
1702 | tx_allowed_mask = ~priv->sta_asleep_mask; | ||
1703 | tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD); | ||
1704 | if (priv->sta_asleep_mask) { | ||
1705 | tx_allowed_mask |= priv->pspoll_mask; | ||
1706 | tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM); | ||
1707 | } else { | ||
1708 | tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM); | ||
1709 | } | ||
1710 | idx = cw1200_get_prio_queue(priv, | ||
1711 | tx_allowed_mask, &total); | ||
1712 | if (idx < 0) | ||
1713 | return -ENOENT; | ||
1714 | |||
1715 | found: | ||
1716 | *queue_p = &priv->tx_queue[idx]; | ||
1717 | *tx_allowed_mask_p = tx_allowed_mask; | ||
1718 | return 0; | ||
1719 | } | ||
1720 | |||
1721 | int wsm_get_tx(struct cw1200_common *priv, u8 **data, | ||
1722 | size_t *tx_len, int *burst) | ||
1723 | { | ||
1724 | struct wsm_tx *wsm = NULL; | ||
1725 | struct ieee80211_tx_info *tx_info; | ||
1726 | struct cw1200_queue *queue = NULL; | ||
1727 | int queue_num; | ||
1728 | u32 tx_allowed_mask = 0; | ||
1729 | const struct cw1200_txpriv *txpriv = NULL; | ||
1730 | int count = 0; | ||
1731 | |||
1732 | /* More is used only for broadcasts. */ | ||
1733 | bool more = false; | ||
1734 | |||
1735 | #ifdef CONFIG_CW1200_ITP | ||
1736 | count = cw1200_itp_get_tx(priv, data, tx_len, burst); | ||
1737 | if (count) | ||
1738 | return count; | ||
1739 | #endif | ||
1740 | |||
1741 | if (priv->wsm_cmd.ptr) { /* CMD request */ | ||
1742 | ++count; | ||
1743 | spin_lock(&priv->wsm_cmd.lock); | ||
1744 | BUG_ON(!priv->wsm_cmd.ptr); | ||
1745 | *data = priv->wsm_cmd.ptr; | ||
1746 | *tx_len = priv->wsm_cmd.len; | ||
1747 | *burst = 1; | ||
1748 | spin_unlock(&priv->wsm_cmd.lock); | ||
1749 | } else { | ||
1750 | for (;;) { | ||
1751 | int ret; | ||
1752 | |||
1753 | if (atomic_add_return(0, &priv->tx_lock)) | ||
1754 | break; | ||
1755 | |||
1756 | spin_lock_bh(&priv->ps_state_lock); | ||
1757 | |||
1758 | ret = wsm_get_tx_queue_and_mask(priv, &queue, | ||
1759 | &tx_allowed_mask, &more); | ||
1760 | queue_num = queue - priv->tx_queue; | ||
1761 | |||
1762 | if (priv->buffered_multicasts && | ||
1763 | (ret || !more) && | ||
1764 | (priv->tx_multicast || !priv->sta_asleep_mask)) { | ||
1765 | priv->buffered_multicasts = false; | ||
1766 | if (priv->tx_multicast) { | ||
1767 | priv->tx_multicast = false; | ||
1768 | queue_work(priv->workqueue, | ||
1769 | &priv->multicast_stop_work); | ||
1770 | } | ||
1771 | } | ||
1772 | |||
1773 | spin_unlock_bh(&priv->ps_state_lock); | ||
1774 | |||
1775 | if (ret) | ||
1776 | break; | ||
1777 | |||
1778 | if (cw1200_queue_get(queue, | ||
1779 | tx_allowed_mask, | ||
1780 | &wsm, &tx_info, &txpriv)) | ||
1781 | continue; | ||
1782 | |||
1783 | if (wsm_handle_tx_data(priv, wsm, | ||
1784 | tx_info, txpriv, queue)) | ||
1785 | continue; /* Handled by WSM */ | ||
1786 | |||
1787 | wsm->hdr.id &= __cpu_to_le16( | ||
1788 | ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX)); | ||
1789 | wsm->hdr.id |= cpu_to_le16( | ||
1790 | WSM_TX_LINK_ID(txpriv->raw_link_id)); | ||
1791 | priv->pspoll_mask &= ~BIT(txpriv->raw_link_id); | ||
1792 | |||
1793 | *data = (u8 *)wsm; | ||
1794 | *tx_len = __le16_to_cpu(wsm->hdr.len); | ||
1795 | |||
1796 | /* allow bursting if txop is set */ | ||
1797 | if (priv->edca.params[queue_num].txop_limit) | ||
1798 | *burst = min(*burst, | ||
1799 | (int)cw1200_queue_get_num_queued(queue, tx_allowed_mask) + 1); | ||
1800 | else | ||
1801 | *burst = 1; | ||
1802 | |||
1803 | /* store index of bursting queue */ | ||
1804 | if (*burst > 1) | ||
1805 | priv->tx_burst_idx = queue_num; | ||
1806 | else | ||
1807 | priv->tx_burst_idx = -1; | ||
1808 | |||
1809 | if (more) { | ||
1810 | struct ieee80211_hdr *hdr = | ||
1811 | (struct ieee80211_hdr *) | ||
1812 | &((u8 *)wsm)[txpriv->offset]; | ||
1813 | /* more buffered multicast/broadcast frames | ||
1814 | * ==> set MoreData flag in IEEE 802.11 header | ||
1815 | * to inform PS STAs | ||
1816 | */ | ||
1817 | hdr->frame_control |= | ||
1818 | cpu_to_le16(IEEE80211_FCTL_MOREDATA); | ||
1819 | } | ||
1820 | |||
1821 | pr_debug("[WSM] >>> 0x%.4X (%zu) %p %c\n", | ||
1822 | 0x0004, *tx_len, *data, | ||
1823 | wsm->more ? 'M' : ' '); | ||
1824 | ++count; | ||
1825 | break; | ||
1826 | } | ||
1827 | } | ||
1828 | |||
1829 | return count; | ||
1830 | } | ||
1831 | |||
1832 | void wsm_txed(struct cw1200_common *priv, u8 *data) | ||
1833 | { | ||
1834 | if (data == priv->wsm_cmd.ptr) { | ||
1835 | spin_lock(&priv->wsm_cmd.lock); | ||
1836 | priv->wsm_cmd.ptr = NULL; | ||
1837 | spin_unlock(&priv->wsm_cmd.lock); | ||
1838 | } | ||
1839 | } | ||
1840 | |||
1841 | /* ******************************************************************** */ | ||
1842 | /* WSM buffer */ | ||
1843 | |||
1844 | void wsm_buf_init(struct wsm_buf *buf) | ||
1845 | { | ||
1846 | BUG_ON(buf->begin); | ||
1847 | buf->begin = kmalloc(FWLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); | ||
1848 | buf->end = buf->begin ? &buf->begin[FWLOAD_BLOCK_SIZE] : buf->begin; | ||
1849 | wsm_buf_reset(buf); | ||
1850 | } | ||
1851 | |||
1852 | void wsm_buf_deinit(struct wsm_buf *buf) | ||
1853 | { | ||
1854 | kfree(buf->begin); | ||
1855 | buf->begin = buf->data = buf->end = NULL; | ||
1856 | } | ||
1857 | |||
1858 | static void wsm_buf_reset(struct wsm_buf *buf) | ||
1859 | { | ||
1860 | if (buf->begin) { | ||
1861 | buf->data = &buf->begin[4]; | ||
1862 | *(u32 *)buf->begin = 0; | ||
1863 | } else { | ||
1864 | buf->data = buf->begin; | ||
1865 | } | ||
1866 | } | ||
1867 | |||
1868 | static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size) | ||
1869 | { | ||
1870 | size_t pos = buf->data - buf->begin; | ||
1871 | size_t size = pos + extra_size; | ||
1872 | |||
1873 | size = round_up(size, FWLOAD_BLOCK_SIZE); | ||
1874 | |||
1875 | buf->begin = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA); | ||
1876 | if (buf->begin) { | ||
1877 | buf->data = &buf->begin[pos]; | ||
1878 | buf->end = &buf->begin[size]; | ||
1879 | return 0; | ||
1880 | } else { | ||
1881 | buf->end = buf->data = buf->begin; | ||
1882 | return -ENOMEM; | ||
1883 | } | ||
1884 | } | ||
diff --git a/drivers/net/wireless/cw1200/wsm.h b/drivers/net/wireless/cw1200/wsm.h new file mode 100644 index 000000000000..8d902d6a7dc4 --- /dev/null +++ b/drivers/net/wireless/cw1200/wsm.h | |||
@@ -0,0 +1,1879 @@ | |||
1 | /* | ||
2 | * WSM host interface (HI) interface for ST-Ericsson CW1200 mac80211 drivers | ||
3 | * | ||
4 | * Copyright (c) 2010, ST-Ericsson | ||
5 | * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> | ||
6 | * | ||
7 | * Based on CW1200 UMAC WSM API, which is | ||
8 | * Copyright (C) ST-Ericsson SA 2010 | ||
9 | * Author: Stewart Mathers <stewart.mathers@stericsson.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | */ | ||
15 | |||
16 | #ifndef CW1200_WSM_H_INCLUDED | ||
17 | #define CW1200_WSM_H_INCLUDED | ||
18 | |||
19 | #include <linux/spinlock.h> | ||
20 | |||
21 | struct cw1200_common; | ||
22 | |||
23 | /* Bands */ | ||
24 | /* Radio band 2.412 -2.484 GHz. */ | ||
25 | #define WSM_PHY_BAND_2_4G (0) | ||
26 | |||
27 | /* Radio band 4.9375-5.8250 GHz. */ | ||
28 | #define WSM_PHY_BAND_5G (1) | ||
29 | |||
30 | /* Transmit rates */ | ||
31 | /* 1 Mbps ERP-DSSS */ | ||
32 | #define WSM_TRANSMIT_RATE_1 (0) | ||
33 | |||
34 | /* 2 Mbps ERP-DSSS */ | ||
35 | #define WSM_TRANSMIT_RATE_2 (1) | ||
36 | |||
37 | /* 5.5 Mbps ERP-CCK */ | ||
38 | #define WSM_TRANSMIT_RATE_5 (2) | ||
39 | |||
40 | /* 11 Mbps ERP-CCK */ | ||
41 | #define WSM_TRANSMIT_RATE_11 (3) | ||
42 | |||
43 | /* 22 Mbps ERP-PBCC (Not supported) */ | ||
44 | /* #define WSM_TRANSMIT_RATE_22 (4) */ | ||
45 | |||
46 | /* 33 Mbps ERP-PBCC (Not supported) */ | ||
47 | /* #define WSM_TRANSMIT_RATE_33 (5) */ | ||
48 | |||
49 | /* 6 Mbps (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */ | ||
50 | #define WSM_TRANSMIT_RATE_6 (6) | ||
51 | |||
52 | /* 9 Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */ | ||
53 | #define WSM_TRANSMIT_RATE_9 (7) | ||
54 | |||
55 | /* 12 Mbps (6 Mbps) ERP-OFDM, QPSK coding rate 1/2 */ | ||
56 | #define WSM_TRANSMIT_RATE_12 (8) | ||
57 | |||
58 | /* 18 Mbps (9 Mbps) ERP-OFDM, QPSK coding rate 3/4 */ | ||
59 | #define WSM_TRANSMIT_RATE_18 (9) | ||
60 | |||
61 | /* 24 Mbps (12 Mbps) ERP-OFDM, 16QAM coding rate 1/2 */ | ||
62 | #define WSM_TRANSMIT_RATE_24 (10) | ||
63 | |||
64 | /* 36 Mbps (18 Mbps) ERP-OFDM, 16QAM coding rate 3/4 */ | ||
65 | #define WSM_TRANSMIT_RATE_36 (11) | ||
66 | |||
67 | /* 48 Mbps (24 Mbps) ERP-OFDM, 64QAM coding rate 1/2 */ | ||
68 | #define WSM_TRANSMIT_RATE_48 (12) | ||
69 | |||
70 | /* 54 Mbps (27 Mbps) ERP-OFDM, 64QAM coding rate 3/4 */ | ||
71 | #define WSM_TRANSMIT_RATE_54 (13) | ||
72 | |||
73 | /* 6.5 Mbps HT-OFDM, BPSK coding rate 1/2 */ | ||
74 | #define WSM_TRANSMIT_RATE_HT_6 (14) | ||
75 | |||
76 | /* 13 Mbps HT-OFDM, QPSK coding rate 1/2 */ | ||
77 | #define WSM_TRANSMIT_RATE_HT_13 (15) | ||
78 | |||
79 | /* 19.5 Mbps HT-OFDM, QPSK coding rate 3/4 */ | ||
80 | #define WSM_TRANSMIT_RATE_HT_19 (16) | ||
81 | |||
82 | /* 26 Mbps HT-OFDM, 16QAM coding rate 1/2 */ | ||
83 | #define WSM_TRANSMIT_RATE_HT_26 (17) | ||
84 | |||
85 | /* 39 Mbps HT-OFDM, 16QAM coding rate 3/4 */ | ||
86 | #define WSM_TRANSMIT_RATE_HT_39 (18) | ||
87 | |||
88 | /* 52 Mbps HT-OFDM, 64QAM coding rate 2/3 */ | ||
89 | #define WSM_TRANSMIT_RATE_HT_52 (19) | ||
90 | |||
91 | /* 58.5 Mbps HT-OFDM, 64QAM coding rate 3/4 */ | ||
92 | #define WSM_TRANSMIT_RATE_HT_58 (20) | ||
93 | |||
94 | /* 65 Mbps HT-OFDM, 64QAM coding rate 5/6 */ | ||
95 | #define WSM_TRANSMIT_RATE_HT_65 (21) | ||
96 | |||
97 | /* Scan types */ | ||
98 | /* Foreground scan */ | ||
99 | #define WSM_SCAN_TYPE_FOREGROUND (0) | ||
100 | |||
101 | /* Background scan */ | ||
102 | #define WSM_SCAN_TYPE_BACKGROUND (1) | ||
103 | |||
104 | /* Auto scan */ | ||
105 | #define WSM_SCAN_TYPE_AUTO (2) | ||
106 | |||
107 | /* Scan flags */ | ||
108 | /* Forced background scan means if the station cannot */ | ||
109 | /* enter the power-save mode, it shall force to perform a */ | ||
110 | /* background scan. Only valid when ScanType is */ | ||
111 | /* background scan. */ | ||
112 | #define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0)) | ||
113 | |||
114 | /* The WLAN device scans one channel at a time so */ | ||
115 | /* that disturbance to the data traffic is minimized. */ | ||
116 | #define WSM_SCAN_FLAG_SPLIT_METHOD (BIT(1)) | ||
117 | |||
118 | /* Preamble Type. Long if not set. */ | ||
119 | #define WSM_SCAN_FLAG_SHORT_PREAMBLE (BIT(2)) | ||
120 | |||
121 | /* 11n Tx Mode. Mixed if not set. */ | ||
122 | #define WSM_SCAN_FLAG_11N_GREENFIELD (BIT(3)) | ||
123 | |||
124 | /* Scan constraints */ | ||
125 | /* Maximum number of channels to be scanned. */ | ||
126 | #define WSM_SCAN_MAX_NUM_OF_CHANNELS (48) | ||
127 | |||
128 | /* The maximum number of SSIDs that the device can scan for. */ | ||
129 | #define WSM_SCAN_MAX_NUM_OF_SSIDS (2) | ||
130 | |||
131 | /* Power management modes */ | ||
132 | /* 802.11 Active mode */ | ||
133 | #define WSM_PSM_ACTIVE (0) | ||
134 | |||
135 | /* 802.11 PS mode */ | ||
136 | #define WSM_PSM_PS BIT(0) | ||
137 | |||
138 | /* Fast Power Save bit */ | ||
139 | #define WSM_PSM_FAST_PS_FLAG BIT(7) | ||
140 | |||
141 | /* Dynamic aka Fast power save */ | ||
142 | #define WSM_PSM_FAST_PS (BIT(0) | BIT(7)) | ||
143 | |||
144 | /* Undetermined */ | ||
145 | /* Note : Undetermined status is reported when the */ | ||
146 | /* NULL data frame used to advertise the PM mode to */ | ||
147 | /* the AP at Pre or Post Background Scan is not Acknowledged */ | ||
148 | #define WSM_PSM_UNKNOWN BIT(1) | ||
149 | |||
150 | /* Queue IDs */ | ||
151 | /* best effort/legacy */ | ||
152 | #define WSM_QUEUE_BEST_EFFORT (0) | ||
153 | |||
154 | /* background */ | ||
155 | #define WSM_QUEUE_BACKGROUND (1) | ||
156 | |||
157 | /* video */ | ||
158 | #define WSM_QUEUE_VIDEO (2) | ||
159 | |||
160 | /* voice */ | ||
161 | #define WSM_QUEUE_VOICE (3) | ||
162 | |||
163 | /* HT TX parameters */ | ||
164 | /* Non-HT */ | ||
165 | #define WSM_HT_TX_NON_HT (0) | ||
166 | |||
167 | /* Mixed format */ | ||
168 | #define WSM_HT_TX_MIXED (1) | ||
169 | |||
170 | /* Greenfield format */ | ||
171 | #define WSM_HT_TX_GREENFIELD (2) | ||
172 | |||
173 | /* STBC allowed */ | ||
174 | #define WSM_HT_TX_STBC (BIT(7)) | ||
175 | |||
176 | /* EPTA prioirty flags for BT Coex */ | ||
177 | /* default epta priority */ | ||
178 | #define WSM_EPTA_PRIORITY_DEFAULT 4 | ||
179 | /* use for normal data */ | ||
180 | #define WSM_EPTA_PRIORITY_DATA 4 | ||
181 | /* use for connect/disconnect/roaming*/ | ||
182 | #define WSM_EPTA_PRIORITY_MGT 5 | ||
183 | /* use for action frames */ | ||
184 | #define WSM_EPTA_PRIORITY_ACTION 5 | ||
185 | /* use for AC_VI data */ | ||
186 | #define WSM_EPTA_PRIORITY_VIDEO 5 | ||
187 | /* use for AC_VO data */ | ||
188 | #define WSM_EPTA_PRIORITY_VOICE 6 | ||
189 | /* use for EAPOL exchange */ | ||
190 | #define WSM_EPTA_PRIORITY_EAPOL 7 | ||
191 | |||
192 | /* TX status */ | ||
193 | /* Frame was sent aggregated */ | ||
194 | /* Only valid for WSM_SUCCESS status. */ | ||
195 | #define WSM_TX_STATUS_AGGREGATION (BIT(0)) | ||
196 | |||
197 | /* Host should requeue this frame later. */ | ||
198 | /* Valid only when status is WSM_REQUEUE. */ | ||
199 | #define WSM_TX_STATUS_REQUEUE (BIT(1)) | ||
200 | |||
201 | /* Normal Ack */ | ||
202 | #define WSM_TX_STATUS_NORMAL_ACK (0<<2) | ||
203 | |||
204 | /* No Ack */ | ||
205 | #define WSM_TX_STATUS_NO_ACK (1<<2) | ||
206 | |||
207 | /* No explicit acknowledgement */ | ||
208 | #define WSM_TX_STATUS_NO_EXPLICIT_ACK (2<<2) | ||
209 | |||
210 | /* Block Ack */ | ||
211 | /* Only valid for WSM_SUCCESS status. */ | ||
212 | #define WSM_TX_STATUS_BLOCK_ACK (3<<2) | ||
213 | |||
214 | /* RX status */ | ||
215 | /* Unencrypted */ | ||
216 | #define WSM_RX_STATUS_UNENCRYPTED (0<<0) | ||
217 | |||
218 | /* WEP */ | ||
219 | #define WSM_RX_STATUS_WEP (1<<0) | ||
220 | |||
221 | /* TKIP */ | ||
222 | #define WSM_RX_STATUS_TKIP (2<<0) | ||
223 | |||
224 | /* AES */ | ||
225 | #define WSM_RX_STATUS_AES (3<<0) | ||
226 | |||
227 | /* WAPI */ | ||
228 | #define WSM_RX_STATUS_WAPI (4<<0) | ||
229 | |||
230 | /* Macro to fetch encryption subfield. */ | ||
231 | #define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07) | ||
232 | |||
233 | /* Frame was part of an aggregation */ | ||
234 | #define WSM_RX_STATUS_AGGREGATE (BIT(3)) | ||
235 | |||
236 | /* Frame was first in the aggregation */ | ||
237 | #define WSM_RX_STATUS_AGGREGATE_FIRST (BIT(4)) | ||
238 | |||
239 | /* Frame was last in the aggregation */ | ||
240 | #define WSM_RX_STATUS_AGGREGATE_LAST (BIT(5)) | ||
241 | |||
242 | /* Indicates a defragmented frame */ | ||
243 | #define WSM_RX_STATUS_DEFRAGMENTED (BIT(6)) | ||
244 | |||
245 | /* Indicates a Beacon frame */ | ||
246 | #define WSM_RX_STATUS_BEACON (BIT(7)) | ||
247 | |||
248 | /* Indicates STA bit beacon TIM field */ | ||
249 | #define WSM_RX_STATUS_TIM (BIT(8)) | ||
250 | |||
251 | /* Indicates Beacon frame's virtual bitmap contains multicast bit */ | ||
252 | #define WSM_RX_STATUS_MULTICAST (BIT(9)) | ||
253 | |||
254 | /* Indicates frame contains a matching SSID */ | ||
255 | #define WSM_RX_STATUS_MATCHING_SSID (BIT(10)) | ||
256 | |||
257 | /* Indicates frame contains a matching BSSI */ | ||
258 | #define WSM_RX_STATUS_MATCHING_BSSI (BIT(11)) | ||
259 | |||
260 | /* Indicates More bit set in Framectl field */ | ||
261 | #define WSM_RX_STATUS_MORE_DATA (BIT(12)) | ||
262 | |||
263 | /* Indicates frame received during a measurement process */ | ||
264 | #define WSM_RX_STATUS_MEASUREMENT (BIT(13)) | ||
265 | |||
266 | /* Indicates frame received as an HT packet */ | ||
267 | #define WSM_RX_STATUS_HT (BIT(14)) | ||
268 | |||
269 | /* Indicates frame received with STBC */ | ||
270 | #define WSM_RX_STATUS_STBC (BIT(15)) | ||
271 | |||
272 | /* Indicates Address 1 field matches dot11StationId */ | ||
273 | #define WSM_RX_STATUS_ADDRESS1 (BIT(16)) | ||
274 | |||
275 | /* Indicates Group address present in the Address 1 field */ | ||
276 | #define WSM_RX_STATUS_GROUP (BIT(17)) | ||
277 | |||
278 | /* Indicates Broadcast address present in the Address 1 field */ | ||
279 | #define WSM_RX_STATUS_BROADCAST (BIT(18)) | ||
280 | |||
281 | /* Indicates group key used with encrypted frames */ | ||
282 | #define WSM_RX_STATUS_GROUP_KEY (BIT(19)) | ||
283 | |||
284 | /* Macro to fetch encryption key index. */ | ||
285 | #define WSM_RX_STATUS_KEY_IDX(status) (((status >> 20)) & 0x0F) | ||
286 | |||
287 | /* Indicates TSF inclusion after 802.11 frame body */ | ||
288 | #define WSM_RX_STATUS_TSF_INCLUDED (BIT(24)) | ||
289 | |||
290 | /* Frame Control field starts at Frame offset + 2 */ | ||
291 | #define WSM_TX_2BYTES_SHIFT (BIT(7)) | ||
292 | |||
293 | /* Join mode */ | ||
294 | /* IBSS */ | ||
295 | #define WSM_JOIN_MODE_IBSS (0) | ||
296 | |||
297 | /* BSS */ | ||
298 | #define WSM_JOIN_MODE_BSS (1) | ||
299 | |||
300 | /* PLCP preamble type */ | ||
301 | /* For long preamble */ | ||
302 | #define WSM_JOIN_PREAMBLE_LONG (0) | ||
303 | |||
304 | /* For short preamble (Long for 1Mbps) */ | ||
305 | #define WSM_JOIN_PREAMBLE_SHORT (1) | ||
306 | |||
307 | /* For short preamble (Long for 1 and 2Mbps) */ | ||
308 | #define WSM_JOIN_PREAMBLE_SHORT_2 (2) | ||
309 | |||
310 | /* Join flags */ | ||
311 | /* Unsynchronized */ | ||
312 | #define WSM_JOIN_FLAGS_UNSYNCRONIZED BIT(0) | ||
313 | /* The BSS owner is a P2P GO */ | ||
314 | #define WSM_JOIN_FLAGS_P2P_GO BIT(1) | ||
315 | /* Force to join BSS with the BSSID and the | ||
316 | * SSID specified without waiting for beacons. The | ||
317 | * ProbeForJoin parameter is ignored. */ | ||
318 | #define WSM_JOIN_FLAGS_FORCE BIT(2) | ||
319 | /* Give probe request/response higher | ||
320 | * priority over the BT traffic */ | ||
321 | #define WSM_JOIN_FLAGS_PRIO BIT(3) | ||
322 | /* Issue immediate join confirmation and use | ||
323 | * join complete to notify about completion */ | ||
324 | #define WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND BIT(5) | ||
325 | |||
326 | /* Key types */ | ||
327 | #define WSM_KEY_TYPE_WEP_DEFAULT (0) | ||
328 | #define WSM_KEY_TYPE_WEP_PAIRWISE (1) | ||
329 | #define WSM_KEY_TYPE_TKIP_GROUP (2) | ||
330 | #define WSM_KEY_TYPE_TKIP_PAIRWISE (3) | ||
331 | #define WSM_KEY_TYPE_AES_GROUP (4) | ||
332 | #define WSM_KEY_TYPE_AES_PAIRWISE (5) | ||
333 | #define WSM_KEY_TYPE_WAPI_GROUP (6) | ||
334 | #define WSM_KEY_TYPE_WAPI_PAIRWISE (7) | ||
335 | |||
336 | /* Key indexes */ | ||
337 | #define WSM_KEY_MAX_INDEX (10) | ||
338 | |||
339 | /* ACK policy */ | ||
340 | #define WSM_ACK_POLICY_NORMAL (0) | ||
341 | #define WSM_ACK_POLICY_NO_ACK (1) | ||
342 | |||
343 | /* Start modes */ | ||
344 | #define WSM_START_MODE_AP (0) /* Mini AP */ | ||
345 | #define WSM_START_MODE_P2P_GO (1) /* P2P GO */ | ||
346 | #define WSM_START_MODE_P2P_DEV (2) /* P2P device */ | ||
347 | |||
348 | /* SetAssociationMode MIB flags */ | ||
349 | #define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE (BIT(0)) | ||
350 | #define WSM_ASSOCIATION_MODE_USE_HT_MODE (BIT(1)) | ||
351 | #define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET (BIT(2)) | ||
352 | #define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING (BIT(3)) | ||
353 | #define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES (BIT(4)) | ||
354 | |||
355 | /* RcpiRssiThreshold MIB flags */ | ||
356 | #define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0)) | ||
357 | #define WSM_RCPI_RSSI_USE_RSSI (BIT(1)) | ||
358 | #define WSM_RCPI_RSSI_DONT_USE_UPPER (BIT(2)) | ||
359 | #define WSM_RCPI_RSSI_DONT_USE_LOWER (BIT(3)) | ||
360 | |||
361 | /* Update-ie constants */ | ||
362 | #define WSM_UPDATE_IE_BEACON (BIT(0)) | ||
363 | #define WSM_UPDATE_IE_PROBE_RESP (BIT(1)) | ||
364 | #define WSM_UPDATE_IE_PROBE_REQ (BIT(2)) | ||
365 | |||
366 | /* WSM events */ | ||
367 | /* Error */ | ||
368 | #define WSM_EVENT_ERROR (0) | ||
369 | |||
370 | /* BSS lost */ | ||
371 | #define WSM_EVENT_BSS_LOST (1) | ||
372 | |||
373 | /* BSS regained */ | ||
374 | #define WSM_EVENT_BSS_REGAINED (2) | ||
375 | |||
376 | /* Radar detected */ | ||
377 | #define WSM_EVENT_RADAR_DETECTED (3) | ||
378 | |||
379 | /* RCPI or RSSI threshold triggered */ | ||
380 | #define WSM_EVENT_RCPI_RSSI (4) | ||
381 | |||
382 | /* BT inactive */ | ||
383 | #define WSM_EVENT_BT_INACTIVE (5) | ||
384 | |||
385 | /* BT active */ | ||
386 | #define WSM_EVENT_BT_ACTIVE (6) | ||
387 | |||
388 | /* MIB IDs */ | ||
389 | /* 4.1 dot11StationId */ | ||
390 | #define WSM_MIB_ID_DOT11_STATION_ID 0x0000 | ||
391 | |||
392 | /* 4.2 dot11MaxtransmitMsduLifeTime */ | ||
393 | #define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME 0x0001 | ||
394 | |||
395 | /* 4.3 dot11MaxReceiveLifeTime */ | ||
396 | #define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME 0x0002 | ||
397 | |||
398 | /* 4.4 dot11SlotTime */ | ||
399 | #define WSM_MIB_ID_DOT11_SLOT_TIME 0x0003 | ||
400 | |||
401 | /* 4.5 dot11GroupAddressesTable */ | ||
402 | #define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004 | ||
403 | #define WSM_MAX_GRP_ADDRTABLE_ENTRIES 8 | ||
404 | |||
405 | /* 4.6 dot11WepDefaultKeyId */ | ||
406 | #define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID 0x0005 | ||
407 | |||
408 | /* 4.7 dot11CurrentTxPowerLevel */ | ||
409 | #define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL 0x0006 | ||
410 | |||
411 | /* 4.8 dot11RTSThreshold */ | ||
412 | #define WSM_MIB_ID_DOT11_RTS_THRESHOLD 0x0007 | ||
413 | |||
414 | /* 4.9 NonErpProtection */ | ||
415 | #define WSM_MIB_ID_NON_ERP_PROTECTION 0x1000 | ||
416 | |||
417 | /* 4.10 ArpIpAddressesTable */ | ||
418 | #define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE 0x1001 | ||
419 | #define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES 1 | ||
420 | |||
421 | /* 4.11 TemplateFrame */ | ||
422 | #define WSM_MIB_ID_TEMPLATE_FRAME 0x1002 | ||
423 | |||
424 | /* 4.12 RxFilter */ | ||
425 | #define WSM_MIB_ID_RX_FILTER 0x1003 | ||
426 | |||
427 | /* 4.13 BeaconFilterTable */ | ||
428 | #define WSM_MIB_ID_BEACON_FILTER_TABLE 0x1004 | ||
429 | |||
430 | /* 4.14 BeaconFilterEnable */ | ||
431 | #define WSM_MIB_ID_BEACON_FILTER_ENABLE 0x1005 | ||
432 | |||
433 | /* 4.15 OperationalPowerMode */ | ||
434 | #define WSM_MIB_ID_OPERATIONAL_POWER_MODE 0x1006 | ||
435 | |||
436 | /* 4.16 BeaconWakeUpPeriod */ | ||
437 | #define WSM_MIB_ID_BEACON_WAKEUP_PERIOD 0x1007 | ||
438 | |||
439 | /* 4.17 RcpiRssiThreshold */ | ||
440 | #define WSM_MIB_ID_RCPI_RSSI_THRESHOLD 0x1009 | ||
441 | |||
442 | /* 4.18 StatisticsTable */ | ||
443 | #define WSM_MIB_ID_STATISTICS_TABLE 0x100A | ||
444 | |||
445 | /* 4.19 IbssPsConfig */ | ||
446 | #define WSM_MIB_ID_IBSS_PS_CONFIG 0x100B | ||
447 | |||
448 | /* 4.20 CountersTable */ | ||
449 | #define WSM_MIB_ID_COUNTERS_TABLE 0x100C | ||
450 | |||
451 | /* 4.21 BlockAckPolicy */ | ||
452 | #define WSM_MIB_ID_BLOCK_ACK_POLICY 0x100E | ||
453 | |||
454 | /* 4.22 OverrideInternalTxRate */ | ||
455 | #define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE 0x100F | ||
456 | |||
457 | /* 4.23 SetAssociationMode */ | ||
458 | #define WSM_MIB_ID_SET_ASSOCIATION_MODE 0x1010 | ||
459 | |||
460 | /* 4.24 UpdateEptaConfigData */ | ||
461 | #define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA 0x1011 | ||
462 | |||
463 | /* 4.25 SelectCcaMethod */ | ||
464 | #define WSM_MIB_ID_SELECT_CCA_METHOD 0x1012 | ||
465 | |||
466 | /* 4.26 SetUpasdInformation */ | ||
467 | #define WSM_MIB_ID_SET_UAPSD_INFORMATION 0x1013 | ||
468 | |||
469 | /* 4.27 SetAutoCalibrationMode WBF00004073 */ | ||
470 | #define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE 0x1015 | ||
471 | |||
472 | /* 4.28 SetTxRateRetryPolicy */ | ||
473 | #define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY 0x1016 | ||
474 | |||
475 | /* 4.29 SetHostMessageTypeFilter */ | ||
476 | #define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER 0x1017 | ||
477 | |||
478 | /* 4.30 P2PFindInfo */ | ||
479 | #define WSM_MIB_ID_P2P_FIND_INFO 0x1018 | ||
480 | |||
481 | /* 4.31 P2PPsModeInfo */ | ||
482 | #define WSM_MIB_ID_P2P_PS_MODE_INFO 0x1019 | ||
483 | |||
484 | /* 4.32 SetEtherTypeDataFrameFilter */ | ||
485 | #define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A | ||
486 | |||
487 | /* 4.33 SetUDPPortDataFrameFilter */ | ||
488 | #define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER 0x101B | ||
489 | |||
490 | /* 4.34 SetMagicDataFrameFilter */ | ||
491 | #define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER 0x101C | ||
492 | |||
493 | /* 4.35 P2PDeviceInfo */ | ||
494 | #define WSM_MIB_ID_P2P_DEVICE_INFO 0x101D | ||
495 | |||
496 | /* 4.36 SetWCDMABand */ | ||
497 | #define WSM_MIB_ID_SET_WCDMA_BAND 0x101E | ||
498 | |||
499 | /* 4.37 GroupTxSequenceCounter */ | ||
500 | #define WSM_MIB_ID_GRP_SEQ_COUNTER 0x101F | ||
501 | |||
502 | /* 4.38 ProtectedMgmtPolicy */ | ||
503 | #define WSM_MIB_ID_PROTECTED_MGMT_POLICY 0x1020 | ||
504 | |||
505 | /* 4.39 SetHtProtection */ | ||
506 | #define WSM_MIB_ID_SET_HT_PROTECTION 0x1021 | ||
507 | |||
508 | /* 4.40 GPIO Command */ | ||
509 | #define WSM_MIB_ID_GPIO_COMMAND 0x1022 | ||
510 | |||
511 | /* 4.41 TSF Counter Value */ | ||
512 | #define WSM_MIB_ID_TSF_COUNTER 0x1023 | ||
513 | |||
514 | /* Test Purposes Only */ | ||
515 | #define WSM_MIB_ID_BLOCK_ACK_INFO 0x100D | ||
516 | |||
517 | /* 4.42 UseMultiTxConfMessage */ | ||
518 | #define WSM_MIB_USE_MULTI_TX_CONF 0x1024 | ||
519 | |||
520 | /* 4.43 Keep-alive period */ | ||
521 | #define WSM_MIB_ID_KEEP_ALIVE_PERIOD 0x1025 | ||
522 | |||
523 | /* 4.44 Disable BSSID filter */ | ||
524 | #define WSM_MIB_ID_DISABLE_BSSID_FILTER 0x1026 | ||
525 | |||
526 | /* Frame template types */ | ||
527 | #define WSM_FRAME_TYPE_PROBE_REQUEST (0) | ||
528 | #define WSM_FRAME_TYPE_BEACON (1) | ||
529 | #define WSM_FRAME_TYPE_NULL (2) | ||
530 | #define WSM_FRAME_TYPE_QOS_NULL (3) | ||
531 | #define WSM_FRAME_TYPE_PS_POLL (4) | ||
532 | #define WSM_FRAME_TYPE_PROBE_RESPONSE (5) | ||
533 | |||
534 | #define WSM_FRAME_GREENFIELD (0x80) /* See 4.11 */ | ||
535 | |||
536 | /* Status */ | ||
537 | /* The WSM firmware has completed a request */ | ||
538 | /* successfully. */ | ||
539 | #define WSM_STATUS_SUCCESS (0) | ||
540 | |||
541 | /* This is a generic failure code if other error codes do */ | ||
542 | /* not apply. */ | ||
543 | #define WSM_STATUS_FAILURE (1) | ||
544 | |||
545 | /* A request contains one or more invalid parameters. */ | ||
546 | #define WSM_INVALID_PARAMETER (2) | ||
547 | |||
548 | /* The request cannot perform because the device is in */ | ||
549 | /* an inappropriate mode. */ | ||
550 | #define WSM_ACCESS_DENIED (3) | ||
551 | |||
552 | /* The frame received includes a decryption error. */ | ||
553 | #define WSM_STATUS_DECRYPTFAILURE (4) | ||
554 | |||
555 | /* A MIC failure is detected in the received packets. */ | ||
556 | #define WSM_STATUS_MICFAILURE (5) | ||
557 | |||
558 | /* The transmit request failed due to retry limit being */ | ||
559 | /* exceeded. */ | ||
560 | #define WSM_STATUS_RETRY_EXCEEDED (6) | ||
561 | |||
562 | /* The transmit request failed due to MSDU life time */ | ||
563 | /* being exceeded. */ | ||
564 | #define WSM_STATUS_TX_LIFETIME_EXCEEDED (7) | ||
565 | |||
566 | /* The link to the AP is lost. */ | ||
567 | #define WSM_STATUS_LINK_LOST (8) | ||
568 | |||
569 | /* No key was found for the encrypted frame */ | ||
570 | #define WSM_STATUS_NO_KEY_FOUND (9) | ||
571 | |||
572 | /* Jammer was detected when transmitting this frame */ | ||
573 | #define WSM_STATUS_JAMMER_DETECTED (10) | ||
574 | |||
575 | /* The message should be requeued later. */ | ||
576 | /* This is applicable only to Transmit */ | ||
577 | #define WSM_REQUEUE (11) | ||
578 | |||
579 | /* Advanced filtering options */ | ||
580 | #define WSM_MAX_FILTER_ELEMENTS (4) | ||
581 | |||
582 | #define WSM_FILTER_ACTION_IGNORE (0) | ||
583 | #define WSM_FILTER_ACTION_FILTER_IN (1) | ||
584 | #define WSM_FILTER_ACTION_FILTER_OUT (2) | ||
585 | |||
586 | #define WSM_FILTER_PORT_TYPE_DST (0) | ||
587 | #define WSM_FILTER_PORT_TYPE_SRC (1) | ||
588 | |||
589 | /* Actual header of WSM messages */ | ||
590 | struct wsm_hdr { | ||
591 | __le16 len; | ||
592 | __le16 id; | ||
593 | }; | ||
594 | |||
595 | #define WSM_TX_SEQ_MAX (7) | ||
596 | #define WSM_TX_SEQ(seq) \ | ||
597 | ((seq & WSM_TX_SEQ_MAX) << 13) | ||
598 | #define WSM_TX_LINK_ID_MAX (0x0F) | ||
599 | #define WSM_TX_LINK_ID(link_id) \ | ||
600 | ((link_id & WSM_TX_LINK_ID_MAX) << 6) | ||
601 | |||
602 | #define MAX_BEACON_SKIP_TIME_MS 1000 | ||
603 | |||
604 | #define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 3 / 2) | ||
605 | |||
606 | /* ******************************************************************** */ | ||
607 | /* WSM capability */ | ||
608 | |||
609 | #define WSM_STARTUP_IND_ID 0x0801 | ||
610 | |||
611 | struct wsm_startup_ind { | ||
612 | u16 input_buffers; | ||
613 | u16 input_buffer_size; | ||
614 | u16 status; | ||
615 | u16 hw_id; | ||
616 | u16 hw_subid; | ||
617 | u16 fw_cap; | ||
618 | u16 fw_type; | ||
619 | u16 fw_api; | ||
620 | u16 fw_build; | ||
621 | u16 fw_ver; | ||
622 | char fw_label[128]; | ||
623 | u32 config[4]; | ||
624 | }; | ||
625 | |||
626 | /* ******************************************************************** */ | ||
627 | /* WSM commands */ | ||
628 | |||
629 | /* 3.1 */ | ||
630 | #define WSM_CONFIGURATION_REQ_ID 0x0009 | ||
631 | #define WSM_CONFIGURATION_RESP_ID 0x0409 | ||
632 | |||
633 | struct wsm_tx_power_range { | ||
634 | int min_power_level; | ||
635 | int max_power_level; | ||
636 | u32 stepping; | ||
637 | }; | ||
638 | |||
639 | struct wsm_configuration { | ||
640 | /* [in] */ u32 dot11MaxTransmitMsduLifeTime; | ||
641 | /* [in] */ u32 dot11MaxReceiveLifeTime; | ||
642 | /* [in] */ u32 dot11RtsThreshold; | ||
643 | /* [in, out] */ u8 *dot11StationId; | ||
644 | /* [in] */ const void *dpdData; | ||
645 | /* [in] */ size_t dpdData_size; | ||
646 | /* [out] */ u8 dot11FrequencyBandsSupported; | ||
647 | /* [out] */ u32 supportedRateMask; | ||
648 | /* [out] */ struct wsm_tx_power_range txPowerRange[2]; | ||
649 | }; | ||
650 | |||
651 | int wsm_configuration(struct cw1200_common *priv, | ||
652 | struct wsm_configuration *arg); | ||
653 | |||
654 | /* 3.3 */ | ||
655 | #define WSM_RESET_REQ_ID 0x000A | ||
656 | #define WSM_RESET_RESP_ID 0x040A | ||
657 | struct wsm_reset { | ||
658 | /* [in] */ int link_id; | ||
659 | /* [in] */ bool reset_statistics; | ||
660 | }; | ||
661 | |||
662 | int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg); | ||
663 | |||
664 | /* 3.5 */ | ||
665 | #define WSM_READ_MIB_REQ_ID 0x0005 | ||
666 | #define WSM_READ_MIB_RESP_ID 0x0405 | ||
667 | int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *buf, | ||
668 | size_t buf_size); | ||
669 | |||
670 | /* 3.7 */ | ||
671 | #define WSM_WRITE_MIB_REQ_ID 0x0006 | ||
672 | #define WSM_WRITE_MIB_RESP_ID 0x0406 | ||
673 | int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *buf, | ||
674 | size_t buf_size); | ||
675 | |||
676 | /* 3.9 */ | ||
677 | #define WSM_START_SCAN_REQ_ID 0x0007 | ||
678 | #define WSM_START_SCAN_RESP_ID 0x0407 | ||
679 | |||
680 | struct wsm_ssid { | ||
681 | u8 ssid[32]; | ||
682 | u32 length; | ||
683 | }; | ||
684 | |||
685 | struct wsm_scan_ch { | ||
686 | u16 number; | ||
687 | u32 min_chan_time; | ||
688 | u32 max_chan_time; | ||
689 | u32 tx_power_level; | ||
690 | }; | ||
691 | |||
692 | struct wsm_scan { | ||
693 | /* WSM_PHY_BAND_... */ | ||
694 | u8 band; | ||
695 | |||
696 | /* WSM_SCAN_TYPE_... */ | ||
697 | u8 type; | ||
698 | |||
699 | /* WSM_SCAN_FLAG_... */ | ||
700 | u8 flags; | ||
701 | |||
702 | /* WSM_TRANSMIT_RATE_... */ | ||
703 | u8 max_tx_rate; | ||
704 | |||
705 | /* Interval period in TUs that the device shall the re- */ | ||
706 | /* execute the requested scan. Max value supported by the device */ | ||
707 | /* is 256s. */ | ||
708 | u32 auto_scan_interval; | ||
709 | |||
710 | /* Number of probe requests (per SSID) sent to one (1) */ | ||
711 | /* channel. Zero (0) means that none is send, which */ | ||
712 | /* means that a passive scan is to be done. Value */ | ||
713 | /* greater than zero (0) means that an active scan is to */ | ||
714 | /* be done. */ | ||
715 | u32 num_probes; | ||
716 | |||
717 | /* Number of channels to be scanned. */ | ||
718 | /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */ | ||
719 | u8 num_channels; | ||
720 | |||
721 | /* Number of SSID provided in the scan command (this */ | ||
722 | /* is zero (0) in broadcast scan) */ | ||
723 | /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */ | ||
724 | u8 num_ssids; | ||
725 | |||
726 | /* The delay time (in microseconds) period */ | ||
727 | /* before sending a probe-request. */ | ||
728 | u8 probe_delay; | ||
729 | |||
730 | /* SSIDs to be scanned [numOfSSIDs]; */ | ||
731 | struct wsm_ssid *ssids; | ||
732 | |||
733 | /* Channels to be scanned [numOfChannels]; */ | ||
734 | struct wsm_scan_ch *ch; | ||
735 | }; | ||
736 | |||
737 | int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg); | ||
738 | |||
739 | /* 3.11 */ | ||
740 | #define WSM_STOP_SCAN_REQ_ID 0x0008 | ||
741 | #define WSM_STOP_SCAN_RESP_ID 0x0408 | ||
742 | int wsm_stop_scan(struct cw1200_common *priv); | ||
743 | |||
744 | /* 3.13 */ | ||
745 | #define WSM_SCAN_COMPLETE_IND_ID 0x0806 | ||
746 | struct wsm_scan_complete { | ||
747 | /* WSM_STATUS_... */ | ||
748 | u32 status; | ||
749 | |||
750 | /* WSM_PSM_... */ | ||
751 | u8 psm; | ||
752 | |||
753 | /* Number of channels that the scan operation completed. */ | ||
754 | u8 num_channels; | ||
755 | }; | ||
756 | |||
757 | /* 3.14 */ | ||
758 | #define WSM_TX_CONFIRM_IND_ID 0x0404 | ||
759 | #define WSM_MULTI_TX_CONFIRM_ID 0x041E | ||
760 | |||
761 | struct wsm_tx_confirm { | ||
762 | /* Packet identifier used in wsm_tx. */ | ||
763 | u32 packet_id; | ||
764 | |||
765 | /* WSM_STATUS_... */ | ||
766 | u32 status; | ||
767 | |||
768 | /* WSM_TRANSMIT_RATE_... */ | ||
769 | u8 tx_rate; | ||
770 | |||
771 | /* The number of times the frame was transmitted */ | ||
772 | /* without receiving an acknowledgement. */ | ||
773 | u8 ack_failures; | ||
774 | |||
775 | /* WSM_TX_STATUS_... */ | ||
776 | u16 flags; | ||
777 | |||
778 | /* The total time in microseconds that the frame spent in */ | ||
779 | /* the WLAN device before transmission as completed. */ | ||
780 | u32 media_delay; | ||
781 | |||
782 | /* The total time in microseconds that the frame spent in */ | ||
783 | /* the WLAN device before transmission was started. */ | ||
784 | u32 tx_queue_delay; | ||
785 | }; | ||
786 | |||
787 | /* 3.15 */ | ||
788 | typedef void (*wsm_tx_confirm_cb) (struct cw1200_common *priv, | ||
789 | struct wsm_tx_confirm *arg); | ||
790 | |||
791 | /* Note that ideology of wsm_tx struct is different against the rest of | ||
792 | * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input | ||
793 | * argument for WSM call, but a prepared bytestream to be sent to firmware. | ||
794 | * It is filled partly in cw1200_tx, partly in low-level WSM code. | ||
795 | * Please pay attention once again: ideology is different. | ||
796 | * | ||
797 | * Legend: | ||
798 | * - [in]: cw1200_tx must fill this field. | ||
799 | * - [wsm]: the field is filled by low-level WSM. | ||
800 | */ | ||
801 | struct wsm_tx { | ||
802 | /* common WSM header */ | ||
803 | struct wsm_hdr hdr; | ||
804 | |||
805 | /* Packet identifier that meant to be used in completion. */ | ||
806 | __le32 packet_id; | ||
807 | |||
808 | /* WSM_TRANSMIT_RATE_... */ | ||
809 | u8 max_tx_rate; | ||
810 | |||
811 | /* WSM_QUEUE_... */ | ||
812 | u8 queue_id; | ||
813 | |||
814 | /* True: another packet is pending on the host for transmission. */ | ||
815 | u8 more; | ||
816 | |||
817 | /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */ | ||
818 | /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */ | ||
819 | /* Bits 3:1 - PTA Priority */ | ||
820 | /* Bits 6:4 - Tx Rate Retry Policy */ | ||
821 | /* Bit 7 - Reserved */ | ||
822 | u8 flags; | ||
823 | |||
824 | /* Should be 0. */ | ||
825 | __le32 reserved; | ||
826 | |||
827 | /* The elapsed time in TUs, after the initial transmission */ | ||
828 | /* of an MSDU, after which further attempts to transmit */ | ||
829 | /* the MSDU shall be terminated. Overrides the global */ | ||
830 | /* dot11MaxTransmitMsduLifeTime setting [optional] */ | ||
831 | /* Device will set the default value if this is 0. */ | ||
832 | __le32 expire_time; | ||
833 | |||
834 | /* WSM_HT_TX_... */ | ||
835 | __le32 ht_tx_parameters; | ||
836 | }; | ||
837 | |||
838 | /* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */ | ||
839 | #define WSM_TX_EXTRA_HEADROOM (28) | ||
840 | |||
841 | /* 3.16 */ | ||
842 | #define WSM_RECEIVE_IND_ID 0x0804 | ||
843 | |||
844 | struct wsm_rx { | ||
845 | /* WSM_STATUS_... */ | ||
846 | __le32 status; | ||
847 | |||
848 | /* Specifies the channel of the received packet. */ | ||
849 | __le16 channel_number; | ||
850 | |||
851 | /* WSM_TRANSMIT_RATE_... */ | ||
852 | u8 rx_rate; | ||
853 | |||
854 | /* This value is expressed in signed Q8.0 format for */ | ||
855 | /* RSSI and unsigned Q7.1 format for RCPI. */ | ||
856 | u8 rcpi_rssi; | ||
857 | |||
858 | /* WSM_RX_STATUS_... */ | ||
859 | __le32 flags; | ||
860 | |||
861 | /* Payload */ | ||
862 | u8 data[0]; | ||
863 | } __packed; | ||
864 | |||
865 | /* = sizeof(generic hi hdr) + sizeof(wsm hdr) */ | ||
866 | #define WSM_RX_EXTRA_HEADROOM (16) | ||
867 | |||
868 | typedef void (*wsm_rx_cb) (struct cw1200_common *priv, struct wsm_rx *arg, | ||
869 | struct sk_buff **skb_p); | ||
870 | |||
871 | /* 3.17 */ | ||
872 | struct wsm_event { | ||
873 | /* WSM_STATUS_... */ | ||
874 | /* [out] */ u32 id; | ||
875 | |||
876 | /* Indication parameters. */ | ||
877 | /* For error indication, this shall be a 32-bit WSM status. */ | ||
878 | /* For RCPI or RSSI indication, this should be an 8-bit */ | ||
879 | /* RCPI or RSSI value. */ | ||
880 | /* [out] */ u32 data; | ||
881 | }; | ||
882 | |||
883 | struct cw1200_wsm_event { | ||
884 | struct list_head link; | ||
885 | struct wsm_event evt; | ||
886 | }; | ||
887 | |||
888 | /* 3.18 - 3.22 */ | ||
889 | /* Measurement. Skipped for now. Irrelevent. */ | ||
890 | |||
891 | typedef void (*wsm_event_cb) (struct cw1200_common *priv, | ||
892 | struct wsm_event *arg); | ||
893 | |||
894 | /* 3.23 */ | ||
895 | #define WSM_JOIN_REQ_ID 0x000B | ||
896 | #define WSM_JOIN_RESP_ID 0x040B | ||
897 | |||
898 | struct wsm_join { | ||
899 | /* WSM_JOIN_MODE_... */ | ||
900 | u8 mode; | ||
901 | |||
902 | /* WSM_PHY_BAND_... */ | ||
903 | u8 band; | ||
904 | |||
905 | /* Specifies the channel number to join. The channel */ | ||
906 | /* number will be mapped to an actual frequency */ | ||
907 | /* according to the band */ | ||
908 | u16 channel_number; | ||
909 | |||
910 | /* Specifies the BSSID of the BSS or IBSS to be joined */ | ||
911 | /* or the IBSS to be started. */ | ||
912 | u8 bssid[6]; | ||
913 | |||
914 | /* ATIM window of IBSS */ | ||
915 | /* When ATIM window is zero the initiated IBSS does */ | ||
916 | /* not support power saving. */ | ||
917 | u16 atim_window; | ||
918 | |||
919 | /* WSM_JOIN_PREAMBLE_... */ | ||
920 | u8 preamble_type; | ||
921 | |||
922 | /* Specifies if a probe request should be send with the */ | ||
923 | /* specified SSID when joining to the network. */ | ||
924 | u8 probe_for_join; | ||
925 | |||
926 | /* DTIM Period (In multiples of beacon interval) */ | ||
927 | u8 dtim_period; | ||
928 | |||
929 | /* WSM_JOIN_FLAGS_... */ | ||
930 | u8 flags; | ||
931 | |||
932 | /* Length of the SSID */ | ||
933 | u32 ssid_len; | ||
934 | |||
935 | /* Specifies the SSID of the IBSS to join or start */ | ||
936 | u8 ssid[32]; | ||
937 | |||
938 | /* Specifies the time between TBTTs in TUs */ | ||
939 | u32 beacon_interval; | ||
940 | |||
941 | /* A bit mask that defines the BSS basic rate set. */ | ||
942 | u32 basic_rate_set; | ||
943 | }; | ||
944 | |||
945 | struct wsm_join_cnf { | ||
946 | u32 status; | ||
947 | |||
948 | /* Minimum transmission power level in units of 0.1dBm */ | ||
949 | u32 min_power_level; | ||
950 | |||
951 | /* Maximum transmission power level in units of 0.1dBm */ | ||
952 | u32 max_power_level; | ||
953 | }; | ||
954 | |||
955 | int wsm_join(struct cw1200_common *priv, struct wsm_join *arg); | ||
956 | |||
957 | /* 3.24 */ | ||
958 | struct wsm_join_complete { | ||
959 | /* WSM_STATUS_... */ | ||
960 | u32 status; | ||
961 | }; | ||
962 | |||
963 | /* 3.25 */ | ||
964 | #define WSM_SET_PM_REQ_ID 0x0010 | ||
965 | #define WSM_SET_PM_RESP_ID 0x0410 | ||
966 | struct wsm_set_pm { | ||
967 | /* WSM_PSM_... */ | ||
968 | u8 mode; | ||
969 | |||
970 | /* in unit of 500us; 0 to use default */ | ||
971 | u8 fast_psm_idle_period; | ||
972 | |||
973 | /* in unit of 500us; 0 to use default */ | ||
974 | u8 ap_psm_change_period; | ||
975 | |||
976 | /* in unit of 500us; 0 to disable auto-pspoll */ | ||
977 | u8 min_auto_pspoll_period; | ||
978 | }; | ||
979 | |||
980 | int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg); | ||
981 | |||
982 | /* 3.27 */ | ||
983 | struct wsm_set_pm_complete { | ||
984 | u8 psm; /* WSM_PSM_... */ | ||
985 | }; | ||
986 | |||
987 | /* 3.28 */ | ||
988 | #define WSM_SET_BSS_PARAMS_REQ_ID 0x0011 | ||
989 | #define WSM_SET_BSS_PARAMS_RESP_ID 0x0411 | ||
990 | struct wsm_set_bss_params { | ||
991 | /* This resets the beacon loss counters only */ | ||
992 | u8 reset_beacon_loss; | ||
993 | |||
994 | /* The number of lost consecutive beacons after which */ | ||
995 | /* the WLAN device should indicate the BSS-Lost event */ | ||
996 | /* to the WLAN host driver. */ | ||
997 | u8 beacon_lost_count; | ||
998 | |||
999 | /* The AID received during the association process. */ | ||
1000 | u16 aid; | ||
1001 | |||
1002 | /* The operational rate set mask */ | ||
1003 | u32 operational_rate_set; | ||
1004 | }; | ||
1005 | |||
1006 | int wsm_set_bss_params(struct cw1200_common *priv, | ||
1007 | const struct wsm_set_bss_params *arg); | ||
1008 | |||
1009 | /* 3.30 */ | ||
1010 | #define WSM_ADD_KEY_REQ_ID 0x000C | ||
1011 | #define WSM_ADD_KEY_RESP_ID 0x040C | ||
1012 | struct wsm_add_key { | ||
1013 | u8 type; /* WSM_KEY_TYPE_... */ | ||
1014 | u8 index; /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */ | ||
1015 | u16 reserved; | ||
1016 | union { | ||
1017 | struct { | ||
1018 | u8 peer[6]; /* MAC address of the | ||
1019 | * peer station */ | ||
1020 | u8 reserved; | ||
1021 | u8 keylen; /* Key length in bytes */ | ||
1022 | u8 keydata[16]; /* Key data */ | ||
1023 | } __packed wep_pairwise; | ||
1024 | struct { | ||
1025 | u8 keyid; /* Unique per key identifier | ||
1026 | * (0..3) */ | ||
1027 | u8 keylen; /* Key length in bytes */ | ||
1028 | u16 reserved; | ||
1029 | u8 keydata[16]; /* Key data */ | ||
1030 | } __packed wep_group; | ||
1031 | struct { | ||
1032 | u8 peer[6]; /* MAC address of the | ||
1033 | * peer station */ | ||
1034 | u16 reserved; | ||
1035 | u8 keydata[16]; /* TKIP key data */ | ||
1036 | u8 rx_mic_key[8]; /* Rx MIC key */ | ||
1037 | u8 tx_mic_key[8]; /* Tx MIC key */ | ||
1038 | } __packed tkip_pairwise; | ||
1039 | struct { | ||
1040 | u8 keydata[16]; /* TKIP key data */ | ||
1041 | u8 rx_mic_key[8]; /* Rx MIC key */ | ||
1042 | u8 keyid; /* Key ID */ | ||
1043 | u8 reserved[3]; | ||
1044 | u8 rx_seqnum[8]; /* Receive Sequence Counter */ | ||
1045 | } __packed tkip_group; | ||
1046 | struct { | ||
1047 | u8 peer[6]; /* MAC address of the | ||
1048 | * peer station */ | ||
1049 | u16 reserved; | ||
1050 | u8 keydata[16]; /* AES key data */ | ||
1051 | } __packed aes_pairwise; | ||
1052 | struct { | ||
1053 | u8 keydata[16]; /* AES key data */ | ||
1054 | u8 keyid; /* Key ID */ | ||
1055 | u8 reserved[3]; | ||
1056 | u8 rx_seqnum[8]; /* Receive Sequence Counter */ | ||
1057 | } __packed aes_group; | ||
1058 | struct { | ||
1059 | u8 peer[6]; /* MAC address of the | ||
1060 | * peer station */ | ||
1061 | u8 keyid; /* Key ID */ | ||
1062 | u8 reserved; | ||
1063 | u8 keydata[16]; /* WAPI key data */ | ||
1064 | u8 mic_key[16]; /* MIC key data */ | ||
1065 | } __packed wapi_pairwise; | ||
1066 | struct { | ||
1067 | u8 keydata[16]; /* WAPI key data */ | ||
1068 | u8 mic_key[16]; /* MIC key data */ | ||
1069 | u8 keyid; /* Key ID */ | ||
1070 | u8 reserved[3]; | ||
1071 | } __packed wapi_group; | ||
1072 | } __packed; | ||
1073 | } __packed; | ||
1074 | |||
1075 | int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg); | ||
1076 | |||
1077 | /* 3.32 */ | ||
1078 | #define WSM_REMOVE_KEY_REQ_ID 0x000D | ||
1079 | #define WSM_REMOVE_KEY_RESP_ID 0x040D | ||
1080 | struct wsm_remove_key { | ||
1081 | u8 index; /* Key entry index : 0-10 */ | ||
1082 | }; | ||
1083 | |||
1084 | int wsm_remove_key(struct cw1200_common *priv, | ||
1085 | const struct wsm_remove_key *arg); | ||
1086 | |||
1087 | /* 3.34 */ | ||
1088 | struct wsm_set_tx_queue_params { | ||
1089 | /* WSM_ACK_POLICY_... */ | ||
1090 | u8 ackPolicy; | ||
1091 | |||
1092 | /* Medium Time of TSPEC (in 32us units) allowed per */ | ||
1093 | /* One Second Averaging Period for this queue. */ | ||
1094 | u16 allowedMediumTime; | ||
1095 | |||
1096 | /* dot11MaxTransmitMsduLifetime to be used for the */ | ||
1097 | /* specified queue. */ | ||
1098 | u32 maxTransmitLifetime; | ||
1099 | }; | ||
1100 | |||
1101 | struct wsm_tx_queue_params { | ||
1102 | /* NOTE: index is a linux queue id. */ | ||
1103 | struct wsm_set_tx_queue_params params[4]; | ||
1104 | }; | ||
1105 | |||
1106 | |||
1107 | #define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time,\ | ||
1108 | max_life_time) \ | ||
1109 | do { \ | ||
1110 | struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \ | ||
1111 | p->ackPolicy = (ack_policy); \ | ||
1112 | p->allowedMediumTime = (allowed_time); \ | ||
1113 | p->maxTransmitLifetime = (max_life_time); \ | ||
1114 | } while (0) | ||
1115 | |||
1116 | int wsm_set_tx_queue_params(struct cw1200_common *priv, | ||
1117 | const struct wsm_set_tx_queue_params *arg, u8 id); | ||
1118 | |||
1119 | /* 3.36 */ | ||
1120 | #define WSM_EDCA_PARAMS_REQ_ID 0x0013 | ||
1121 | #define WSM_EDCA_PARAMS_RESP_ID 0x0413 | ||
1122 | struct wsm_edca_queue_params { | ||
1123 | /* CWmin (in slots) for the access class. */ | ||
1124 | __le16 cwmin; | ||
1125 | |||
1126 | /* CWmax (in slots) for the access class. */ | ||
1127 | __le16 cwmax; | ||
1128 | |||
1129 | /* AIFS (in slots) for the access class. */ | ||
1130 | __le16 aifns; | ||
1131 | |||
1132 | /* TX OP Limit (in microseconds) for the access class. */ | ||
1133 | __le16 txop_limit; | ||
1134 | |||
1135 | /* dot11MaxReceiveLifetime to be used for the specified */ | ||
1136 | /* the access class. Overrides the global */ | ||
1137 | /* dot11MaxReceiveLifetime value */ | ||
1138 | __le32 max_rx_lifetime; | ||
1139 | } __packed; | ||
1140 | |||
1141 | struct wsm_edca_params { | ||
1142 | /* NOTE: index is a linux queue id. */ | ||
1143 | struct wsm_edca_queue_params params[4]; | ||
1144 | bool uapsd_enable[4]; | ||
1145 | }; | ||
1146 | |||
1147 | #define TXOP_UNIT 32 | ||
1148 | #define WSM_EDCA_SET(__edca, __queue, __aifs, __cw_min, __cw_max, __txop, __lifetime,\ | ||
1149 | __uapsd) \ | ||
1150 | do { \ | ||
1151 | struct wsm_edca_queue_params *p = &(__edca)->params[__queue]; \ | ||
1152 | p->cwmin = (__cw_min); \ | ||
1153 | p->cwmax = (__cw_max); \ | ||
1154 | p->aifns = (__aifs); \ | ||
1155 | p->txop_limit = ((__txop) * TXOP_UNIT); \ | ||
1156 | p->max_rx_lifetime = (__lifetime); \ | ||
1157 | (__edca)->uapsd_enable[__queue] = (__uapsd); \ | ||
1158 | } while (0) | ||
1159 | |||
1160 | int wsm_set_edca_params(struct cw1200_common *priv, | ||
1161 | const struct wsm_edca_params *arg); | ||
1162 | |||
1163 | int wsm_set_uapsd_param(struct cw1200_common *priv, | ||
1164 | const struct wsm_edca_params *arg); | ||
1165 | |||
1166 | /* 3.38 */ | ||
1167 | /* Set-System info. Skipped for now. Irrelevent. */ | ||
1168 | |||
1169 | /* 3.40 */ | ||
1170 | #define WSM_SWITCH_CHANNEL_REQ_ID 0x0016 | ||
1171 | #define WSM_SWITCH_CHANNEL_RESP_ID 0x0416 | ||
1172 | |||
1173 | struct wsm_switch_channel { | ||
1174 | /* 1 - means the STA shall not transmit any further */ | ||
1175 | /* frames until the channel switch has completed */ | ||
1176 | u8 mode; | ||
1177 | |||
1178 | /* Number of TBTTs until channel switch occurs. */ | ||
1179 | /* 0 - indicates switch shall occur at any time */ | ||
1180 | /* 1 - occurs immediately before the next TBTT */ | ||
1181 | u8 switch_count; | ||
1182 | |||
1183 | /* The new channel number to switch to. */ | ||
1184 | /* Note this is defined as per section 2.7. */ | ||
1185 | u16 channel_number; | ||
1186 | }; | ||
1187 | |||
1188 | int wsm_switch_channel(struct cw1200_common *priv, | ||
1189 | const struct wsm_switch_channel *arg); | ||
1190 | |||
1191 | typedef void (*wsm_channel_switch_cb) (struct cw1200_common *priv); | ||
1192 | |||
1193 | #define WSM_START_REQ_ID 0x0017 | ||
1194 | #define WSM_START_RESP_ID 0x0417 | ||
1195 | |||
1196 | struct wsm_start { | ||
1197 | /* WSM_START_MODE_... */ | ||
1198 | /* [in] */ u8 mode; | ||
1199 | |||
1200 | /* WSM_PHY_BAND_... */ | ||
1201 | /* [in] */ u8 band; | ||
1202 | |||
1203 | /* Channel number */ | ||
1204 | /* [in] */ u16 channel_number; | ||
1205 | |||
1206 | /* Client Traffic window in units of TU */ | ||
1207 | /* Valid only when mode == ..._P2P */ | ||
1208 | /* [in] */ u32 ct_window; | ||
1209 | |||
1210 | /* Interval between two consecutive */ | ||
1211 | /* beacon transmissions in TU. */ | ||
1212 | /* [in] */ u32 beacon_interval; | ||
1213 | |||
1214 | /* DTIM period in terms of beacon intervals */ | ||
1215 | /* [in] */ u8 dtim_period; | ||
1216 | |||
1217 | /* WSM_JOIN_PREAMBLE_... */ | ||
1218 | /* [in] */ u8 preamble; | ||
1219 | |||
1220 | /* The delay time (in microseconds) period */ | ||
1221 | /* before sending a probe-request. */ | ||
1222 | /* [in] */ u8 probe_delay; | ||
1223 | |||
1224 | /* Length of the SSID */ | ||
1225 | /* [in] */ u8 ssid_len; | ||
1226 | |||
1227 | /* SSID of the BSS or P2P_GO to be started now. */ | ||
1228 | /* [in] */ u8 ssid[32]; | ||
1229 | |||
1230 | /* The basic supported rates for the MiniAP. */ | ||
1231 | /* [in] */ u32 basic_rate_set; | ||
1232 | }; | ||
1233 | |||
1234 | int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg); | ||
1235 | |||
1236 | #define WSM_BEACON_TRANSMIT_REQ_ID 0x0018 | ||
1237 | #define WSM_BEACON_TRANSMIT_RESP_ID 0x0418 | ||
1238 | |||
1239 | struct wsm_beacon_transmit { | ||
1240 | /* 1: enable; 0: disable */ | ||
1241 | /* [in] */ u8 enable_beaconing; | ||
1242 | }; | ||
1243 | |||
1244 | int wsm_beacon_transmit(struct cw1200_common *priv, | ||
1245 | const struct wsm_beacon_transmit *arg); | ||
1246 | |||
1247 | int wsm_start_find(struct cw1200_common *priv); | ||
1248 | |||
1249 | int wsm_stop_find(struct cw1200_common *priv); | ||
1250 | |||
1251 | typedef void (*wsm_find_complete_cb) (struct cw1200_common *priv, u32 status); | ||
1252 | |||
1253 | struct wsm_suspend_resume { | ||
1254 | /* See 3.52 */ | ||
1255 | /* Link ID */ | ||
1256 | /* [out] */ int link_id; | ||
1257 | /* Stop sending further Tx requests down to device for this link */ | ||
1258 | /* [out] */ bool stop; | ||
1259 | /* Transmit multicast Frames */ | ||
1260 | /* [out] */ bool multicast; | ||
1261 | /* The AC on which Tx to be suspended /resumed. */ | ||
1262 | /* This is applicable only for U-APSD */ | ||
1263 | /* WSM_QUEUE_... */ | ||
1264 | /* [out] */ int queue; | ||
1265 | }; | ||
1266 | |||
1267 | typedef void (*wsm_suspend_resume_cb) (struct cw1200_common *priv, | ||
1268 | struct wsm_suspend_resume *arg); | ||
1269 | |||
1270 | /* 3.54 Update-IE request. */ | ||
1271 | struct wsm_update_ie { | ||
1272 | /* WSM_UPDATE_IE_... */ | ||
1273 | /* [in] */ u16 what; | ||
1274 | /* [in] */ u16 count; | ||
1275 | /* [in] */ u8 *ies; | ||
1276 | /* [in] */ size_t length; | ||
1277 | }; | ||
1278 | |||
1279 | int wsm_update_ie(struct cw1200_common *priv, | ||
1280 | const struct wsm_update_ie *arg); | ||
1281 | |||
1282 | /* 3.56 */ | ||
1283 | struct wsm_map_link { | ||
1284 | /* MAC address of the remote device */ | ||
1285 | /* [in] */ u8 mac_addr[6]; | ||
1286 | /* [in] */ u8 link_id; | ||
1287 | }; | ||
1288 | |||
1289 | int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg); | ||
1290 | |||
1291 | /* ******************************************************************** */ | ||
1292 | /* MIB shortcats */ | ||
1293 | |||
1294 | static inline int wsm_set_output_power(struct cw1200_common *priv, | ||
1295 | int power_level) | ||
1296 | { | ||
1297 | __le32 val = __cpu_to_le32(power_level); | ||
1298 | return wsm_write_mib(priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL, | ||
1299 | &val, sizeof(val)); | ||
1300 | } | ||
1301 | |||
1302 | static inline int wsm_set_beacon_wakeup_period(struct cw1200_common *priv, | ||
1303 | unsigned dtim_interval, | ||
1304 | unsigned listen_interval) | ||
1305 | { | ||
1306 | struct { | ||
1307 | u8 numBeaconPeriods; | ||
1308 | u8 reserved; | ||
1309 | __le16 listenInterval; | ||
1310 | } val = { | ||
1311 | dtim_interval, 0, __cpu_to_le16(listen_interval) | ||
1312 | }; | ||
1313 | |||
1314 | if (dtim_interval > 0xFF || listen_interval > 0xFFFF) | ||
1315 | return -EINVAL; | ||
1316 | else | ||
1317 | return wsm_write_mib(priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD, | ||
1318 | &val, sizeof(val)); | ||
1319 | } | ||
1320 | |||
1321 | struct wsm_rcpi_rssi_threshold { | ||
1322 | u8 rssiRcpiMode; /* WSM_RCPI_RSSI_... */ | ||
1323 | u8 lowerThreshold; | ||
1324 | u8 upperThreshold; | ||
1325 | u8 rollingAverageCount; | ||
1326 | }; | ||
1327 | |||
1328 | static inline int wsm_set_rcpi_rssi_threshold(struct cw1200_common *priv, | ||
1329 | struct wsm_rcpi_rssi_threshold *arg) | ||
1330 | { | ||
1331 | return wsm_write_mib(priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg, | ||
1332 | sizeof(*arg)); | ||
1333 | } | ||
1334 | |||
1335 | struct wsm_mib_counters_table { | ||
1336 | __le32 plcp_errors; | ||
1337 | __le32 fcs_errors; | ||
1338 | __le32 tx_packets; | ||
1339 | __le32 rx_packets; | ||
1340 | __le32 rx_packet_errors; | ||
1341 | __le32 rx_decryption_failures; | ||
1342 | __le32 rx_mic_failures; | ||
1343 | __le32 rx_no_key_failures; | ||
1344 | __le32 tx_multicast_frames; | ||
1345 | __le32 tx_frames_success; | ||
1346 | __le32 tx_frame_failures; | ||
1347 | __le32 tx_frames_retried; | ||
1348 | __le32 tx_frames_multi_retried; | ||
1349 | __le32 rx_frame_duplicates; | ||
1350 | __le32 rts_success; | ||
1351 | __le32 rts_failures; | ||
1352 | __le32 ack_failures; | ||
1353 | __le32 rx_multicast_frames; | ||
1354 | __le32 rx_frames_success; | ||
1355 | __le32 rx_cmac_icv_errors; | ||
1356 | __le32 rx_cmac_replays; | ||
1357 | __le32 rx_mgmt_ccmp_replays; | ||
1358 | } __packed; | ||
1359 | |||
1360 | static inline int wsm_get_counters_table(struct cw1200_common *priv, | ||
1361 | struct wsm_mib_counters_table *arg) | ||
1362 | { | ||
1363 | return wsm_read_mib(priv, WSM_MIB_ID_COUNTERS_TABLE, | ||
1364 | arg, sizeof(*arg)); | ||
1365 | } | ||
1366 | |||
1367 | static inline int wsm_get_station_id(struct cw1200_common *priv, u8 *mac) | ||
1368 | { | ||
1369 | return wsm_read_mib(priv, WSM_MIB_ID_DOT11_STATION_ID, mac, ETH_ALEN); | ||
1370 | } | ||
1371 | |||
1372 | struct wsm_rx_filter { | ||
1373 | bool promiscuous; | ||
1374 | bool bssid; | ||
1375 | bool fcs; | ||
1376 | bool probeResponder; | ||
1377 | }; | ||
1378 | |||
1379 | static inline int wsm_set_rx_filter(struct cw1200_common *priv, | ||
1380 | const struct wsm_rx_filter *arg) | ||
1381 | { | ||
1382 | __le32 val = 0; | ||
1383 | if (arg->promiscuous) | ||
1384 | val |= __cpu_to_le32(BIT(0)); | ||
1385 | if (arg->bssid) | ||
1386 | val |= __cpu_to_le32(BIT(1)); | ||
1387 | if (arg->fcs) | ||
1388 | val |= __cpu_to_le32(BIT(2)); | ||
1389 | if (arg->probeResponder) | ||
1390 | val |= __cpu_to_le32(BIT(3)); | ||
1391 | return wsm_write_mib(priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val)); | ||
1392 | } | ||
1393 | |||
1394 | int wsm_set_probe_responder(struct cw1200_common *priv, bool enable); | ||
1395 | |||
1396 | #define WSM_BEACON_FILTER_IE_HAS_CHANGED BIT(0) | ||
1397 | #define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1) | ||
1398 | #define WSM_BEACON_FILTER_IE_HAS_APPEARED BIT(2) | ||
1399 | |||
1400 | struct wsm_beacon_filter_table_entry { | ||
1401 | u8 ie_id; | ||
1402 | u8 flags; | ||
1403 | u8 oui[3]; | ||
1404 | u8 match_data[3]; | ||
1405 | } __packed; | ||
1406 | |||
1407 | struct wsm_mib_beacon_filter_table { | ||
1408 | __le32 num; | ||
1409 | struct wsm_beacon_filter_table_entry entry[10]; | ||
1410 | } __packed; | ||
1411 | |||
1412 | static inline int wsm_set_beacon_filter_table(struct cw1200_common *priv, | ||
1413 | struct wsm_mib_beacon_filter_table *ft) | ||
1414 | { | ||
1415 | size_t size = __le32_to_cpu(ft->num) * | ||
1416 | sizeof(struct wsm_beacon_filter_table_entry) + | ||
1417 | sizeof(__le32); | ||
1418 | |||
1419 | return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size); | ||
1420 | } | ||
1421 | |||
1422 | #define WSM_BEACON_FILTER_ENABLE BIT(0) /* Enable/disable beacon filtering */ | ||
1423 | #define WSM_BEACON_FILTER_AUTO_ERP BIT(1) /* If 1 FW will handle ERP IE changes internally */ | ||
1424 | |||
1425 | struct wsm_beacon_filter_control { | ||
1426 | int enabled; | ||
1427 | int bcn_count; | ||
1428 | }; | ||
1429 | |||
1430 | static inline int wsm_beacon_filter_control(struct cw1200_common *priv, | ||
1431 | struct wsm_beacon_filter_control *arg) | ||
1432 | { | ||
1433 | struct { | ||
1434 | __le32 enabled; | ||
1435 | __le32 bcn_count; | ||
1436 | } val; | ||
1437 | val.enabled = __cpu_to_le32(arg->enabled); | ||
1438 | val.bcn_count = __cpu_to_le32(arg->bcn_count); | ||
1439 | return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val, | ||
1440 | sizeof(val)); | ||
1441 | } | ||
1442 | |||
1443 | enum wsm_power_mode { | ||
1444 | wsm_power_mode_active = 0, | ||
1445 | wsm_power_mode_doze = 1, | ||
1446 | wsm_power_mode_quiescent = 2, | ||
1447 | }; | ||
1448 | |||
1449 | struct wsm_operational_mode { | ||
1450 | enum wsm_power_mode power_mode; | ||
1451 | int disable_more_flag_usage; | ||
1452 | int perform_ant_diversity; | ||
1453 | }; | ||
1454 | |||
1455 | static inline int wsm_set_operational_mode(struct cw1200_common *priv, | ||
1456 | const struct wsm_operational_mode *arg) | ||
1457 | { | ||
1458 | u8 val = arg->power_mode; | ||
1459 | if (arg->disable_more_flag_usage) | ||
1460 | val |= BIT(4); | ||
1461 | if (arg->perform_ant_diversity) | ||
1462 | val |= BIT(5); | ||
1463 | return wsm_write_mib(priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val, | ||
1464 | sizeof(val)); | ||
1465 | } | ||
1466 | |||
1467 | struct wsm_template_frame { | ||
1468 | u8 frame_type; | ||
1469 | u8 rate; | ||
1470 | struct sk_buff *skb; | ||
1471 | }; | ||
1472 | |||
1473 | static inline int wsm_set_template_frame(struct cw1200_common *priv, | ||
1474 | struct wsm_template_frame *arg) | ||
1475 | { | ||
1476 | int ret; | ||
1477 | u8 *p = skb_push(arg->skb, 4); | ||
1478 | p[0] = arg->frame_type; | ||
1479 | p[1] = arg->rate; | ||
1480 | ((u16 *)p)[1] = __cpu_to_le16(arg->skb->len - 4); | ||
1481 | ret = wsm_write_mib(priv, WSM_MIB_ID_TEMPLATE_FRAME, p, arg->skb->len); | ||
1482 | skb_pull(arg->skb, 4); | ||
1483 | return ret; | ||
1484 | } | ||
1485 | |||
1486 | |||
1487 | struct wsm_protected_mgmt_policy { | ||
1488 | bool protectedMgmtEnable; | ||
1489 | bool unprotectedMgmtFramesAllowed; | ||
1490 | bool encryptionForAuthFrame; | ||
1491 | }; | ||
1492 | |||
1493 | static inline int wsm_set_protected_mgmt_policy(struct cw1200_common *priv, | ||
1494 | struct wsm_protected_mgmt_policy *arg) | ||
1495 | { | ||
1496 | __le32 val = 0; | ||
1497 | int ret; | ||
1498 | if (arg->protectedMgmtEnable) | ||
1499 | val |= __cpu_to_le32(BIT(0)); | ||
1500 | if (arg->unprotectedMgmtFramesAllowed) | ||
1501 | val |= __cpu_to_le32(BIT(1)); | ||
1502 | if (arg->encryptionForAuthFrame) | ||
1503 | val |= __cpu_to_le32(BIT(2)); | ||
1504 | ret = wsm_write_mib(priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY, | ||
1505 | &val, sizeof(val)); | ||
1506 | return ret; | ||
1507 | } | ||
1508 | |||
1509 | struct wsm_mib_block_ack_policy { | ||
1510 | u8 tx_tid; | ||
1511 | u8 reserved1; | ||
1512 | u8 rx_tid; | ||
1513 | u8 reserved2; | ||
1514 | } __packed; | ||
1515 | |||
1516 | static inline int wsm_set_block_ack_policy(struct cw1200_common *priv, | ||
1517 | u8 tx_tid_policy, | ||
1518 | u8 rx_tid_policy) | ||
1519 | { | ||
1520 | struct wsm_mib_block_ack_policy val = { | ||
1521 | .tx_tid = tx_tid_policy, | ||
1522 | .rx_tid = rx_tid_policy, | ||
1523 | }; | ||
1524 | return wsm_write_mib(priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val, | ||
1525 | sizeof(val)); | ||
1526 | } | ||
1527 | |||
1528 | struct wsm_mib_association_mode { | ||
1529 | u8 flags; /* WSM_ASSOCIATION_MODE_... */ | ||
1530 | u8 preamble; /* WSM_JOIN_PREAMBLE_... */ | ||
1531 | u8 greenfield; /* 1 for greenfield */ | ||
1532 | u8 mpdu_start_spacing; | ||
1533 | __le32 basic_rate_set; | ||
1534 | } __packed; | ||
1535 | |||
1536 | static inline int wsm_set_association_mode(struct cw1200_common *priv, | ||
1537 | struct wsm_mib_association_mode *arg) | ||
1538 | { | ||
1539 | return wsm_write_mib(priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg, | ||
1540 | sizeof(*arg)); | ||
1541 | } | ||
1542 | |||
1543 | #define WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED BIT(2) | ||
1544 | #define WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT BIT(3) | ||
1545 | struct wsm_tx_rate_retry_policy { | ||
1546 | u8 index; | ||
1547 | u8 short_retries; | ||
1548 | u8 long_retries; | ||
1549 | /* BIT(2) - Terminate retries when Tx rate retry policy | ||
1550 | * finishes. | ||
1551 | * BIT(3) - Count initial frame transmission as part of | ||
1552 | * rate retry counting but not as a retry | ||
1553 | * attempt */ | ||
1554 | u8 flags; | ||
1555 | u8 rate_recoveries; | ||
1556 | u8 reserved[3]; | ||
1557 | __le32 rate_count_indices[3]; | ||
1558 | } __packed; | ||
1559 | |||
1560 | struct wsm_set_tx_rate_retry_policy { | ||
1561 | u8 num; | ||
1562 | u8 reserved[3]; | ||
1563 | struct wsm_tx_rate_retry_policy tbl[8]; | ||
1564 | } __packed; | ||
1565 | |||
1566 | static inline int wsm_set_tx_rate_retry_policy(struct cw1200_common *priv, | ||
1567 | struct wsm_set_tx_rate_retry_policy *arg) | ||
1568 | { | ||
1569 | size_t size = 4 + arg->num * sizeof(struct wsm_tx_rate_retry_policy); | ||
1570 | return wsm_write_mib(priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg, | ||
1571 | size); | ||
1572 | } | ||
1573 | |||
1574 | /* 4.32 SetEtherTypeDataFrameFilter */ | ||
1575 | struct wsm_ether_type_filter_hdr { | ||
1576 | u8 num; /* Up to WSM_MAX_FILTER_ELEMENTS */ | ||
1577 | u8 reserved[3]; | ||
1578 | } __packed; | ||
1579 | |||
1580 | struct wsm_ether_type_filter { | ||
1581 | u8 action; /* WSM_FILTER_ACTION_XXX */ | ||
1582 | u8 reserved; | ||
1583 | __le16 type; /* Type of ethernet frame */ | ||
1584 | } __packed; | ||
1585 | |||
1586 | static inline int wsm_set_ether_type_filter(struct cw1200_common *priv, | ||
1587 | struct wsm_ether_type_filter_hdr *arg) | ||
1588 | { | ||
1589 | size_t size = sizeof(struct wsm_ether_type_filter_hdr) + | ||
1590 | arg->num * sizeof(struct wsm_ether_type_filter); | ||
1591 | return wsm_write_mib(priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER, | ||
1592 | arg, size); | ||
1593 | } | ||
1594 | |||
1595 | /* 4.33 SetUDPPortDataFrameFilter */ | ||
1596 | struct wsm_udp_port_filter_hdr { | ||
1597 | u8 num; /* Up to WSM_MAX_FILTER_ELEMENTS */ | ||
1598 | u8 reserved[3]; | ||
1599 | } __packed; | ||
1600 | |||
1601 | struct wsm_udp_port_filter { | ||
1602 | u8 action; /* WSM_FILTER_ACTION_XXX */ | ||
1603 | u8 type; /* WSM_FILTER_PORT_TYPE_XXX */ | ||
1604 | __le16 port; /* Port number */ | ||
1605 | } __packed; | ||
1606 | |||
1607 | static inline int wsm_set_udp_port_filter(struct cw1200_common *priv, | ||
1608 | struct wsm_udp_port_filter_hdr *arg) | ||
1609 | { | ||
1610 | size_t size = sizeof(struct wsm_udp_port_filter_hdr) + | ||
1611 | arg->num * sizeof(struct wsm_udp_port_filter); | ||
1612 | return wsm_write_mib(priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER, | ||
1613 | arg, size); | ||
1614 | } | ||
1615 | |||
1616 | /* Undocumented MIBs: */ | ||
1617 | /* 4.35 P2PDeviceInfo */ | ||
1618 | #define D11_MAX_SSID_LEN (32) | ||
1619 | |||
1620 | struct wsm_p2p_device_type { | ||
1621 | __le16 categoryId; | ||
1622 | u8 oui[4]; | ||
1623 | __le16 subCategoryId; | ||
1624 | } __packed; | ||
1625 | |||
1626 | struct wsm_p2p_device_info { | ||
1627 | struct wsm_p2p_device_type primaryDevice; | ||
1628 | u8 reserved1[3]; | ||
1629 | u8 devNameSize; | ||
1630 | u8 localDevName[D11_MAX_SSID_LEN]; | ||
1631 | u8 reserved2[3]; | ||
1632 | u8 numSecDevSupported; | ||
1633 | struct wsm_p2p_device_type secondaryDevices[0]; | ||
1634 | } __packed; | ||
1635 | |||
1636 | /* 4.36 SetWCDMABand - WO */ | ||
1637 | struct wsm_cdma_band { | ||
1638 | u8 WCDMA_Band; | ||
1639 | u8 reserved[3]; | ||
1640 | } __packed; | ||
1641 | |||
1642 | /* 4.37 GroupTxSequenceCounter - RO */ | ||
1643 | struct wsm_group_tx_seq { | ||
1644 | __le32 bits_47_16; | ||
1645 | __le16 bits_15_00; | ||
1646 | __le16 reserved; | ||
1647 | } __packed; | ||
1648 | |||
1649 | /* 4.39 SetHtProtection - WO */ | ||
1650 | #define WSM_DUAL_CTS_PROT_ENB (1 << 0) | ||
1651 | #define WSM_NON_GREENFIELD_STA_PRESENT (1 << 1) | ||
1652 | #define WSM_HT_PROT_MODE__NO_PROT (0 << 2) | ||
1653 | #define WSM_HT_PROT_MODE__NON_MEMBER (1 << 2) | ||
1654 | #define WSM_HT_PROT_MODE__20_MHZ (2 << 2) | ||
1655 | #define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2) | ||
1656 | #define WSM_LSIG_TXOP_PROT_FULL (1 << 4) | ||
1657 | #define WSM_LARGE_L_LENGTH_PROT (1 << 5) | ||
1658 | |||
1659 | struct wsm_ht_protection { | ||
1660 | __le32 flags; | ||
1661 | } __packed; | ||
1662 | |||
1663 | /* 4.40 GPIO Command - R/W */ | ||
1664 | #define WSM_GPIO_COMMAND_SETUP 0 | ||
1665 | #define WSM_GPIO_COMMAND_READ 1 | ||
1666 | #define WSM_GPIO_COMMAND_WRITE 2 | ||
1667 | #define WSM_GPIO_COMMAND_RESET 3 | ||
1668 | #define WSM_GPIO_ALL_PINS 0xFF | ||
1669 | |||
1670 | struct wsm_gpio_command { | ||
1671 | u8 GPIO_Command; | ||
1672 | u8 pin; | ||
1673 | __le16 config; | ||
1674 | } __packed; | ||
1675 | |||
1676 | /* 4.41 TSFCounter - RO */ | ||
1677 | struct wsm_tsf_counter { | ||
1678 | __le64 TSF_Counter; | ||
1679 | } __packed; | ||
1680 | |||
1681 | /* 4.43 Keep alive period */ | ||
1682 | struct wsm_keep_alive_period { | ||
1683 | __le16 keepAlivePeriod; | ||
1684 | u8 reserved[2]; | ||
1685 | } __packed; | ||
1686 | |||
1687 | static inline int wsm_keep_alive_period(struct cw1200_common *priv, | ||
1688 | int period) | ||
1689 | { | ||
1690 | struct wsm_keep_alive_period arg = { | ||
1691 | .keepAlivePeriod = __cpu_to_le16(period), | ||
1692 | }; | ||
1693 | return wsm_write_mib(priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD, | ||
1694 | &arg, sizeof(arg)); | ||
1695 | }; | ||
1696 | |||
1697 | /* BSSID filtering */ | ||
1698 | struct wsm_set_bssid_filtering { | ||
1699 | u8 filter; | ||
1700 | u8 reserved[3]; | ||
1701 | } __packed; | ||
1702 | |||
1703 | static inline int wsm_set_bssid_filtering(struct cw1200_common *priv, | ||
1704 | bool enabled) | ||
1705 | { | ||
1706 | struct wsm_set_bssid_filtering arg = { | ||
1707 | .filter = !enabled, | ||
1708 | }; | ||
1709 | return wsm_write_mib(priv, WSM_MIB_ID_DISABLE_BSSID_FILTER, | ||
1710 | &arg, sizeof(arg)); | ||
1711 | } | ||
1712 | |||
1713 | /* Multicast filtering - 4.5 */ | ||
1714 | struct wsm_mib_multicast_filter { | ||
1715 | __le32 enable; | ||
1716 | __le32 num_addrs; | ||
1717 | u8 macaddrs[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN]; | ||
1718 | } __packed; | ||
1719 | |||
1720 | static inline int wsm_set_multicast_filter(struct cw1200_common *priv, | ||
1721 | struct wsm_mib_multicast_filter *fp) | ||
1722 | { | ||
1723 | return wsm_write_mib(priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE, | ||
1724 | fp, sizeof(*fp)); | ||
1725 | } | ||
1726 | |||
1727 | /* ARP IPv4 filtering - 4.10 */ | ||
1728 | struct wsm_mib_arp_ipv4_filter { | ||
1729 | __le32 enable; | ||
1730 | __be32 ipv4addrs[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES]; | ||
1731 | } __packed; | ||
1732 | |||
1733 | static inline int wsm_set_arp_ipv4_filter(struct cw1200_common *priv, | ||
1734 | struct wsm_mib_arp_ipv4_filter *fp) | ||
1735 | { | ||
1736 | return wsm_write_mib(priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE, | ||
1737 | fp, sizeof(*fp)); | ||
1738 | } | ||
1739 | |||
1740 | /* P2P Power Save Mode Info - 4.31 */ | ||
1741 | struct wsm_p2p_ps_modeinfo { | ||
1742 | u8 oppPsCTWindow; | ||
1743 | u8 count; | ||
1744 | u8 reserved; | ||
1745 | u8 dtimCount; | ||
1746 | __le32 duration; | ||
1747 | __le32 interval; | ||
1748 | __le32 startTime; | ||
1749 | } __packed; | ||
1750 | |||
1751 | static inline int wsm_set_p2p_ps_modeinfo(struct cw1200_common *priv, | ||
1752 | struct wsm_p2p_ps_modeinfo *mi) | ||
1753 | { | ||
1754 | return wsm_write_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO, | ||
1755 | mi, sizeof(*mi)); | ||
1756 | } | ||
1757 | |||
1758 | static inline int wsm_get_p2p_ps_modeinfo(struct cw1200_common *priv, | ||
1759 | struct wsm_p2p_ps_modeinfo *mi) | ||
1760 | { | ||
1761 | return wsm_read_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO, | ||
1762 | mi, sizeof(*mi)); | ||
1763 | } | ||
1764 | |||
1765 | /* UseMultiTxConfMessage */ | ||
1766 | |||
1767 | static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv, | ||
1768 | bool enabled) | ||
1769 | { | ||
1770 | __le32 arg = enabled ? __cpu_to_le32(1) : 0; | ||
1771 | |||
1772 | return wsm_write_mib(priv, WSM_MIB_USE_MULTI_TX_CONF, | ||
1773 | &arg, sizeof(arg)); | ||
1774 | } | ||
1775 | |||
1776 | |||
1777 | /* 4.26 SetUpasdInformation */ | ||
1778 | struct wsm_uapsd_info { | ||
1779 | __le16 uapsd_flags; | ||
1780 | __le16 min_auto_trigger_interval; | ||
1781 | __le16 max_auto_trigger_interval; | ||
1782 | __le16 auto_trigger_step; | ||
1783 | }; | ||
1784 | |||
1785 | static inline int wsm_set_uapsd_info(struct cw1200_common *priv, | ||
1786 | struct wsm_uapsd_info *arg) | ||
1787 | { | ||
1788 | return wsm_write_mib(priv, WSM_MIB_ID_SET_UAPSD_INFORMATION, | ||
1789 | arg, sizeof(*arg)); | ||
1790 | } | ||
1791 | |||
1792 | /* 4.22 OverrideInternalTxRate */ | ||
1793 | struct wsm_override_internal_txrate { | ||
1794 | u8 internalTxRate; | ||
1795 | u8 nonErpInternalTxRate; | ||
1796 | u8 reserved[2]; | ||
1797 | } __packed; | ||
1798 | |||
1799 | static inline int wsm_set_override_internal_txrate(struct cw1200_common *priv, | ||
1800 | struct wsm_override_internal_txrate *arg) | ||
1801 | { | ||
1802 | return wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, | ||
1803 | arg, sizeof(*arg)); | ||
1804 | } | ||
1805 | |||
1806 | /* ******************************************************************** */ | ||
1807 | /* WSM TX port control */ | ||
1808 | |||
1809 | void wsm_lock_tx(struct cw1200_common *priv); | ||
1810 | void wsm_lock_tx_async(struct cw1200_common *priv); | ||
1811 | bool wsm_flush_tx(struct cw1200_common *priv); | ||
1812 | void wsm_unlock_tx(struct cw1200_common *priv); | ||
1813 | |||
1814 | /* ******************************************************************** */ | ||
1815 | /* WSM / BH API */ | ||
1816 | |||
1817 | int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len); | ||
1818 | int wsm_handle_rx(struct cw1200_common *priv, u16 id, struct wsm_hdr *wsm, | ||
1819 | struct sk_buff **skb_p); | ||
1820 | |||
1821 | /* ******************************************************************** */ | ||
1822 | /* wsm_buf API */ | ||
1823 | |||
1824 | struct wsm_buf { | ||
1825 | u8 *begin; | ||
1826 | u8 *data; | ||
1827 | u8 *end; | ||
1828 | }; | ||
1829 | |||
1830 | void wsm_buf_init(struct wsm_buf *buf); | ||
1831 | void wsm_buf_deinit(struct wsm_buf *buf); | ||
1832 | |||
1833 | /* ******************************************************************** */ | ||
1834 | /* wsm_cmd API */ | ||
1835 | |||
1836 | struct wsm_cmd { | ||
1837 | spinlock_t lock; /* Protect structure from multiple access */ | ||
1838 | int done; | ||
1839 | u8 *ptr; | ||
1840 | size_t len; | ||
1841 | void *arg; | ||
1842 | int ret; | ||
1843 | u16 cmd; | ||
1844 | }; | ||
1845 | |||
1846 | /* ******************************************************************** */ | ||
1847 | /* WSM TX buffer access */ | ||
1848 | |||
1849 | int wsm_get_tx(struct cw1200_common *priv, u8 **data, | ||
1850 | size_t *tx_len, int *burst); | ||
1851 | void wsm_txed(struct cw1200_common *priv, u8 *data); | ||
1852 | |||
1853 | /* ******************************************************************** */ | ||
1854 | /* Queue mapping: WSM <---> linux */ | ||
1855 | /* Linux: VO VI BE BK */ | ||
1856 | /* WSM: BE BK VI VO */ | ||
1857 | |||
1858 | static inline u8 wsm_queue_id_to_linux(u8 queue_id) | ||
1859 | { | ||
1860 | static const u8 queue_mapping[] = { | ||
1861 | 2, 3, 1, 0 | ||
1862 | }; | ||
1863 | return queue_mapping[queue_id]; | ||
1864 | } | ||
1865 | |||
1866 | static inline u8 wsm_queue_id_to_wsm(u8 queue_id) | ||
1867 | { | ||
1868 | static const u8 queue_mapping[] = { | ||
1869 | 3, 2, 0, 1 | ||
1870 | }; | ||
1871 | return queue_mapping[queue_id]; | ||
1872 | } | ||
1873 | |||
1874 | |||
1875 | #ifdef CONFIG_CW1200_ETF | ||
1876 | int wsm_raw_cmd(struct cw1200_common *priv, u8 *data, size_t len); | ||
1877 | #endif | ||
1878 | |||
1879 | #endif /* CW1200_HWIO_H_INCLUDED */ | ||