1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * WSM host interface (HI) implementation for
4*4882a593Smuzhiyun * ST-Ericsson CW1200 mac80211 drivers.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Copyright (c) 2010, ST-Ericsson
7*4882a593Smuzhiyun * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <linux/skbuff.h>
11*4882a593Smuzhiyun #include <linux/wait.h>
12*4882a593Smuzhiyun #include <linux/delay.h>
13*4882a593Smuzhiyun #include <linux/sched.h>
14*4882a593Smuzhiyun #include <linux/random.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include "cw1200.h"
17*4882a593Smuzhiyun #include "wsm.h"
18*4882a593Smuzhiyun #include "bh.h"
19*4882a593Smuzhiyun #include "sta.h"
20*4882a593Smuzhiyun #include "debug.h"
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */
23*4882a593Smuzhiyun #define WSM_CMD_START_TIMEOUT (7 * HZ)
24*4882a593Smuzhiyun #define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */
25*4882a593Smuzhiyun #define WSM_CMD_MAX_TIMEOUT (3 * HZ)
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun #define WSM_SKIP(buf, size) \
28*4882a593Smuzhiyun do { \
29*4882a593Smuzhiyun if ((buf)->data + size > (buf)->end) \
30*4882a593Smuzhiyun goto underflow; \
31*4882a593Smuzhiyun (buf)->data += size; \
32*4882a593Smuzhiyun } while (0)
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun #define WSM_GET(buf, ptr, size) \
35*4882a593Smuzhiyun do { \
36*4882a593Smuzhiyun if ((buf)->data + size > (buf)->end) \
37*4882a593Smuzhiyun goto underflow; \
38*4882a593Smuzhiyun memcpy(ptr, (buf)->data, size); \
39*4882a593Smuzhiyun (buf)->data += size; \
40*4882a593Smuzhiyun } while (0)
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun #define __WSM_GET(buf, type, type2, cvt) \
43*4882a593Smuzhiyun ({ \
44*4882a593Smuzhiyun type val; \
45*4882a593Smuzhiyun if ((buf)->data + sizeof(type) > (buf)->end) \
46*4882a593Smuzhiyun goto underflow; \
47*4882a593Smuzhiyun val = cvt(*(type2 *)(buf)->data); \
48*4882a593Smuzhiyun (buf)->data += sizeof(type); \
49*4882a593Smuzhiyun val; \
50*4882a593Smuzhiyun })
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun #define WSM_GET8(buf) __WSM_GET(buf, u8, u8, (u8))
53*4882a593Smuzhiyun #define WSM_GET16(buf) __WSM_GET(buf, u16, __le16, __le16_to_cpu)
54*4882a593Smuzhiyun #define WSM_GET32(buf) __WSM_GET(buf, u32, __le32, __le32_to_cpu)
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun #define WSM_PUT(buf, ptr, size) \
57*4882a593Smuzhiyun do { \
58*4882a593Smuzhiyun if ((buf)->data + size > (buf)->end) \
59*4882a593Smuzhiyun if (wsm_buf_reserve((buf), size)) \
60*4882a593Smuzhiyun goto nomem; \
61*4882a593Smuzhiyun memcpy((buf)->data, ptr, size); \
62*4882a593Smuzhiyun (buf)->data += size; \
63*4882a593Smuzhiyun } while (0)
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun #define __WSM_PUT(buf, val, type, type2, cvt) \
66*4882a593Smuzhiyun do { \
67*4882a593Smuzhiyun if ((buf)->data + sizeof(type) > (buf)->end) \
68*4882a593Smuzhiyun if (wsm_buf_reserve((buf), sizeof(type))) \
69*4882a593Smuzhiyun goto nomem; \
70*4882a593Smuzhiyun *(type2 *)(buf)->data = cvt(val); \
71*4882a593Smuzhiyun (buf)->data += sizeof(type); \
72*4882a593Smuzhiyun } while (0)
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun #define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, u8, (u8))
75*4882a593Smuzhiyun #define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __le16, __cpu_to_le16)
76*4882a593Smuzhiyun #define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __le32, __cpu_to_le32)
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun static void wsm_buf_reset(struct wsm_buf *buf);
79*4882a593Smuzhiyun static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size);
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun static int wsm_cmd_send(struct cw1200_common *priv,
82*4882a593Smuzhiyun struct wsm_buf *buf,
83*4882a593Smuzhiyun void *arg, u16 cmd, long tmo);
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun #define wsm_cmd_lock(__priv) mutex_lock(&((__priv)->wsm_cmd_mux))
86*4882a593Smuzhiyun #define wsm_cmd_unlock(__priv) mutex_unlock(&((__priv)->wsm_cmd_mux))
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun /* ******************************************************************** */
89*4882a593Smuzhiyun /* WSM API implementation */
90*4882a593Smuzhiyun
wsm_generic_confirm(struct cw1200_common * priv,void * arg,struct wsm_buf * buf)91*4882a593Smuzhiyun static int wsm_generic_confirm(struct cw1200_common *priv,
92*4882a593Smuzhiyun void *arg,
93*4882a593Smuzhiyun struct wsm_buf *buf)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun u32 status = WSM_GET32(buf);
96*4882a593Smuzhiyun if (status != WSM_STATUS_SUCCESS)
97*4882a593Smuzhiyun return -EINVAL;
98*4882a593Smuzhiyun return 0;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun underflow:
101*4882a593Smuzhiyun WARN_ON(1);
102*4882a593Smuzhiyun return -EINVAL;
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun
wsm_configuration(struct cw1200_common * priv,struct wsm_configuration * arg)105*4882a593Smuzhiyun int wsm_configuration(struct cw1200_common *priv, struct wsm_configuration *arg)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun int ret;
108*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun wsm_cmd_lock(priv);
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime);
113*4882a593Smuzhiyun WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime);
114*4882a593Smuzhiyun WSM_PUT32(buf, arg->dot11RtsThreshold);
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun /* DPD block. */
117*4882a593Smuzhiyun WSM_PUT16(buf, arg->dpdData_size + 12);
118*4882a593Smuzhiyun WSM_PUT16(buf, 1); /* DPD version */
119*4882a593Smuzhiyun WSM_PUT(buf, arg->dot11StationId, ETH_ALEN);
120*4882a593Smuzhiyun WSM_PUT16(buf, 5); /* DPD flags */
121*4882a593Smuzhiyun WSM_PUT(buf, arg->dpdData, arg->dpdData_size);
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, arg,
124*4882a593Smuzhiyun WSM_CONFIGURATION_REQ_ID, WSM_CMD_TIMEOUT);
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun wsm_cmd_unlock(priv);
127*4882a593Smuzhiyun return ret;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun nomem:
130*4882a593Smuzhiyun wsm_cmd_unlock(priv);
131*4882a593Smuzhiyun return -ENOMEM;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun
wsm_configuration_confirm(struct cw1200_common * priv,struct wsm_configuration * arg,struct wsm_buf * buf)134*4882a593Smuzhiyun static int wsm_configuration_confirm(struct cw1200_common *priv,
135*4882a593Smuzhiyun struct wsm_configuration *arg,
136*4882a593Smuzhiyun struct wsm_buf *buf)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun int i;
139*4882a593Smuzhiyun int status;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun status = WSM_GET32(buf);
142*4882a593Smuzhiyun if (WARN_ON(status != WSM_STATUS_SUCCESS))
143*4882a593Smuzhiyun return -EINVAL;
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun WSM_GET(buf, arg->dot11StationId, ETH_ALEN);
146*4882a593Smuzhiyun arg->dot11FrequencyBandsSupported = WSM_GET8(buf);
147*4882a593Smuzhiyun WSM_SKIP(buf, 1);
148*4882a593Smuzhiyun arg->supportedRateMask = WSM_GET32(buf);
149*4882a593Smuzhiyun for (i = 0; i < 2; ++i) {
150*4882a593Smuzhiyun arg->txPowerRange[i].min_power_level = WSM_GET32(buf);
151*4882a593Smuzhiyun arg->txPowerRange[i].max_power_level = WSM_GET32(buf);
152*4882a593Smuzhiyun arg->txPowerRange[i].stepping = WSM_GET32(buf);
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun return 0;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun underflow:
157*4882a593Smuzhiyun WARN_ON(1);
158*4882a593Smuzhiyun return -EINVAL;
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun /* ******************************************************************** */
162*4882a593Smuzhiyun
wsm_reset(struct cw1200_common * priv,const struct wsm_reset * arg)163*4882a593Smuzhiyun int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun int ret;
166*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
167*4882a593Smuzhiyun u16 cmd = WSM_RESET_REQ_ID | WSM_TX_LINK_ID(arg->link_id);
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun wsm_cmd_lock(priv);
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun WSM_PUT32(buf, arg->reset_statistics ? 0 : 1);
172*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT);
173*4882a593Smuzhiyun wsm_cmd_unlock(priv);
174*4882a593Smuzhiyun return ret;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun nomem:
177*4882a593Smuzhiyun wsm_cmd_unlock(priv);
178*4882a593Smuzhiyun return -ENOMEM;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun /* ******************************************************************** */
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun struct wsm_mib {
184*4882a593Smuzhiyun u16 mib_id;
185*4882a593Smuzhiyun void *buf;
186*4882a593Smuzhiyun size_t buf_size;
187*4882a593Smuzhiyun };
188*4882a593Smuzhiyun
wsm_read_mib(struct cw1200_common * priv,u16 mib_id,void * _buf,size_t buf_size)189*4882a593Smuzhiyun int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *_buf,
190*4882a593Smuzhiyun size_t buf_size)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun int ret;
193*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
194*4882a593Smuzhiyun struct wsm_mib mib_buf = {
195*4882a593Smuzhiyun .mib_id = mib_id,
196*4882a593Smuzhiyun .buf = _buf,
197*4882a593Smuzhiyun .buf_size = buf_size,
198*4882a593Smuzhiyun };
199*4882a593Smuzhiyun wsm_cmd_lock(priv);
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun WSM_PUT16(buf, mib_id);
202*4882a593Smuzhiyun WSM_PUT16(buf, 0);
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, &mib_buf,
205*4882a593Smuzhiyun WSM_READ_MIB_REQ_ID, WSM_CMD_TIMEOUT);
206*4882a593Smuzhiyun wsm_cmd_unlock(priv);
207*4882a593Smuzhiyun return ret;
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun nomem:
210*4882a593Smuzhiyun wsm_cmd_unlock(priv);
211*4882a593Smuzhiyun return -ENOMEM;
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun
wsm_read_mib_confirm(struct cw1200_common * priv,struct wsm_mib * arg,struct wsm_buf * buf)214*4882a593Smuzhiyun static int wsm_read_mib_confirm(struct cw1200_common *priv,
215*4882a593Smuzhiyun struct wsm_mib *arg,
216*4882a593Smuzhiyun struct wsm_buf *buf)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun u16 size;
219*4882a593Smuzhiyun if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS))
220*4882a593Smuzhiyun return -EINVAL;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun if (WARN_ON(WSM_GET16(buf) != arg->mib_id))
223*4882a593Smuzhiyun return -EINVAL;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun size = WSM_GET16(buf);
226*4882a593Smuzhiyun if (size > arg->buf_size)
227*4882a593Smuzhiyun size = arg->buf_size;
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun WSM_GET(buf, arg->buf, size);
230*4882a593Smuzhiyun arg->buf_size = size;
231*4882a593Smuzhiyun return 0;
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun underflow:
234*4882a593Smuzhiyun WARN_ON(1);
235*4882a593Smuzhiyun return -EINVAL;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun /* ******************************************************************** */
239*4882a593Smuzhiyun
wsm_write_mib(struct cw1200_common * priv,u16 mib_id,void * _buf,size_t buf_size)240*4882a593Smuzhiyun int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *_buf,
241*4882a593Smuzhiyun size_t buf_size)
242*4882a593Smuzhiyun {
243*4882a593Smuzhiyun int ret;
244*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
245*4882a593Smuzhiyun struct wsm_mib mib_buf = {
246*4882a593Smuzhiyun .mib_id = mib_id,
247*4882a593Smuzhiyun .buf = _buf,
248*4882a593Smuzhiyun .buf_size = buf_size,
249*4882a593Smuzhiyun };
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun wsm_cmd_lock(priv);
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun WSM_PUT16(buf, mib_id);
254*4882a593Smuzhiyun WSM_PUT16(buf, buf_size);
255*4882a593Smuzhiyun WSM_PUT(buf, _buf, buf_size);
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, &mib_buf,
258*4882a593Smuzhiyun WSM_WRITE_MIB_REQ_ID, WSM_CMD_TIMEOUT);
259*4882a593Smuzhiyun wsm_cmd_unlock(priv);
260*4882a593Smuzhiyun return ret;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun nomem:
263*4882a593Smuzhiyun wsm_cmd_unlock(priv);
264*4882a593Smuzhiyun return -ENOMEM;
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun
wsm_write_mib_confirm(struct cw1200_common * priv,struct wsm_mib * arg,struct wsm_buf * buf)267*4882a593Smuzhiyun static int wsm_write_mib_confirm(struct cw1200_common *priv,
268*4882a593Smuzhiyun struct wsm_mib *arg,
269*4882a593Smuzhiyun struct wsm_buf *buf)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun int ret;
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun ret = wsm_generic_confirm(priv, arg, buf);
274*4882a593Smuzhiyun if (ret)
275*4882a593Smuzhiyun return ret;
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun if (arg->mib_id == WSM_MIB_ID_OPERATIONAL_POWER_MODE) {
278*4882a593Smuzhiyun /* OperationalMode: update PM status. */
279*4882a593Smuzhiyun const char *p = arg->buf;
280*4882a593Smuzhiyun cw1200_enable_powersave(priv, (p[0] & 0x0F) ? true : false);
281*4882a593Smuzhiyun }
282*4882a593Smuzhiyun return 0;
283*4882a593Smuzhiyun }
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun /* ******************************************************************** */
286*4882a593Smuzhiyun
wsm_scan(struct cw1200_common * priv,const struct wsm_scan * arg)287*4882a593Smuzhiyun int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg)
288*4882a593Smuzhiyun {
289*4882a593Smuzhiyun int i;
290*4882a593Smuzhiyun int ret;
291*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun if (arg->num_channels > 48)
294*4882a593Smuzhiyun return -EINVAL;
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun if (arg->num_ssids > 2)
297*4882a593Smuzhiyun return -EINVAL;
298*4882a593Smuzhiyun
299*4882a593Smuzhiyun if (arg->band > 1)
300*4882a593Smuzhiyun return -EINVAL;
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun wsm_cmd_lock(priv);
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun WSM_PUT8(buf, arg->band);
305*4882a593Smuzhiyun WSM_PUT8(buf, arg->type);
306*4882a593Smuzhiyun WSM_PUT8(buf, arg->flags);
307*4882a593Smuzhiyun WSM_PUT8(buf, arg->max_tx_rate);
308*4882a593Smuzhiyun WSM_PUT32(buf, arg->auto_scan_interval);
309*4882a593Smuzhiyun WSM_PUT8(buf, arg->num_probes);
310*4882a593Smuzhiyun WSM_PUT8(buf, arg->num_channels);
311*4882a593Smuzhiyun WSM_PUT8(buf, arg->num_ssids);
312*4882a593Smuzhiyun WSM_PUT8(buf, arg->probe_delay);
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun for (i = 0; i < arg->num_channels; ++i) {
315*4882a593Smuzhiyun WSM_PUT16(buf, arg->ch[i].number);
316*4882a593Smuzhiyun WSM_PUT16(buf, 0);
317*4882a593Smuzhiyun WSM_PUT32(buf, arg->ch[i].min_chan_time);
318*4882a593Smuzhiyun WSM_PUT32(buf, arg->ch[i].max_chan_time);
319*4882a593Smuzhiyun WSM_PUT32(buf, 0);
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun for (i = 0; i < arg->num_ssids; ++i) {
323*4882a593Smuzhiyun WSM_PUT32(buf, arg->ssids[i].length);
324*4882a593Smuzhiyun WSM_PUT(buf, &arg->ssids[i].ssid[0],
325*4882a593Smuzhiyun sizeof(arg->ssids[i].ssid));
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL,
329*4882a593Smuzhiyun WSM_START_SCAN_REQ_ID, WSM_CMD_TIMEOUT);
330*4882a593Smuzhiyun wsm_cmd_unlock(priv);
331*4882a593Smuzhiyun return ret;
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun nomem:
334*4882a593Smuzhiyun wsm_cmd_unlock(priv);
335*4882a593Smuzhiyun return -ENOMEM;
336*4882a593Smuzhiyun }
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun /* ******************************************************************** */
339*4882a593Smuzhiyun
wsm_stop_scan(struct cw1200_common * priv)340*4882a593Smuzhiyun int wsm_stop_scan(struct cw1200_common *priv)
341*4882a593Smuzhiyun {
342*4882a593Smuzhiyun int ret;
343*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
344*4882a593Smuzhiyun wsm_cmd_lock(priv);
345*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL,
346*4882a593Smuzhiyun WSM_STOP_SCAN_REQ_ID, WSM_CMD_TIMEOUT);
347*4882a593Smuzhiyun wsm_cmd_unlock(priv);
348*4882a593Smuzhiyun return ret;
349*4882a593Smuzhiyun }
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun
wsm_tx_confirm(struct cw1200_common * priv,struct wsm_buf * buf,int link_id)352*4882a593Smuzhiyun static int wsm_tx_confirm(struct cw1200_common *priv,
353*4882a593Smuzhiyun struct wsm_buf *buf,
354*4882a593Smuzhiyun int link_id)
355*4882a593Smuzhiyun {
356*4882a593Smuzhiyun struct wsm_tx_confirm tx_confirm;
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun tx_confirm.packet_id = WSM_GET32(buf);
359*4882a593Smuzhiyun tx_confirm.status = WSM_GET32(buf);
360*4882a593Smuzhiyun tx_confirm.tx_rate = WSM_GET8(buf);
361*4882a593Smuzhiyun tx_confirm.ack_failures = WSM_GET8(buf);
362*4882a593Smuzhiyun tx_confirm.flags = WSM_GET16(buf);
363*4882a593Smuzhiyun tx_confirm.media_delay = WSM_GET32(buf);
364*4882a593Smuzhiyun tx_confirm.tx_queue_delay = WSM_GET32(buf);
365*4882a593Smuzhiyun
366*4882a593Smuzhiyun cw1200_tx_confirm_cb(priv, link_id, &tx_confirm);
367*4882a593Smuzhiyun return 0;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun underflow:
370*4882a593Smuzhiyun WARN_ON(1);
371*4882a593Smuzhiyun return -EINVAL;
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun
wsm_multi_tx_confirm(struct cw1200_common * priv,struct wsm_buf * buf,int link_id)374*4882a593Smuzhiyun static int wsm_multi_tx_confirm(struct cw1200_common *priv,
375*4882a593Smuzhiyun struct wsm_buf *buf, int link_id)
376*4882a593Smuzhiyun {
377*4882a593Smuzhiyun int ret;
378*4882a593Smuzhiyun int count;
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun count = WSM_GET32(buf);
381*4882a593Smuzhiyun if (WARN_ON(count <= 0))
382*4882a593Smuzhiyun return -EINVAL;
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun if (count > 1) {
385*4882a593Smuzhiyun /* We already released one buffer, now for the rest */
386*4882a593Smuzhiyun ret = wsm_release_tx_buffer(priv, count - 1);
387*4882a593Smuzhiyun if (ret < 0)
388*4882a593Smuzhiyun return ret;
389*4882a593Smuzhiyun else if (ret > 0)
390*4882a593Smuzhiyun cw1200_bh_wakeup(priv);
391*4882a593Smuzhiyun }
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun cw1200_debug_txed_multi(priv, count);
394*4882a593Smuzhiyun do {
395*4882a593Smuzhiyun ret = wsm_tx_confirm(priv, buf, link_id);
396*4882a593Smuzhiyun } while (!ret && --count);
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun return ret;
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun underflow:
401*4882a593Smuzhiyun WARN_ON(1);
402*4882a593Smuzhiyun return -EINVAL;
403*4882a593Smuzhiyun }
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun /* ******************************************************************** */
406*4882a593Smuzhiyun
wsm_join_confirm(struct cw1200_common * priv,struct wsm_join_cnf * arg,struct wsm_buf * buf)407*4882a593Smuzhiyun static int wsm_join_confirm(struct cw1200_common *priv,
408*4882a593Smuzhiyun struct wsm_join_cnf *arg,
409*4882a593Smuzhiyun struct wsm_buf *buf)
410*4882a593Smuzhiyun {
411*4882a593Smuzhiyun arg->status = WSM_GET32(buf);
412*4882a593Smuzhiyun if (WARN_ON(arg->status) != WSM_STATUS_SUCCESS)
413*4882a593Smuzhiyun return -EINVAL;
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun arg->min_power_level = WSM_GET32(buf);
416*4882a593Smuzhiyun arg->max_power_level = WSM_GET32(buf);
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun return 0;
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun underflow:
421*4882a593Smuzhiyun WARN_ON(1);
422*4882a593Smuzhiyun return -EINVAL;
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun
wsm_join(struct cw1200_common * priv,struct wsm_join * arg)425*4882a593Smuzhiyun int wsm_join(struct cw1200_common *priv, struct wsm_join *arg)
426*4882a593Smuzhiyun {
427*4882a593Smuzhiyun int ret;
428*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
429*4882a593Smuzhiyun struct wsm_join_cnf resp;
430*4882a593Smuzhiyun wsm_cmd_lock(priv);
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun WSM_PUT8(buf, arg->mode);
433*4882a593Smuzhiyun WSM_PUT8(buf, arg->band);
434*4882a593Smuzhiyun WSM_PUT16(buf, arg->channel_number);
435*4882a593Smuzhiyun WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid));
436*4882a593Smuzhiyun WSM_PUT16(buf, arg->atim_window);
437*4882a593Smuzhiyun WSM_PUT8(buf, arg->preamble_type);
438*4882a593Smuzhiyun WSM_PUT8(buf, arg->probe_for_join);
439*4882a593Smuzhiyun WSM_PUT8(buf, arg->dtim_period);
440*4882a593Smuzhiyun WSM_PUT8(buf, arg->flags);
441*4882a593Smuzhiyun WSM_PUT32(buf, arg->ssid_len);
442*4882a593Smuzhiyun WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid));
443*4882a593Smuzhiyun WSM_PUT32(buf, arg->beacon_interval);
444*4882a593Smuzhiyun WSM_PUT32(buf, arg->basic_rate_set);
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun priv->tx_burst_idx = -1;
447*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, &resp,
448*4882a593Smuzhiyun WSM_JOIN_REQ_ID, WSM_CMD_TIMEOUT);
449*4882a593Smuzhiyun /* TODO: Update state based on resp.min|max_power_level */
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun priv->join_complete_status = resp.status;
452*4882a593Smuzhiyun
453*4882a593Smuzhiyun wsm_cmd_unlock(priv);
454*4882a593Smuzhiyun return ret;
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun nomem:
457*4882a593Smuzhiyun wsm_cmd_unlock(priv);
458*4882a593Smuzhiyun return -ENOMEM;
459*4882a593Smuzhiyun }
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun /* ******************************************************************** */
462*4882a593Smuzhiyun
wsm_set_bss_params(struct cw1200_common * priv,const struct wsm_set_bss_params * arg)463*4882a593Smuzhiyun int wsm_set_bss_params(struct cw1200_common *priv,
464*4882a593Smuzhiyun const struct wsm_set_bss_params *arg)
465*4882a593Smuzhiyun {
466*4882a593Smuzhiyun int ret;
467*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun wsm_cmd_lock(priv);
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun WSM_PUT8(buf, (arg->reset_beacon_loss ? 0x1 : 0));
472*4882a593Smuzhiyun WSM_PUT8(buf, arg->beacon_lost_count);
473*4882a593Smuzhiyun WSM_PUT16(buf, arg->aid);
474*4882a593Smuzhiyun WSM_PUT32(buf, arg->operational_rate_set);
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL,
477*4882a593Smuzhiyun WSM_SET_BSS_PARAMS_REQ_ID, WSM_CMD_TIMEOUT);
478*4882a593Smuzhiyun
479*4882a593Smuzhiyun wsm_cmd_unlock(priv);
480*4882a593Smuzhiyun return ret;
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun nomem:
483*4882a593Smuzhiyun wsm_cmd_unlock(priv);
484*4882a593Smuzhiyun return -ENOMEM;
485*4882a593Smuzhiyun }
486*4882a593Smuzhiyun
487*4882a593Smuzhiyun /* ******************************************************************** */
488*4882a593Smuzhiyun
wsm_add_key(struct cw1200_common * priv,const struct wsm_add_key * arg)489*4882a593Smuzhiyun int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg)
490*4882a593Smuzhiyun {
491*4882a593Smuzhiyun int ret;
492*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun wsm_cmd_lock(priv);
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun WSM_PUT(buf, arg, sizeof(*arg));
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL,
499*4882a593Smuzhiyun WSM_ADD_KEY_REQ_ID, WSM_CMD_TIMEOUT);
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun wsm_cmd_unlock(priv);
502*4882a593Smuzhiyun return ret;
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun nomem:
505*4882a593Smuzhiyun wsm_cmd_unlock(priv);
506*4882a593Smuzhiyun return -ENOMEM;
507*4882a593Smuzhiyun }
508*4882a593Smuzhiyun
509*4882a593Smuzhiyun /* ******************************************************************** */
510*4882a593Smuzhiyun
wsm_remove_key(struct cw1200_common * priv,const struct wsm_remove_key * arg)511*4882a593Smuzhiyun int wsm_remove_key(struct cw1200_common *priv, const struct wsm_remove_key *arg)
512*4882a593Smuzhiyun {
513*4882a593Smuzhiyun int ret;
514*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun wsm_cmd_lock(priv);
517*4882a593Smuzhiyun
518*4882a593Smuzhiyun WSM_PUT8(buf, arg->index);
519*4882a593Smuzhiyun WSM_PUT8(buf, 0);
520*4882a593Smuzhiyun WSM_PUT16(buf, 0);
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL,
523*4882a593Smuzhiyun WSM_REMOVE_KEY_REQ_ID, WSM_CMD_TIMEOUT);
524*4882a593Smuzhiyun
525*4882a593Smuzhiyun wsm_cmd_unlock(priv);
526*4882a593Smuzhiyun return ret;
527*4882a593Smuzhiyun
528*4882a593Smuzhiyun nomem:
529*4882a593Smuzhiyun wsm_cmd_unlock(priv);
530*4882a593Smuzhiyun return -ENOMEM;
531*4882a593Smuzhiyun }
532*4882a593Smuzhiyun
533*4882a593Smuzhiyun /* ******************************************************************** */
534*4882a593Smuzhiyun
wsm_set_tx_queue_params(struct cw1200_common * priv,const struct wsm_set_tx_queue_params * arg,u8 id)535*4882a593Smuzhiyun int wsm_set_tx_queue_params(struct cw1200_common *priv,
536*4882a593Smuzhiyun const struct wsm_set_tx_queue_params *arg, u8 id)
537*4882a593Smuzhiyun {
538*4882a593Smuzhiyun int ret;
539*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
540*4882a593Smuzhiyun u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1};
541*4882a593Smuzhiyun
542*4882a593Smuzhiyun wsm_cmd_lock(priv);
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun WSM_PUT8(buf, queue_id_to_wmm_aci[id]);
545*4882a593Smuzhiyun WSM_PUT8(buf, 0);
546*4882a593Smuzhiyun WSM_PUT8(buf, arg->ackPolicy);
547*4882a593Smuzhiyun WSM_PUT8(buf, 0);
548*4882a593Smuzhiyun WSM_PUT32(buf, arg->maxTransmitLifetime);
549*4882a593Smuzhiyun WSM_PUT16(buf, arg->allowedMediumTime);
550*4882a593Smuzhiyun WSM_PUT16(buf, 0);
551*4882a593Smuzhiyun
552*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT);
553*4882a593Smuzhiyun
554*4882a593Smuzhiyun wsm_cmd_unlock(priv);
555*4882a593Smuzhiyun return ret;
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun nomem:
558*4882a593Smuzhiyun wsm_cmd_unlock(priv);
559*4882a593Smuzhiyun return -ENOMEM;
560*4882a593Smuzhiyun }
561*4882a593Smuzhiyun
562*4882a593Smuzhiyun /* ******************************************************************** */
563*4882a593Smuzhiyun
wsm_set_edca_params(struct cw1200_common * priv,const struct wsm_edca_params * arg)564*4882a593Smuzhiyun int wsm_set_edca_params(struct cw1200_common *priv,
565*4882a593Smuzhiyun const struct wsm_edca_params *arg)
566*4882a593Smuzhiyun {
567*4882a593Smuzhiyun int ret;
568*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
569*4882a593Smuzhiyun
570*4882a593Smuzhiyun wsm_cmd_lock(priv);
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun /* Implemented according to specification. */
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun WSM_PUT16(buf, arg->params[3].cwmin);
575*4882a593Smuzhiyun WSM_PUT16(buf, arg->params[2].cwmin);
576*4882a593Smuzhiyun WSM_PUT16(buf, arg->params[1].cwmin);
577*4882a593Smuzhiyun WSM_PUT16(buf, arg->params[0].cwmin);
578*4882a593Smuzhiyun
579*4882a593Smuzhiyun WSM_PUT16(buf, arg->params[3].cwmax);
580*4882a593Smuzhiyun WSM_PUT16(buf, arg->params[2].cwmax);
581*4882a593Smuzhiyun WSM_PUT16(buf, arg->params[1].cwmax);
582*4882a593Smuzhiyun WSM_PUT16(buf, arg->params[0].cwmax);
583*4882a593Smuzhiyun
584*4882a593Smuzhiyun WSM_PUT8(buf, arg->params[3].aifns);
585*4882a593Smuzhiyun WSM_PUT8(buf, arg->params[2].aifns);
586*4882a593Smuzhiyun WSM_PUT8(buf, arg->params[1].aifns);
587*4882a593Smuzhiyun WSM_PUT8(buf, arg->params[0].aifns);
588*4882a593Smuzhiyun
589*4882a593Smuzhiyun WSM_PUT16(buf, arg->params[3].txop_limit);
590*4882a593Smuzhiyun WSM_PUT16(buf, arg->params[2].txop_limit);
591*4882a593Smuzhiyun WSM_PUT16(buf, arg->params[1].txop_limit);
592*4882a593Smuzhiyun WSM_PUT16(buf, arg->params[0].txop_limit);
593*4882a593Smuzhiyun
594*4882a593Smuzhiyun WSM_PUT32(buf, arg->params[3].max_rx_lifetime);
595*4882a593Smuzhiyun WSM_PUT32(buf, arg->params[2].max_rx_lifetime);
596*4882a593Smuzhiyun WSM_PUT32(buf, arg->params[1].max_rx_lifetime);
597*4882a593Smuzhiyun WSM_PUT32(buf, arg->params[0].max_rx_lifetime);
598*4882a593Smuzhiyun
599*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL,
600*4882a593Smuzhiyun WSM_EDCA_PARAMS_REQ_ID, WSM_CMD_TIMEOUT);
601*4882a593Smuzhiyun wsm_cmd_unlock(priv);
602*4882a593Smuzhiyun return ret;
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun nomem:
605*4882a593Smuzhiyun wsm_cmd_unlock(priv);
606*4882a593Smuzhiyun return -ENOMEM;
607*4882a593Smuzhiyun }
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun /* ******************************************************************** */
610*4882a593Smuzhiyun
wsm_switch_channel(struct cw1200_common * priv,const struct wsm_switch_channel * arg)611*4882a593Smuzhiyun int wsm_switch_channel(struct cw1200_common *priv,
612*4882a593Smuzhiyun const struct wsm_switch_channel *arg)
613*4882a593Smuzhiyun {
614*4882a593Smuzhiyun int ret;
615*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
616*4882a593Smuzhiyun
617*4882a593Smuzhiyun wsm_cmd_lock(priv);
618*4882a593Smuzhiyun
619*4882a593Smuzhiyun WSM_PUT8(buf, arg->mode);
620*4882a593Smuzhiyun WSM_PUT8(buf, arg->switch_count);
621*4882a593Smuzhiyun WSM_PUT16(buf, arg->channel_number);
622*4882a593Smuzhiyun
623*4882a593Smuzhiyun priv->channel_switch_in_progress = 1;
624*4882a593Smuzhiyun
625*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL,
626*4882a593Smuzhiyun WSM_SWITCH_CHANNEL_REQ_ID, WSM_CMD_TIMEOUT);
627*4882a593Smuzhiyun if (ret)
628*4882a593Smuzhiyun priv->channel_switch_in_progress = 0;
629*4882a593Smuzhiyun
630*4882a593Smuzhiyun wsm_cmd_unlock(priv);
631*4882a593Smuzhiyun return ret;
632*4882a593Smuzhiyun
633*4882a593Smuzhiyun nomem:
634*4882a593Smuzhiyun wsm_cmd_unlock(priv);
635*4882a593Smuzhiyun return -ENOMEM;
636*4882a593Smuzhiyun }
637*4882a593Smuzhiyun
638*4882a593Smuzhiyun /* ******************************************************************** */
639*4882a593Smuzhiyun
wsm_set_pm(struct cw1200_common * priv,const struct wsm_set_pm * arg)640*4882a593Smuzhiyun int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg)
641*4882a593Smuzhiyun {
642*4882a593Smuzhiyun int ret;
643*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
644*4882a593Smuzhiyun priv->ps_mode_switch_in_progress = 1;
645*4882a593Smuzhiyun
646*4882a593Smuzhiyun wsm_cmd_lock(priv);
647*4882a593Smuzhiyun
648*4882a593Smuzhiyun WSM_PUT8(buf, arg->mode);
649*4882a593Smuzhiyun WSM_PUT8(buf, arg->fast_psm_idle_period);
650*4882a593Smuzhiyun WSM_PUT8(buf, arg->ap_psm_change_period);
651*4882a593Smuzhiyun WSM_PUT8(buf, arg->min_auto_pspoll_period);
652*4882a593Smuzhiyun
653*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL,
654*4882a593Smuzhiyun WSM_SET_PM_REQ_ID, WSM_CMD_TIMEOUT);
655*4882a593Smuzhiyun
656*4882a593Smuzhiyun wsm_cmd_unlock(priv);
657*4882a593Smuzhiyun return ret;
658*4882a593Smuzhiyun
659*4882a593Smuzhiyun nomem:
660*4882a593Smuzhiyun wsm_cmd_unlock(priv);
661*4882a593Smuzhiyun return -ENOMEM;
662*4882a593Smuzhiyun }
663*4882a593Smuzhiyun
664*4882a593Smuzhiyun /* ******************************************************************** */
665*4882a593Smuzhiyun
wsm_start(struct cw1200_common * priv,const struct wsm_start * arg)666*4882a593Smuzhiyun int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg)
667*4882a593Smuzhiyun {
668*4882a593Smuzhiyun int ret;
669*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
670*4882a593Smuzhiyun
671*4882a593Smuzhiyun wsm_cmd_lock(priv);
672*4882a593Smuzhiyun
673*4882a593Smuzhiyun WSM_PUT8(buf, arg->mode);
674*4882a593Smuzhiyun WSM_PUT8(buf, arg->band);
675*4882a593Smuzhiyun WSM_PUT16(buf, arg->channel_number);
676*4882a593Smuzhiyun WSM_PUT32(buf, arg->ct_window);
677*4882a593Smuzhiyun WSM_PUT32(buf, arg->beacon_interval);
678*4882a593Smuzhiyun WSM_PUT8(buf, arg->dtim_period);
679*4882a593Smuzhiyun WSM_PUT8(buf, arg->preamble);
680*4882a593Smuzhiyun WSM_PUT8(buf, arg->probe_delay);
681*4882a593Smuzhiyun WSM_PUT8(buf, arg->ssid_len);
682*4882a593Smuzhiyun WSM_PUT(buf, arg->ssid, sizeof(arg->ssid));
683*4882a593Smuzhiyun WSM_PUT32(buf, arg->basic_rate_set);
684*4882a593Smuzhiyun
685*4882a593Smuzhiyun priv->tx_burst_idx = -1;
686*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL,
687*4882a593Smuzhiyun WSM_START_REQ_ID, WSM_CMD_START_TIMEOUT);
688*4882a593Smuzhiyun
689*4882a593Smuzhiyun wsm_cmd_unlock(priv);
690*4882a593Smuzhiyun return ret;
691*4882a593Smuzhiyun
692*4882a593Smuzhiyun nomem:
693*4882a593Smuzhiyun wsm_cmd_unlock(priv);
694*4882a593Smuzhiyun return -ENOMEM;
695*4882a593Smuzhiyun }
696*4882a593Smuzhiyun
697*4882a593Smuzhiyun /* ******************************************************************** */
698*4882a593Smuzhiyun
wsm_beacon_transmit(struct cw1200_common * priv,const struct wsm_beacon_transmit * arg)699*4882a593Smuzhiyun int wsm_beacon_transmit(struct cw1200_common *priv,
700*4882a593Smuzhiyun const struct wsm_beacon_transmit *arg)
701*4882a593Smuzhiyun {
702*4882a593Smuzhiyun int ret;
703*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
704*4882a593Smuzhiyun
705*4882a593Smuzhiyun wsm_cmd_lock(priv);
706*4882a593Smuzhiyun
707*4882a593Smuzhiyun WSM_PUT32(buf, arg->enable_beaconing ? 1 : 0);
708*4882a593Smuzhiyun
709*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL,
710*4882a593Smuzhiyun WSM_BEACON_TRANSMIT_REQ_ID, WSM_CMD_TIMEOUT);
711*4882a593Smuzhiyun
712*4882a593Smuzhiyun wsm_cmd_unlock(priv);
713*4882a593Smuzhiyun return ret;
714*4882a593Smuzhiyun
715*4882a593Smuzhiyun nomem:
716*4882a593Smuzhiyun wsm_cmd_unlock(priv);
717*4882a593Smuzhiyun return -ENOMEM;
718*4882a593Smuzhiyun }
719*4882a593Smuzhiyun
720*4882a593Smuzhiyun /* ******************************************************************** */
721*4882a593Smuzhiyun
wsm_start_find(struct cw1200_common * priv)722*4882a593Smuzhiyun int wsm_start_find(struct cw1200_common *priv)
723*4882a593Smuzhiyun {
724*4882a593Smuzhiyun int ret;
725*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
726*4882a593Smuzhiyun
727*4882a593Smuzhiyun wsm_cmd_lock(priv);
728*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT);
729*4882a593Smuzhiyun wsm_cmd_unlock(priv);
730*4882a593Smuzhiyun return ret;
731*4882a593Smuzhiyun }
732*4882a593Smuzhiyun
733*4882a593Smuzhiyun /* ******************************************************************** */
734*4882a593Smuzhiyun
wsm_stop_find(struct cw1200_common * priv)735*4882a593Smuzhiyun int wsm_stop_find(struct cw1200_common *priv)
736*4882a593Smuzhiyun {
737*4882a593Smuzhiyun int ret;
738*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
739*4882a593Smuzhiyun
740*4882a593Smuzhiyun wsm_cmd_lock(priv);
741*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT);
742*4882a593Smuzhiyun wsm_cmd_unlock(priv);
743*4882a593Smuzhiyun return ret;
744*4882a593Smuzhiyun }
745*4882a593Smuzhiyun
746*4882a593Smuzhiyun /* ******************************************************************** */
747*4882a593Smuzhiyun
wsm_map_link(struct cw1200_common * priv,const struct wsm_map_link * arg)748*4882a593Smuzhiyun int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg)
749*4882a593Smuzhiyun {
750*4882a593Smuzhiyun int ret;
751*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
752*4882a593Smuzhiyun u16 cmd = 0x001C | WSM_TX_LINK_ID(arg->link_id);
753*4882a593Smuzhiyun
754*4882a593Smuzhiyun wsm_cmd_lock(priv);
755*4882a593Smuzhiyun
756*4882a593Smuzhiyun WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr));
757*4882a593Smuzhiyun WSM_PUT16(buf, 0);
758*4882a593Smuzhiyun
759*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT);
760*4882a593Smuzhiyun
761*4882a593Smuzhiyun wsm_cmd_unlock(priv);
762*4882a593Smuzhiyun return ret;
763*4882a593Smuzhiyun
764*4882a593Smuzhiyun nomem:
765*4882a593Smuzhiyun wsm_cmd_unlock(priv);
766*4882a593Smuzhiyun return -ENOMEM;
767*4882a593Smuzhiyun }
768*4882a593Smuzhiyun
769*4882a593Smuzhiyun /* ******************************************************************** */
770*4882a593Smuzhiyun
wsm_update_ie(struct cw1200_common * priv,const struct wsm_update_ie * arg)771*4882a593Smuzhiyun int wsm_update_ie(struct cw1200_common *priv,
772*4882a593Smuzhiyun const struct wsm_update_ie *arg)
773*4882a593Smuzhiyun {
774*4882a593Smuzhiyun int ret;
775*4882a593Smuzhiyun struct wsm_buf *buf = &priv->wsm_cmd_buf;
776*4882a593Smuzhiyun
777*4882a593Smuzhiyun wsm_cmd_lock(priv);
778*4882a593Smuzhiyun
779*4882a593Smuzhiyun WSM_PUT16(buf, arg->what);
780*4882a593Smuzhiyun WSM_PUT16(buf, arg->count);
781*4882a593Smuzhiyun WSM_PUT(buf, arg->ies, arg->length);
782*4882a593Smuzhiyun
783*4882a593Smuzhiyun ret = wsm_cmd_send(priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT);
784*4882a593Smuzhiyun
785*4882a593Smuzhiyun wsm_cmd_unlock(priv);
786*4882a593Smuzhiyun return ret;
787*4882a593Smuzhiyun
788*4882a593Smuzhiyun nomem:
789*4882a593Smuzhiyun wsm_cmd_unlock(priv);
790*4882a593Smuzhiyun return -ENOMEM;
791*4882a593Smuzhiyun }
792*4882a593Smuzhiyun
793*4882a593Smuzhiyun /* ******************************************************************** */
wsm_set_probe_responder(struct cw1200_common * priv,bool enable)794*4882a593Smuzhiyun int wsm_set_probe_responder(struct cw1200_common *priv, bool enable)
795*4882a593Smuzhiyun {
796*4882a593Smuzhiyun priv->rx_filter.probeResponder = enable;
797*4882a593Smuzhiyun return wsm_set_rx_filter(priv, &priv->rx_filter);
798*4882a593Smuzhiyun }
799*4882a593Smuzhiyun
800*4882a593Smuzhiyun /* ******************************************************************** */
801*4882a593Smuzhiyun /* WSM indication events implementation */
802*4882a593Smuzhiyun const char * const cw1200_fw_types[] = {
803*4882a593Smuzhiyun "ETF",
804*4882a593Smuzhiyun "WFM",
805*4882a593Smuzhiyun "WSM",
806*4882a593Smuzhiyun "HI test",
807*4882a593Smuzhiyun "Platform test"
808*4882a593Smuzhiyun };
809*4882a593Smuzhiyun
wsm_startup_indication(struct cw1200_common * priv,struct wsm_buf * buf)810*4882a593Smuzhiyun static int wsm_startup_indication(struct cw1200_common *priv,
811*4882a593Smuzhiyun struct wsm_buf *buf)
812*4882a593Smuzhiyun {
813*4882a593Smuzhiyun priv->wsm_caps.input_buffers = WSM_GET16(buf);
814*4882a593Smuzhiyun priv->wsm_caps.input_buffer_size = WSM_GET16(buf);
815*4882a593Smuzhiyun priv->wsm_caps.hw_id = WSM_GET16(buf);
816*4882a593Smuzhiyun priv->wsm_caps.hw_subid = WSM_GET16(buf);
817*4882a593Smuzhiyun priv->wsm_caps.status = WSM_GET16(buf);
818*4882a593Smuzhiyun priv->wsm_caps.fw_cap = WSM_GET16(buf);
819*4882a593Smuzhiyun priv->wsm_caps.fw_type = WSM_GET16(buf);
820*4882a593Smuzhiyun priv->wsm_caps.fw_api = WSM_GET16(buf);
821*4882a593Smuzhiyun priv->wsm_caps.fw_build = WSM_GET16(buf);
822*4882a593Smuzhiyun priv->wsm_caps.fw_ver = WSM_GET16(buf);
823*4882a593Smuzhiyun WSM_GET(buf, priv->wsm_caps.fw_label, sizeof(priv->wsm_caps.fw_label));
824*4882a593Smuzhiyun priv->wsm_caps.fw_label[sizeof(priv->wsm_caps.fw_label) - 1] = 0; /* Do not trust FW too much... */
825*4882a593Smuzhiyun
826*4882a593Smuzhiyun if (WARN_ON(priv->wsm_caps.status))
827*4882a593Smuzhiyun return -EINVAL;
828*4882a593Smuzhiyun
829*4882a593Smuzhiyun if (WARN_ON(priv->wsm_caps.fw_type > 4))
830*4882a593Smuzhiyun return -EINVAL;
831*4882a593Smuzhiyun
832*4882a593Smuzhiyun pr_info("CW1200 WSM init done.\n"
833*4882a593Smuzhiyun " Input buffers: %d x %d bytes\n"
834*4882a593Smuzhiyun " Hardware: %d.%d\n"
835*4882a593Smuzhiyun " %s firmware [%s], ver: %d, build: %d,"
836*4882a593Smuzhiyun " api: %d, cap: 0x%.4X\n",
837*4882a593Smuzhiyun priv->wsm_caps.input_buffers,
838*4882a593Smuzhiyun priv->wsm_caps.input_buffer_size,
839*4882a593Smuzhiyun priv->wsm_caps.hw_id, priv->wsm_caps.hw_subid,
840*4882a593Smuzhiyun cw1200_fw_types[priv->wsm_caps.fw_type],
841*4882a593Smuzhiyun priv->wsm_caps.fw_label, priv->wsm_caps.fw_ver,
842*4882a593Smuzhiyun priv->wsm_caps.fw_build,
843*4882a593Smuzhiyun priv->wsm_caps.fw_api, priv->wsm_caps.fw_cap);
844*4882a593Smuzhiyun
845*4882a593Smuzhiyun /* Disable unsupported frequency bands */
846*4882a593Smuzhiyun if (!(priv->wsm_caps.fw_cap & 0x1))
847*4882a593Smuzhiyun priv->hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL;
848*4882a593Smuzhiyun if (!(priv->wsm_caps.fw_cap & 0x2))
849*4882a593Smuzhiyun priv->hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL;
850*4882a593Smuzhiyun
851*4882a593Smuzhiyun priv->firmware_ready = 1;
852*4882a593Smuzhiyun wake_up(&priv->wsm_startup_done);
853*4882a593Smuzhiyun return 0;
854*4882a593Smuzhiyun
855*4882a593Smuzhiyun underflow:
856*4882a593Smuzhiyun WARN_ON(1);
857*4882a593Smuzhiyun return -EINVAL;
858*4882a593Smuzhiyun }
859*4882a593Smuzhiyun
wsm_receive_indication(struct cw1200_common * priv,int link_id,struct wsm_buf * buf,struct sk_buff ** skb_p)860*4882a593Smuzhiyun static int wsm_receive_indication(struct cw1200_common *priv,
861*4882a593Smuzhiyun int link_id,
862*4882a593Smuzhiyun struct wsm_buf *buf,
863*4882a593Smuzhiyun struct sk_buff **skb_p)
864*4882a593Smuzhiyun {
865*4882a593Smuzhiyun struct wsm_rx rx;
866*4882a593Smuzhiyun struct ieee80211_hdr *hdr;
867*4882a593Smuzhiyun size_t hdr_len;
868*4882a593Smuzhiyun __le16 fctl;
869*4882a593Smuzhiyun
870*4882a593Smuzhiyun rx.status = WSM_GET32(buf);
871*4882a593Smuzhiyun rx.channel_number = WSM_GET16(buf);
872*4882a593Smuzhiyun rx.rx_rate = WSM_GET8(buf);
873*4882a593Smuzhiyun rx.rcpi_rssi = WSM_GET8(buf);
874*4882a593Smuzhiyun rx.flags = WSM_GET32(buf);
875*4882a593Smuzhiyun
876*4882a593Smuzhiyun /* FW Workaround: Drop probe resp or
877*4882a593Smuzhiyun beacon when RSSI is 0
878*4882a593Smuzhiyun */
879*4882a593Smuzhiyun hdr = (struct ieee80211_hdr *)(*skb_p)->data;
880*4882a593Smuzhiyun
881*4882a593Smuzhiyun if (!rx.rcpi_rssi &&
882*4882a593Smuzhiyun (ieee80211_is_probe_resp(hdr->frame_control) ||
883*4882a593Smuzhiyun ieee80211_is_beacon(hdr->frame_control)))
884*4882a593Smuzhiyun return 0;
885*4882a593Smuzhiyun
886*4882a593Smuzhiyun /* If no RSSI subscription has been made,
887*4882a593Smuzhiyun * convert RCPI to RSSI here
888*4882a593Smuzhiyun */
889*4882a593Smuzhiyun if (!priv->cqm_use_rssi)
890*4882a593Smuzhiyun rx.rcpi_rssi = rx.rcpi_rssi / 2 - 110;
891*4882a593Smuzhiyun
892*4882a593Smuzhiyun fctl = *(__le16 *)buf->data;
893*4882a593Smuzhiyun hdr_len = buf->data - buf->begin;
894*4882a593Smuzhiyun skb_pull(*skb_p, hdr_len);
895*4882a593Smuzhiyun if (!rx.status && ieee80211_is_deauth(fctl)) {
896*4882a593Smuzhiyun if (priv->join_status == CW1200_JOIN_STATUS_STA) {
897*4882a593Smuzhiyun /* Shedule unjoin work */
898*4882a593Smuzhiyun pr_debug("[WSM] Issue unjoin command (RX).\n");
899*4882a593Smuzhiyun wsm_lock_tx_async(priv);
900*4882a593Smuzhiyun if (queue_work(priv->workqueue,
901*4882a593Smuzhiyun &priv->unjoin_work) <= 0)
902*4882a593Smuzhiyun wsm_unlock_tx(priv);
903*4882a593Smuzhiyun }
904*4882a593Smuzhiyun }
905*4882a593Smuzhiyun cw1200_rx_cb(priv, &rx, link_id, skb_p);
906*4882a593Smuzhiyun if (*skb_p)
907*4882a593Smuzhiyun skb_push(*skb_p, hdr_len);
908*4882a593Smuzhiyun
909*4882a593Smuzhiyun return 0;
910*4882a593Smuzhiyun
911*4882a593Smuzhiyun underflow:
912*4882a593Smuzhiyun return -EINVAL;
913*4882a593Smuzhiyun }
914*4882a593Smuzhiyun
wsm_event_indication(struct cw1200_common * priv,struct wsm_buf * buf)915*4882a593Smuzhiyun static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf)
916*4882a593Smuzhiyun {
917*4882a593Smuzhiyun int first;
918*4882a593Smuzhiyun struct cw1200_wsm_event *event;
919*4882a593Smuzhiyun
920*4882a593Smuzhiyun if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
921*4882a593Smuzhiyun /* STA is stopped. */
922*4882a593Smuzhiyun return 0;
923*4882a593Smuzhiyun }
924*4882a593Smuzhiyun
925*4882a593Smuzhiyun event = kzalloc(sizeof(struct cw1200_wsm_event), GFP_KERNEL);
926*4882a593Smuzhiyun if (!event)
927*4882a593Smuzhiyun return -ENOMEM;
928*4882a593Smuzhiyun
929*4882a593Smuzhiyun event->evt.id = WSM_GET32(buf);
930*4882a593Smuzhiyun event->evt.data = WSM_GET32(buf);
931*4882a593Smuzhiyun
932*4882a593Smuzhiyun pr_debug("[WSM] Event: %d(%d)\n",
933*4882a593Smuzhiyun event->evt.id, event->evt.data);
934*4882a593Smuzhiyun
935*4882a593Smuzhiyun spin_lock(&priv->event_queue_lock);
936*4882a593Smuzhiyun first = list_empty(&priv->event_queue);
937*4882a593Smuzhiyun list_add_tail(&event->link, &priv->event_queue);
938*4882a593Smuzhiyun spin_unlock(&priv->event_queue_lock);
939*4882a593Smuzhiyun
940*4882a593Smuzhiyun if (first)
941*4882a593Smuzhiyun queue_work(priv->workqueue, &priv->event_handler);
942*4882a593Smuzhiyun
943*4882a593Smuzhiyun return 0;
944*4882a593Smuzhiyun
945*4882a593Smuzhiyun underflow:
946*4882a593Smuzhiyun kfree(event);
947*4882a593Smuzhiyun return -EINVAL;
948*4882a593Smuzhiyun }
949*4882a593Smuzhiyun
wsm_channel_switch_indication(struct cw1200_common * priv,struct wsm_buf * buf)950*4882a593Smuzhiyun static int wsm_channel_switch_indication(struct cw1200_common *priv,
951*4882a593Smuzhiyun struct wsm_buf *buf)
952*4882a593Smuzhiyun {
953*4882a593Smuzhiyun WARN_ON(WSM_GET32(buf));
954*4882a593Smuzhiyun
955*4882a593Smuzhiyun priv->channel_switch_in_progress = 0;
956*4882a593Smuzhiyun wake_up(&priv->channel_switch_done);
957*4882a593Smuzhiyun
958*4882a593Smuzhiyun wsm_unlock_tx(priv);
959*4882a593Smuzhiyun
960*4882a593Smuzhiyun return 0;
961*4882a593Smuzhiyun
962*4882a593Smuzhiyun underflow:
963*4882a593Smuzhiyun return -EINVAL;
964*4882a593Smuzhiyun }
965*4882a593Smuzhiyun
wsm_set_pm_indication(struct cw1200_common * priv,struct wsm_buf * buf)966*4882a593Smuzhiyun static int wsm_set_pm_indication(struct cw1200_common *priv,
967*4882a593Smuzhiyun struct wsm_buf *buf)
968*4882a593Smuzhiyun {
969*4882a593Smuzhiyun /* TODO: Check buf (struct wsm_set_pm_complete) for validity */
970*4882a593Smuzhiyun if (priv->ps_mode_switch_in_progress) {
971*4882a593Smuzhiyun priv->ps_mode_switch_in_progress = 0;
972*4882a593Smuzhiyun wake_up(&priv->ps_mode_switch_done);
973*4882a593Smuzhiyun }
974*4882a593Smuzhiyun return 0;
975*4882a593Smuzhiyun }
976*4882a593Smuzhiyun
wsm_scan_started(struct cw1200_common * priv,void * arg,struct wsm_buf * buf)977*4882a593Smuzhiyun static int wsm_scan_started(struct cw1200_common *priv, void *arg,
978*4882a593Smuzhiyun struct wsm_buf *buf)
979*4882a593Smuzhiyun {
980*4882a593Smuzhiyun u32 status = WSM_GET32(buf);
981*4882a593Smuzhiyun if (status != WSM_STATUS_SUCCESS) {
982*4882a593Smuzhiyun cw1200_scan_failed_cb(priv);
983*4882a593Smuzhiyun return -EINVAL;
984*4882a593Smuzhiyun }
985*4882a593Smuzhiyun return 0;
986*4882a593Smuzhiyun
987*4882a593Smuzhiyun underflow:
988*4882a593Smuzhiyun WARN_ON(1);
989*4882a593Smuzhiyun return -EINVAL;
990*4882a593Smuzhiyun }
991*4882a593Smuzhiyun
wsm_scan_complete_indication(struct cw1200_common * priv,struct wsm_buf * buf)992*4882a593Smuzhiyun static int wsm_scan_complete_indication(struct cw1200_common *priv,
993*4882a593Smuzhiyun struct wsm_buf *buf)
994*4882a593Smuzhiyun {
995*4882a593Smuzhiyun struct wsm_scan_complete arg;
996*4882a593Smuzhiyun arg.status = WSM_GET32(buf);
997*4882a593Smuzhiyun arg.psm = WSM_GET8(buf);
998*4882a593Smuzhiyun arg.num_channels = WSM_GET8(buf);
999*4882a593Smuzhiyun cw1200_scan_complete_cb(priv, &arg);
1000*4882a593Smuzhiyun
1001*4882a593Smuzhiyun return 0;
1002*4882a593Smuzhiyun
1003*4882a593Smuzhiyun underflow:
1004*4882a593Smuzhiyun return -EINVAL;
1005*4882a593Smuzhiyun }
1006*4882a593Smuzhiyun
wsm_join_complete_indication(struct cw1200_common * priv,struct wsm_buf * buf)1007*4882a593Smuzhiyun static int wsm_join_complete_indication(struct cw1200_common *priv,
1008*4882a593Smuzhiyun struct wsm_buf *buf)
1009*4882a593Smuzhiyun {
1010*4882a593Smuzhiyun struct wsm_join_complete arg;
1011*4882a593Smuzhiyun arg.status = WSM_GET32(buf);
1012*4882a593Smuzhiyun pr_debug("[WSM] Join complete indication, status: %d\n", arg.status);
1013*4882a593Smuzhiyun cw1200_join_complete_cb(priv, &arg);
1014*4882a593Smuzhiyun
1015*4882a593Smuzhiyun return 0;
1016*4882a593Smuzhiyun
1017*4882a593Smuzhiyun underflow:
1018*4882a593Smuzhiyun return -EINVAL;
1019*4882a593Smuzhiyun }
1020*4882a593Smuzhiyun
wsm_find_complete_indication(struct cw1200_common * priv,struct wsm_buf * buf)1021*4882a593Smuzhiyun static int wsm_find_complete_indication(struct cw1200_common *priv,
1022*4882a593Smuzhiyun struct wsm_buf *buf)
1023*4882a593Smuzhiyun {
1024*4882a593Smuzhiyun pr_warn("Implement find_complete_indication\n");
1025*4882a593Smuzhiyun return 0;
1026*4882a593Smuzhiyun }
1027*4882a593Smuzhiyun
wsm_ba_timeout_indication(struct cw1200_common * priv,struct wsm_buf * buf)1028*4882a593Smuzhiyun static int wsm_ba_timeout_indication(struct cw1200_common *priv,
1029*4882a593Smuzhiyun struct wsm_buf *buf)
1030*4882a593Smuzhiyun {
1031*4882a593Smuzhiyun u8 tid;
1032*4882a593Smuzhiyun u8 addr[ETH_ALEN];
1033*4882a593Smuzhiyun
1034*4882a593Smuzhiyun WSM_GET32(buf);
1035*4882a593Smuzhiyun tid = WSM_GET8(buf);
1036*4882a593Smuzhiyun WSM_GET8(buf);
1037*4882a593Smuzhiyun WSM_GET(buf, addr, ETH_ALEN);
1038*4882a593Smuzhiyun
1039*4882a593Smuzhiyun pr_info("BlockACK timeout, tid %d, addr %pM\n",
1040*4882a593Smuzhiyun tid, addr);
1041*4882a593Smuzhiyun
1042*4882a593Smuzhiyun return 0;
1043*4882a593Smuzhiyun
1044*4882a593Smuzhiyun underflow:
1045*4882a593Smuzhiyun return -EINVAL;
1046*4882a593Smuzhiyun }
1047*4882a593Smuzhiyun
wsm_suspend_resume_indication(struct cw1200_common * priv,int link_id,struct wsm_buf * buf)1048*4882a593Smuzhiyun static int wsm_suspend_resume_indication(struct cw1200_common *priv,
1049*4882a593Smuzhiyun int link_id, struct wsm_buf *buf)
1050*4882a593Smuzhiyun {
1051*4882a593Smuzhiyun u32 flags;
1052*4882a593Smuzhiyun struct wsm_suspend_resume arg;
1053*4882a593Smuzhiyun
1054*4882a593Smuzhiyun flags = WSM_GET32(buf);
1055*4882a593Smuzhiyun arg.link_id = link_id;
1056*4882a593Smuzhiyun arg.stop = !(flags & 1);
1057*4882a593Smuzhiyun arg.multicast = !!(flags & 8);
1058*4882a593Smuzhiyun arg.queue = (flags >> 1) & 3;
1059*4882a593Smuzhiyun
1060*4882a593Smuzhiyun cw1200_suspend_resume(priv, &arg);
1061*4882a593Smuzhiyun
1062*4882a593Smuzhiyun return 0;
1063*4882a593Smuzhiyun
1064*4882a593Smuzhiyun underflow:
1065*4882a593Smuzhiyun return -EINVAL;
1066*4882a593Smuzhiyun }
1067*4882a593Smuzhiyun
1068*4882a593Smuzhiyun
1069*4882a593Smuzhiyun /* ******************************************************************** */
1070*4882a593Smuzhiyun /* WSM TX */
1071*4882a593Smuzhiyun
wsm_cmd_send(struct cw1200_common * priv,struct wsm_buf * buf,void * arg,u16 cmd,long tmo)1072*4882a593Smuzhiyun static int wsm_cmd_send(struct cw1200_common *priv,
1073*4882a593Smuzhiyun struct wsm_buf *buf,
1074*4882a593Smuzhiyun void *arg, u16 cmd, long tmo)
1075*4882a593Smuzhiyun {
1076*4882a593Smuzhiyun size_t buf_len = buf->data - buf->begin;
1077*4882a593Smuzhiyun int ret;
1078*4882a593Smuzhiyun
1079*4882a593Smuzhiyun /* Don't bother if we're dead. */
1080*4882a593Smuzhiyun if (priv->bh_error) {
1081*4882a593Smuzhiyun ret = 0;
1082*4882a593Smuzhiyun goto done;
1083*4882a593Smuzhiyun }
1084*4882a593Smuzhiyun
1085*4882a593Smuzhiyun /* Block until the cmd buffer is completed. Tortuous. */
1086*4882a593Smuzhiyun spin_lock(&priv->wsm_cmd.lock);
1087*4882a593Smuzhiyun while (!priv->wsm_cmd.done) {
1088*4882a593Smuzhiyun spin_unlock(&priv->wsm_cmd.lock);
1089*4882a593Smuzhiyun spin_lock(&priv->wsm_cmd.lock);
1090*4882a593Smuzhiyun }
1091*4882a593Smuzhiyun priv->wsm_cmd.done = 0;
1092*4882a593Smuzhiyun spin_unlock(&priv->wsm_cmd.lock);
1093*4882a593Smuzhiyun
1094*4882a593Smuzhiyun if (cmd == WSM_WRITE_MIB_REQ_ID ||
1095*4882a593Smuzhiyun cmd == WSM_READ_MIB_REQ_ID)
1096*4882a593Smuzhiyun pr_debug("[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%zu)\n",
1097*4882a593Smuzhiyun cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]),
1098*4882a593Smuzhiyun buf_len);
1099*4882a593Smuzhiyun else
1100*4882a593Smuzhiyun pr_debug("[WSM] >>> 0x%.4X (%zu)\n", cmd, buf_len);
1101*4882a593Smuzhiyun
1102*4882a593Smuzhiyun /* Due to buggy SPI on CW1200, we need to
1103*4882a593Smuzhiyun * pad the message by a few bytes to ensure
1104*4882a593Smuzhiyun * that it's completely received.
1105*4882a593Smuzhiyun */
1106*4882a593Smuzhiyun buf_len += 4;
1107*4882a593Smuzhiyun
1108*4882a593Smuzhiyun /* Fill HI message header */
1109*4882a593Smuzhiyun /* BH will add sequence number */
1110*4882a593Smuzhiyun ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len);
1111*4882a593Smuzhiyun ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd);
1112*4882a593Smuzhiyun
1113*4882a593Smuzhiyun spin_lock(&priv->wsm_cmd.lock);
1114*4882a593Smuzhiyun BUG_ON(priv->wsm_cmd.ptr);
1115*4882a593Smuzhiyun priv->wsm_cmd.ptr = buf->begin;
1116*4882a593Smuzhiyun priv->wsm_cmd.len = buf_len;
1117*4882a593Smuzhiyun priv->wsm_cmd.arg = arg;
1118*4882a593Smuzhiyun priv->wsm_cmd.cmd = cmd;
1119*4882a593Smuzhiyun spin_unlock(&priv->wsm_cmd.lock);
1120*4882a593Smuzhiyun
1121*4882a593Smuzhiyun cw1200_bh_wakeup(priv);
1122*4882a593Smuzhiyun
1123*4882a593Smuzhiyun /* Wait for command completion */
1124*4882a593Smuzhiyun ret = wait_event_timeout(priv->wsm_cmd_wq,
1125*4882a593Smuzhiyun priv->wsm_cmd.done, tmo);
1126*4882a593Smuzhiyun
1127*4882a593Smuzhiyun if (!ret && !priv->wsm_cmd.done) {
1128*4882a593Smuzhiyun spin_lock(&priv->wsm_cmd.lock);
1129*4882a593Smuzhiyun priv->wsm_cmd.done = 1;
1130*4882a593Smuzhiyun priv->wsm_cmd.ptr = NULL;
1131*4882a593Smuzhiyun spin_unlock(&priv->wsm_cmd.lock);
1132*4882a593Smuzhiyun if (priv->bh_error) {
1133*4882a593Smuzhiyun /* Return ok to help system cleanup */
1134*4882a593Smuzhiyun ret = 0;
1135*4882a593Smuzhiyun } else {
1136*4882a593Smuzhiyun pr_err("CMD req (0x%04x) stuck in firmware, killing BH\n", priv->wsm_cmd.cmd);
1137*4882a593Smuzhiyun print_hex_dump_bytes("REQDUMP: ", DUMP_PREFIX_NONE,
1138*4882a593Smuzhiyun buf->begin, buf_len);
1139*4882a593Smuzhiyun pr_err("Outstanding outgoing frames: %d\n", priv->hw_bufs_used);
1140*4882a593Smuzhiyun
1141*4882a593Smuzhiyun /* Kill BH thread to report the error to the top layer. */
1142*4882a593Smuzhiyun atomic_add(1, &priv->bh_term);
1143*4882a593Smuzhiyun wake_up(&priv->bh_wq);
1144*4882a593Smuzhiyun ret = -ETIMEDOUT;
1145*4882a593Smuzhiyun }
1146*4882a593Smuzhiyun } else {
1147*4882a593Smuzhiyun spin_lock(&priv->wsm_cmd.lock);
1148*4882a593Smuzhiyun BUG_ON(!priv->wsm_cmd.done);
1149*4882a593Smuzhiyun ret = priv->wsm_cmd.ret;
1150*4882a593Smuzhiyun spin_unlock(&priv->wsm_cmd.lock);
1151*4882a593Smuzhiyun }
1152*4882a593Smuzhiyun done:
1153*4882a593Smuzhiyun wsm_buf_reset(buf);
1154*4882a593Smuzhiyun return ret;
1155*4882a593Smuzhiyun }
1156*4882a593Smuzhiyun
1157*4882a593Smuzhiyun /* ******************************************************************** */
1158*4882a593Smuzhiyun /* WSM TX port control */
1159*4882a593Smuzhiyun
wsm_lock_tx(struct cw1200_common * priv)1160*4882a593Smuzhiyun void wsm_lock_tx(struct cw1200_common *priv)
1161*4882a593Smuzhiyun {
1162*4882a593Smuzhiyun wsm_cmd_lock(priv);
1163*4882a593Smuzhiyun if (atomic_add_return(1, &priv->tx_lock) == 1) {
1164*4882a593Smuzhiyun if (wsm_flush_tx(priv))
1165*4882a593Smuzhiyun pr_debug("[WSM] TX is locked.\n");
1166*4882a593Smuzhiyun }
1167*4882a593Smuzhiyun wsm_cmd_unlock(priv);
1168*4882a593Smuzhiyun }
1169*4882a593Smuzhiyun
wsm_lock_tx_async(struct cw1200_common * priv)1170*4882a593Smuzhiyun void wsm_lock_tx_async(struct cw1200_common *priv)
1171*4882a593Smuzhiyun {
1172*4882a593Smuzhiyun if (atomic_add_return(1, &priv->tx_lock) == 1)
1173*4882a593Smuzhiyun pr_debug("[WSM] TX is locked (async).\n");
1174*4882a593Smuzhiyun }
1175*4882a593Smuzhiyun
wsm_flush_tx(struct cw1200_common * priv)1176*4882a593Smuzhiyun bool wsm_flush_tx(struct cw1200_common *priv)
1177*4882a593Smuzhiyun {
1178*4882a593Smuzhiyun unsigned long timestamp = jiffies;
1179*4882a593Smuzhiyun bool pending = false;
1180*4882a593Smuzhiyun long timeout;
1181*4882a593Smuzhiyun int i;
1182*4882a593Smuzhiyun
1183*4882a593Smuzhiyun /* Flush must be called with TX lock held. */
1184*4882a593Smuzhiyun BUG_ON(!atomic_read(&priv->tx_lock));
1185*4882a593Smuzhiyun
1186*4882a593Smuzhiyun /* First check if we really need to do something.
1187*4882a593Smuzhiyun * It is safe to use unprotected access, as hw_bufs_used
1188*4882a593Smuzhiyun * can only decrements.
1189*4882a593Smuzhiyun */
1190*4882a593Smuzhiyun if (!priv->hw_bufs_used)
1191*4882a593Smuzhiyun return true;
1192*4882a593Smuzhiyun
1193*4882a593Smuzhiyun if (priv->bh_error) {
1194*4882a593Smuzhiyun /* In case of failure do not wait for magic. */
1195*4882a593Smuzhiyun pr_err("[WSM] Fatal error occurred, will not flush TX.\n");
1196*4882a593Smuzhiyun return false;
1197*4882a593Smuzhiyun } else {
1198*4882a593Smuzhiyun /* Get a timestamp of "oldest" frame */
1199*4882a593Smuzhiyun for (i = 0; i < 4; ++i)
1200*4882a593Smuzhiyun pending |= cw1200_queue_get_xmit_timestamp(
1201*4882a593Smuzhiyun &priv->tx_queue[i],
1202*4882a593Smuzhiyun ×tamp, 0xffffffff);
1203*4882a593Smuzhiyun /* If there's nothing pending, we're good */
1204*4882a593Smuzhiyun if (!pending)
1205*4882a593Smuzhiyun return true;
1206*4882a593Smuzhiyun
1207*4882a593Smuzhiyun timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies;
1208*4882a593Smuzhiyun if (timeout < 0 || wait_event_timeout(priv->bh_evt_wq,
1209*4882a593Smuzhiyun !priv->hw_bufs_used,
1210*4882a593Smuzhiyun timeout) <= 0) {
1211*4882a593Smuzhiyun /* Hmmm... Not good. Frame had stuck in firmware. */
1212*4882a593Smuzhiyun priv->bh_error = 1;
1213*4882a593Smuzhiyun wiphy_err(priv->hw->wiphy, "[WSM] TX Frames (%d) stuck in firmware, killing BH\n", priv->hw_bufs_used);
1214*4882a593Smuzhiyun wake_up(&priv->bh_wq);
1215*4882a593Smuzhiyun return false;
1216*4882a593Smuzhiyun }
1217*4882a593Smuzhiyun
1218*4882a593Smuzhiyun /* Ok, everything is flushed. */
1219*4882a593Smuzhiyun return true;
1220*4882a593Smuzhiyun }
1221*4882a593Smuzhiyun }
1222*4882a593Smuzhiyun
wsm_unlock_tx(struct cw1200_common * priv)1223*4882a593Smuzhiyun void wsm_unlock_tx(struct cw1200_common *priv)
1224*4882a593Smuzhiyun {
1225*4882a593Smuzhiyun int tx_lock;
1226*4882a593Smuzhiyun tx_lock = atomic_sub_return(1, &priv->tx_lock);
1227*4882a593Smuzhiyun BUG_ON(tx_lock < 0);
1228*4882a593Smuzhiyun
1229*4882a593Smuzhiyun if (tx_lock == 0) {
1230*4882a593Smuzhiyun if (!priv->bh_error)
1231*4882a593Smuzhiyun cw1200_bh_wakeup(priv);
1232*4882a593Smuzhiyun pr_debug("[WSM] TX is unlocked.\n");
1233*4882a593Smuzhiyun }
1234*4882a593Smuzhiyun }
1235*4882a593Smuzhiyun
1236*4882a593Smuzhiyun /* ******************************************************************** */
1237*4882a593Smuzhiyun /* WSM RX */
1238*4882a593Smuzhiyun
wsm_handle_exception(struct cw1200_common * priv,u8 * data,size_t len)1239*4882a593Smuzhiyun int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len)
1240*4882a593Smuzhiyun {
1241*4882a593Smuzhiyun struct wsm_buf buf;
1242*4882a593Smuzhiyun u32 reason;
1243*4882a593Smuzhiyun u32 reg[18];
1244*4882a593Smuzhiyun char fname[48];
1245*4882a593Smuzhiyun unsigned int i;
1246*4882a593Smuzhiyun
1247*4882a593Smuzhiyun static const char * const reason_str[] = {
1248*4882a593Smuzhiyun "undefined instruction",
1249*4882a593Smuzhiyun "prefetch abort",
1250*4882a593Smuzhiyun "data abort",
1251*4882a593Smuzhiyun "unknown error",
1252*4882a593Smuzhiyun };
1253*4882a593Smuzhiyun
1254*4882a593Smuzhiyun buf.begin = buf.data = data;
1255*4882a593Smuzhiyun buf.end = &buf.begin[len];
1256*4882a593Smuzhiyun
1257*4882a593Smuzhiyun reason = WSM_GET32(&buf);
1258*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(reg); ++i)
1259*4882a593Smuzhiyun reg[i] = WSM_GET32(&buf);
1260*4882a593Smuzhiyun WSM_GET(&buf, fname, sizeof(fname));
1261*4882a593Smuzhiyun
1262*4882a593Smuzhiyun if (reason < 4)
1263*4882a593Smuzhiyun wiphy_err(priv->hw->wiphy,
1264*4882a593Smuzhiyun "Firmware exception: %s.\n",
1265*4882a593Smuzhiyun reason_str[reason]);
1266*4882a593Smuzhiyun else
1267*4882a593Smuzhiyun wiphy_err(priv->hw->wiphy,
1268*4882a593Smuzhiyun "Firmware assert at %.*s, line %d\n",
1269*4882a593Smuzhiyun (int) sizeof(fname), fname, reg[1]);
1270*4882a593Smuzhiyun
1271*4882a593Smuzhiyun for (i = 0; i < 12; i += 4)
1272*4882a593Smuzhiyun wiphy_err(priv->hw->wiphy,
1273*4882a593Smuzhiyun "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n",
1274*4882a593Smuzhiyun i + 0, reg[i + 0], i + 1, reg[i + 1],
1275*4882a593Smuzhiyun i + 2, reg[i + 2], i + 3, reg[i + 3]);
1276*4882a593Smuzhiyun wiphy_err(priv->hw->wiphy,
1277*4882a593Smuzhiyun "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n",
1278*4882a593Smuzhiyun reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]);
1279*4882a593Smuzhiyun i += 4;
1280*4882a593Smuzhiyun wiphy_err(priv->hw->wiphy,
1281*4882a593Smuzhiyun "CPSR: 0x%.8X, SPSR: 0x%.8X\n",
1282*4882a593Smuzhiyun reg[i + 0], reg[i + 1]);
1283*4882a593Smuzhiyun
1284*4882a593Smuzhiyun print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE,
1285*4882a593Smuzhiyun fname, sizeof(fname));
1286*4882a593Smuzhiyun return 0;
1287*4882a593Smuzhiyun
1288*4882a593Smuzhiyun underflow:
1289*4882a593Smuzhiyun wiphy_err(priv->hw->wiphy, "Firmware exception.\n");
1290*4882a593Smuzhiyun print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE,
1291*4882a593Smuzhiyun data, len);
1292*4882a593Smuzhiyun return -EINVAL;
1293*4882a593Smuzhiyun }
1294*4882a593Smuzhiyun
wsm_handle_rx(struct cw1200_common * priv,u16 id,struct wsm_hdr * wsm,struct sk_buff ** skb_p)1295*4882a593Smuzhiyun int wsm_handle_rx(struct cw1200_common *priv, u16 id,
1296*4882a593Smuzhiyun struct wsm_hdr *wsm, struct sk_buff **skb_p)
1297*4882a593Smuzhiyun {
1298*4882a593Smuzhiyun int ret = 0;
1299*4882a593Smuzhiyun struct wsm_buf wsm_buf;
1300*4882a593Smuzhiyun int link_id = (id >> 6) & 0x0F;
1301*4882a593Smuzhiyun
1302*4882a593Smuzhiyun /* Strip link id. */
1303*4882a593Smuzhiyun id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
1304*4882a593Smuzhiyun
1305*4882a593Smuzhiyun wsm_buf.begin = (u8 *)&wsm[0];
1306*4882a593Smuzhiyun wsm_buf.data = (u8 *)&wsm[1];
1307*4882a593Smuzhiyun wsm_buf.end = &wsm_buf.begin[__le16_to_cpu(wsm->len)];
1308*4882a593Smuzhiyun
1309*4882a593Smuzhiyun pr_debug("[WSM] <<< 0x%.4X (%td)\n", id,
1310*4882a593Smuzhiyun wsm_buf.end - wsm_buf.begin);
1311*4882a593Smuzhiyun
1312*4882a593Smuzhiyun if (id == WSM_TX_CONFIRM_IND_ID) {
1313*4882a593Smuzhiyun ret = wsm_tx_confirm(priv, &wsm_buf, link_id);
1314*4882a593Smuzhiyun } else if (id == WSM_MULTI_TX_CONFIRM_ID) {
1315*4882a593Smuzhiyun ret = wsm_multi_tx_confirm(priv, &wsm_buf, link_id);
1316*4882a593Smuzhiyun } else if (id & 0x0400) {
1317*4882a593Smuzhiyun void *wsm_arg;
1318*4882a593Smuzhiyun u16 wsm_cmd;
1319*4882a593Smuzhiyun
1320*4882a593Smuzhiyun /* Do not trust FW too much. Protection against repeated
1321*4882a593Smuzhiyun * response and race condition removal (see above).
1322*4882a593Smuzhiyun */
1323*4882a593Smuzhiyun spin_lock(&priv->wsm_cmd.lock);
1324*4882a593Smuzhiyun wsm_arg = priv->wsm_cmd.arg;
1325*4882a593Smuzhiyun wsm_cmd = priv->wsm_cmd.cmd &
1326*4882a593Smuzhiyun ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
1327*4882a593Smuzhiyun priv->wsm_cmd.cmd = 0xFFFF;
1328*4882a593Smuzhiyun spin_unlock(&priv->wsm_cmd.lock);
1329*4882a593Smuzhiyun
1330*4882a593Smuzhiyun if (WARN_ON((id & ~0x0400) != wsm_cmd)) {
1331*4882a593Smuzhiyun /* Note that any non-zero is a fatal retcode. */
1332*4882a593Smuzhiyun ret = -EINVAL;
1333*4882a593Smuzhiyun goto out;
1334*4882a593Smuzhiyun }
1335*4882a593Smuzhiyun
1336*4882a593Smuzhiyun /* Note that wsm_arg can be NULL in case of timeout in
1337*4882a593Smuzhiyun * wsm_cmd_send().
1338*4882a593Smuzhiyun */
1339*4882a593Smuzhiyun
1340*4882a593Smuzhiyun switch (id) {
1341*4882a593Smuzhiyun case WSM_READ_MIB_RESP_ID:
1342*4882a593Smuzhiyun if (wsm_arg)
1343*4882a593Smuzhiyun ret = wsm_read_mib_confirm(priv, wsm_arg,
1344*4882a593Smuzhiyun &wsm_buf);
1345*4882a593Smuzhiyun break;
1346*4882a593Smuzhiyun case WSM_WRITE_MIB_RESP_ID:
1347*4882a593Smuzhiyun if (wsm_arg)
1348*4882a593Smuzhiyun ret = wsm_write_mib_confirm(priv, wsm_arg,
1349*4882a593Smuzhiyun &wsm_buf);
1350*4882a593Smuzhiyun break;
1351*4882a593Smuzhiyun case WSM_START_SCAN_RESP_ID:
1352*4882a593Smuzhiyun if (wsm_arg)
1353*4882a593Smuzhiyun ret = wsm_scan_started(priv, wsm_arg, &wsm_buf);
1354*4882a593Smuzhiyun break;
1355*4882a593Smuzhiyun case WSM_CONFIGURATION_RESP_ID:
1356*4882a593Smuzhiyun if (wsm_arg)
1357*4882a593Smuzhiyun ret = wsm_configuration_confirm(priv, wsm_arg,
1358*4882a593Smuzhiyun &wsm_buf);
1359*4882a593Smuzhiyun break;
1360*4882a593Smuzhiyun case WSM_JOIN_RESP_ID:
1361*4882a593Smuzhiyun if (wsm_arg)
1362*4882a593Smuzhiyun ret = wsm_join_confirm(priv, wsm_arg, &wsm_buf);
1363*4882a593Smuzhiyun break;
1364*4882a593Smuzhiyun case WSM_STOP_SCAN_RESP_ID:
1365*4882a593Smuzhiyun case WSM_RESET_RESP_ID:
1366*4882a593Smuzhiyun case WSM_ADD_KEY_RESP_ID:
1367*4882a593Smuzhiyun case WSM_REMOVE_KEY_RESP_ID:
1368*4882a593Smuzhiyun case WSM_SET_PM_RESP_ID:
1369*4882a593Smuzhiyun case WSM_SET_BSS_PARAMS_RESP_ID:
1370*4882a593Smuzhiyun case 0x0412: /* set_tx_queue_params */
1371*4882a593Smuzhiyun case WSM_EDCA_PARAMS_RESP_ID:
1372*4882a593Smuzhiyun case WSM_SWITCH_CHANNEL_RESP_ID:
1373*4882a593Smuzhiyun case WSM_START_RESP_ID:
1374*4882a593Smuzhiyun case WSM_BEACON_TRANSMIT_RESP_ID:
1375*4882a593Smuzhiyun case 0x0419: /* start_find */
1376*4882a593Smuzhiyun case 0x041A: /* stop_find */
1377*4882a593Smuzhiyun case 0x041B: /* update_ie */
1378*4882a593Smuzhiyun case 0x041C: /* map_link */
1379*4882a593Smuzhiyun WARN_ON(wsm_arg != NULL);
1380*4882a593Smuzhiyun ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf);
1381*4882a593Smuzhiyun if (ret) {
1382*4882a593Smuzhiyun wiphy_warn(priv->hw->wiphy,
1383*4882a593Smuzhiyun "wsm_generic_confirm failed for request 0x%04x.\n",
1384*4882a593Smuzhiyun id & ~0x0400);
1385*4882a593Smuzhiyun
1386*4882a593Smuzhiyun /* often 0x407 and 0x410 occur, this means we're dead.. */
1387*4882a593Smuzhiyun if (priv->join_status >= CW1200_JOIN_STATUS_JOINING) {
1388*4882a593Smuzhiyun wsm_lock_tx(priv);
1389*4882a593Smuzhiyun if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
1390*4882a593Smuzhiyun wsm_unlock_tx(priv);
1391*4882a593Smuzhiyun }
1392*4882a593Smuzhiyun }
1393*4882a593Smuzhiyun break;
1394*4882a593Smuzhiyun default:
1395*4882a593Smuzhiyun wiphy_warn(priv->hw->wiphy,
1396*4882a593Smuzhiyun "Unrecognized confirmation 0x%04x\n",
1397*4882a593Smuzhiyun id & ~0x0400);
1398*4882a593Smuzhiyun }
1399*4882a593Smuzhiyun
1400*4882a593Smuzhiyun spin_lock(&priv->wsm_cmd.lock);
1401*4882a593Smuzhiyun priv->wsm_cmd.ret = ret;
1402*4882a593Smuzhiyun priv->wsm_cmd.done = 1;
1403*4882a593Smuzhiyun spin_unlock(&priv->wsm_cmd.lock);
1404*4882a593Smuzhiyun
1405*4882a593Smuzhiyun ret = 0; /* Error response from device should ne stop BH. */
1406*4882a593Smuzhiyun
1407*4882a593Smuzhiyun wake_up(&priv->wsm_cmd_wq);
1408*4882a593Smuzhiyun } else if (id & 0x0800) {
1409*4882a593Smuzhiyun switch (id) {
1410*4882a593Smuzhiyun case WSM_STARTUP_IND_ID:
1411*4882a593Smuzhiyun ret = wsm_startup_indication(priv, &wsm_buf);
1412*4882a593Smuzhiyun break;
1413*4882a593Smuzhiyun case WSM_RECEIVE_IND_ID:
1414*4882a593Smuzhiyun ret = wsm_receive_indication(priv, link_id,
1415*4882a593Smuzhiyun &wsm_buf, skb_p);
1416*4882a593Smuzhiyun break;
1417*4882a593Smuzhiyun case 0x0805:
1418*4882a593Smuzhiyun ret = wsm_event_indication(priv, &wsm_buf);
1419*4882a593Smuzhiyun break;
1420*4882a593Smuzhiyun case WSM_SCAN_COMPLETE_IND_ID:
1421*4882a593Smuzhiyun ret = wsm_scan_complete_indication(priv, &wsm_buf);
1422*4882a593Smuzhiyun break;
1423*4882a593Smuzhiyun case 0x0808:
1424*4882a593Smuzhiyun ret = wsm_ba_timeout_indication(priv, &wsm_buf);
1425*4882a593Smuzhiyun break;
1426*4882a593Smuzhiyun case 0x0809:
1427*4882a593Smuzhiyun ret = wsm_set_pm_indication(priv, &wsm_buf);
1428*4882a593Smuzhiyun break;
1429*4882a593Smuzhiyun case 0x080A:
1430*4882a593Smuzhiyun ret = wsm_channel_switch_indication(priv, &wsm_buf);
1431*4882a593Smuzhiyun break;
1432*4882a593Smuzhiyun case 0x080B:
1433*4882a593Smuzhiyun ret = wsm_find_complete_indication(priv, &wsm_buf);
1434*4882a593Smuzhiyun break;
1435*4882a593Smuzhiyun case 0x080C:
1436*4882a593Smuzhiyun ret = wsm_suspend_resume_indication(priv,
1437*4882a593Smuzhiyun link_id, &wsm_buf);
1438*4882a593Smuzhiyun break;
1439*4882a593Smuzhiyun case 0x080F:
1440*4882a593Smuzhiyun ret = wsm_join_complete_indication(priv, &wsm_buf);
1441*4882a593Smuzhiyun break;
1442*4882a593Smuzhiyun default:
1443*4882a593Smuzhiyun pr_warn("Unrecognised WSM ID %04x\n", id);
1444*4882a593Smuzhiyun }
1445*4882a593Smuzhiyun } else {
1446*4882a593Smuzhiyun WARN_ON(1);
1447*4882a593Smuzhiyun ret = -EINVAL;
1448*4882a593Smuzhiyun }
1449*4882a593Smuzhiyun out:
1450*4882a593Smuzhiyun return ret;
1451*4882a593Smuzhiyun }
1452*4882a593Smuzhiyun
wsm_handle_tx_data(struct cw1200_common * priv,struct wsm_tx * wsm,const struct ieee80211_tx_info * tx_info,const struct cw1200_txpriv * txpriv,struct cw1200_queue * queue)1453*4882a593Smuzhiyun static bool wsm_handle_tx_data(struct cw1200_common *priv,
1454*4882a593Smuzhiyun struct wsm_tx *wsm,
1455*4882a593Smuzhiyun const struct ieee80211_tx_info *tx_info,
1456*4882a593Smuzhiyun const struct cw1200_txpriv *txpriv,
1457*4882a593Smuzhiyun struct cw1200_queue *queue)
1458*4882a593Smuzhiyun {
1459*4882a593Smuzhiyun bool handled = false;
1460*4882a593Smuzhiyun const struct ieee80211_hdr *frame =
1461*4882a593Smuzhiyun (struct ieee80211_hdr *)&((u8 *)wsm)[txpriv->offset];
1462*4882a593Smuzhiyun __le16 fctl = frame->frame_control;
1463*4882a593Smuzhiyun enum {
1464*4882a593Smuzhiyun do_probe,
1465*4882a593Smuzhiyun do_drop,
1466*4882a593Smuzhiyun do_wep,
1467*4882a593Smuzhiyun do_tx,
1468*4882a593Smuzhiyun } action = do_tx;
1469*4882a593Smuzhiyun
1470*4882a593Smuzhiyun switch (priv->mode) {
1471*4882a593Smuzhiyun case NL80211_IFTYPE_STATION:
1472*4882a593Smuzhiyun if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
1473*4882a593Smuzhiyun action = do_tx;
1474*4882a593Smuzhiyun else if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA)
1475*4882a593Smuzhiyun action = do_drop;
1476*4882a593Smuzhiyun break;
1477*4882a593Smuzhiyun case NL80211_IFTYPE_AP:
1478*4882a593Smuzhiyun if (!priv->join_status) {
1479*4882a593Smuzhiyun action = do_drop;
1480*4882a593Smuzhiyun } else if (!(BIT(txpriv->raw_link_id) &
1481*4882a593Smuzhiyun (BIT(0) | priv->link_id_map))) {
1482*4882a593Smuzhiyun wiphy_warn(priv->hw->wiphy,
1483*4882a593Smuzhiyun "A frame with expired link id is dropped.\n");
1484*4882a593Smuzhiyun action = do_drop;
1485*4882a593Smuzhiyun }
1486*4882a593Smuzhiyun if (cw1200_queue_get_generation(wsm->packet_id) >
1487*4882a593Smuzhiyun CW1200_MAX_REQUEUE_ATTEMPTS) {
1488*4882a593Smuzhiyun /* HACK!!! WSM324 firmware has tendency to requeue
1489*4882a593Smuzhiyun * multicast frames in a loop, causing performance
1490*4882a593Smuzhiyun * drop and high power consumption of the driver.
1491*4882a593Smuzhiyun * In this situation it is better just to drop
1492*4882a593Smuzhiyun * the problematic frame.
1493*4882a593Smuzhiyun */
1494*4882a593Smuzhiyun wiphy_warn(priv->hw->wiphy,
1495*4882a593Smuzhiyun "Too many attempts to requeue a frame; dropped.\n");
1496*4882a593Smuzhiyun action = do_drop;
1497*4882a593Smuzhiyun }
1498*4882a593Smuzhiyun break;
1499*4882a593Smuzhiyun case NL80211_IFTYPE_ADHOC:
1500*4882a593Smuzhiyun if (priv->join_status != CW1200_JOIN_STATUS_IBSS)
1501*4882a593Smuzhiyun action = do_drop;
1502*4882a593Smuzhiyun break;
1503*4882a593Smuzhiyun case NL80211_IFTYPE_MESH_POINT:
1504*4882a593Smuzhiyun action = do_tx; /* TODO: Test me! */
1505*4882a593Smuzhiyun break;
1506*4882a593Smuzhiyun case NL80211_IFTYPE_MONITOR:
1507*4882a593Smuzhiyun default:
1508*4882a593Smuzhiyun action = do_drop;
1509*4882a593Smuzhiyun break;
1510*4882a593Smuzhiyun }
1511*4882a593Smuzhiyun
1512*4882a593Smuzhiyun if (action == do_tx) {
1513*4882a593Smuzhiyun if (ieee80211_is_nullfunc(fctl)) {
1514*4882a593Smuzhiyun spin_lock(&priv->bss_loss_lock);
1515*4882a593Smuzhiyun if (priv->bss_loss_state) {
1516*4882a593Smuzhiyun priv->bss_loss_confirm_id = wsm->packet_id;
1517*4882a593Smuzhiyun wsm->queue_id = WSM_QUEUE_VOICE;
1518*4882a593Smuzhiyun }
1519*4882a593Smuzhiyun spin_unlock(&priv->bss_loss_lock);
1520*4882a593Smuzhiyun } else if (ieee80211_is_probe_req(fctl)) {
1521*4882a593Smuzhiyun action = do_probe;
1522*4882a593Smuzhiyun } else if (ieee80211_is_deauth(fctl) &&
1523*4882a593Smuzhiyun priv->mode != NL80211_IFTYPE_AP) {
1524*4882a593Smuzhiyun pr_debug("[WSM] Issue unjoin command due to tx deauth.\n");
1525*4882a593Smuzhiyun wsm_lock_tx_async(priv);
1526*4882a593Smuzhiyun if (queue_work(priv->workqueue,
1527*4882a593Smuzhiyun &priv->unjoin_work) <= 0)
1528*4882a593Smuzhiyun wsm_unlock_tx(priv);
1529*4882a593Smuzhiyun } else if (ieee80211_has_protected(fctl) &&
1530*4882a593Smuzhiyun tx_info->control.hw_key &&
1531*4882a593Smuzhiyun tx_info->control.hw_key->keyidx != priv->wep_default_key_id &&
1532*4882a593Smuzhiyun (tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
1533*4882a593Smuzhiyun tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP104)) {
1534*4882a593Smuzhiyun action = do_wep;
1535*4882a593Smuzhiyun }
1536*4882a593Smuzhiyun }
1537*4882a593Smuzhiyun
1538*4882a593Smuzhiyun switch (action) {
1539*4882a593Smuzhiyun case do_probe:
1540*4882a593Smuzhiyun /* An interesting FW "feature". Device filters probe responses.
1541*4882a593Smuzhiyun * The easiest way to get it back is to convert
1542*4882a593Smuzhiyun * probe request into WSM start_scan command.
1543*4882a593Smuzhiyun */
1544*4882a593Smuzhiyun pr_debug("[WSM] Convert probe request to scan.\n");
1545*4882a593Smuzhiyun wsm_lock_tx_async(priv);
1546*4882a593Smuzhiyun priv->pending_frame_id = wsm->packet_id;
1547*4882a593Smuzhiyun if (queue_delayed_work(priv->workqueue,
1548*4882a593Smuzhiyun &priv->scan.probe_work, 0) <= 0)
1549*4882a593Smuzhiyun wsm_unlock_tx(priv);
1550*4882a593Smuzhiyun handled = true;
1551*4882a593Smuzhiyun break;
1552*4882a593Smuzhiyun case do_drop:
1553*4882a593Smuzhiyun pr_debug("[WSM] Drop frame (0x%.4X).\n", fctl);
1554*4882a593Smuzhiyun BUG_ON(cw1200_queue_remove(queue, wsm->packet_id));
1555*4882a593Smuzhiyun handled = true;
1556*4882a593Smuzhiyun break;
1557*4882a593Smuzhiyun case do_wep:
1558*4882a593Smuzhiyun pr_debug("[WSM] Issue set_default_wep_key.\n");
1559*4882a593Smuzhiyun wsm_lock_tx_async(priv);
1560*4882a593Smuzhiyun priv->wep_default_key_id = tx_info->control.hw_key->keyidx;
1561*4882a593Smuzhiyun priv->pending_frame_id = wsm->packet_id;
1562*4882a593Smuzhiyun if (queue_work(priv->workqueue, &priv->wep_key_work) <= 0)
1563*4882a593Smuzhiyun wsm_unlock_tx(priv);
1564*4882a593Smuzhiyun handled = true;
1565*4882a593Smuzhiyun break;
1566*4882a593Smuzhiyun case do_tx:
1567*4882a593Smuzhiyun pr_debug("[WSM] Transmit frame.\n");
1568*4882a593Smuzhiyun break;
1569*4882a593Smuzhiyun default:
1570*4882a593Smuzhiyun /* Do nothing */
1571*4882a593Smuzhiyun break;
1572*4882a593Smuzhiyun }
1573*4882a593Smuzhiyun return handled;
1574*4882a593Smuzhiyun }
1575*4882a593Smuzhiyun
cw1200_get_prio_queue(struct cw1200_common * priv,u32 link_id_map,int * total)1576*4882a593Smuzhiyun static int cw1200_get_prio_queue(struct cw1200_common *priv,
1577*4882a593Smuzhiyun u32 link_id_map, int *total)
1578*4882a593Smuzhiyun {
1579*4882a593Smuzhiyun static const int urgent = BIT(CW1200_LINK_ID_AFTER_DTIM) |
1580*4882a593Smuzhiyun BIT(CW1200_LINK_ID_UAPSD);
1581*4882a593Smuzhiyun struct wsm_edca_queue_params *edca;
1582*4882a593Smuzhiyun unsigned score, best = -1;
1583*4882a593Smuzhiyun int winner = -1;
1584*4882a593Smuzhiyun int queued;
1585*4882a593Smuzhiyun int i;
1586*4882a593Smuzhiyun
1587*4882a593Smuzhiyun /* search for a winner using edca params */
1588*4882a593Smuzhiyun for (i = 0; i < 4; ++i) {
1589*4882a593Smuzhiyun queued = cw1200_queue_get_num_queued(&priv->tx_queue[i],
1590*4882a593Smuzhiyun link_id_map);
1591*4882a593Smuzhiyun if (!queued)
1592*4882a593Smuzhiyun continue;
1593*4882a593Smuzhiyun *total += queued;
1594*4882a593Smuzhiyun edca = &priv->edca.params[i];
1595*4882a593Smuzhiyun score = ((edca->aifns + edca->cwmin) << 16) +
1596*4882a593Smuzhiyun ((edca->cwmax - edca->cwmin) *
1597*4882a593Smuzhiyun (get_random_int() & 0xFFFF));
1598*4882a593Smuzhiyun if (score < best && (winner < 0 || i != 3)) {
1599*4882a593Smuzhiyun best = score;
1600*4882a593Smuzhiyun winner = i;
1601*4882a593Smuzhiyun }
1602*4882a593Smuzhiyun }
1603*4882a593Smuzhiyun
1604*4882a593Smuzhiyun /* override winner if bursting */
1605*4882a593Smuzhiyun if (winner >= 0 && priv->tx_burst_idx >= 0 &&
1606*4882a593Smuzhiyun winner != priv->tx_burst_idx &&
1607*4882a593Smuzhiyun !cw1200_queue_get_num_queued(
1608*4882a593Smuzhiyun &priv->tx_queue[winner],
1609*4882a593Smuzhiyun link_id_map & urgent) &&
1610*4882a593Smuzhiyun cw1200_queue_get_num_queued(
1611*4882a593Smuzhiyun &priv->tx_queue[priv->tx_burst_idx],
1612*4882a593Smuzhiyun link_id_map))
1613*4882a593Smuzhiyun winner = priv->tx_burst_idx;
1614*4882a593Smuzhiyun
1615*4882a593Smuzhiyun return winner;
1616*4882a593Smuzhiyun }
1617*4882a593Smuzhiyun
wsm_get_tx_queue_and_mask(struct cw1200_common * priv,struct cw1200_queue ** queue_p,u32 * tx_allowed_mask_p,bool * more)1618*4882a593Smuzhiyun static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv,
1619*4882a593Smuzhiyun struct cw1200_queue **queue_p,
1620*4882a593Smuzhiyun u32 *tx_allowed_mask_p,
1621*4882a593Smuzhiyun bool *more)
1622*4882a593Smuzhiyun {
1623*4882a593Smuzhiyun int idx;
1624*4882a593Smuzhiyun u32 tx_allowed_mask;
1625*4882a593Smuzhiyun int total = 0;
1626*4882a593Smuzhiyun
1627*4882a593Smuzhiyun /* Search for a queue with multicast frames buffered */
1628*4882a593Smuzhiyun if (priv->tx_multicast) {
1629*4882a593Smuzhiyun tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM);
1630*4882a593Smuzhiyun idx = cw1200_get_prio_queue(priv,
1631*4882a593Smuzhiyun tx_allowed_mask, &total);
1632*4882a593Smuzhiyun if (idx >= 0) {
1633*4882a593Smuzhiyun *more = total > 1;
1634*4882a593Smuzhiyun goto found;
1635*4882a593Smuzhiyun }
1636*4882a593Smuzhiyun }
1637*4882a593Smuzhiyun
1638*4882a593Smuzhiyun /* Search for unicast traffic */
1639*4882a593Smuzhiyun tx_allowed_mask = ~priv->sta_asleep_mask;
1640*4882a593Smuzhiyun tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD);
1641*4882a593Smuzhiyun if (priv->sta_asleep_mask) {
1642*4882a593Smuzhiyun tx_allowed_mask |= priv->pspoll_mask;
1643*4882a593Smuzhiyun tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM);
1644*4882a593Smuzhiyun } else {
1645*4882a593Smuzhiyun tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM);
1646*4882a593Smuzhiyun }
1647*4882a593Smuzhiyun idx = cw1200_get_prio_queue(priv,
1648*4882a593Smuzhiyun tx_allowed_mask, &total);
1649*4882a593Smuzhiyun if (idx < 0)
1650*4882a593Smuzhiyun return -ENOENT;
1651*4882a593Smuzhiyun
1652*4882a593Smuzhiyun found:
1653*4882a593Smuzhiyun *queue_p = &priv->tx_queue[idx];
1654*4882a593Smuzhiyun *tx_allowed_mask_p = tx_allowed_mask;
1655*4882a593Smuzhiyun return 0;
1656*4882a593Smuzhiyun }
1657*4882a593Smuzhiyun
wsm_get_tx(struct cw1200_common * priv,u8 ** data,size_t * tx_len,int * burst)1658*4882a593Smuzhiyun int wsm_get_tx(struct cw1200_common *priv, u8 **data,
1659*4882a593Smuzhiyun size_t *tx_len, int *burst)
1660*4882a593Smuzhiyun {
1661*4882a593Smuzhiyun struct wsm_tx *wsm = NULL;
1662*4882a593Smuzhiyun struct ieee80211_tx_info *tx_info;
1663*4882a593Smuzhiyun struct cw1200_queue *queue = NULL;
1664*4882a593Smuzhiyun int queue_num;
1665*4882a593Smuzhiyun u32 tx_allowed_mask = 0;
1666*4882a593Smuzhiyun const struct cw1200_txpriv *txpriv = NULL;
1667*4882a593Smuzhiyun int count = 0;
1668*4882a593Smuzhiyun
1669*4882a593Smuzhiyun /* More is used only for broadcasts. */
1670*4882a593Smuzhiyun bool more = false;
1671*4882a593Smuzhiyun
1672*4882a593Smuzhiyun if (priv->wsm_cmd.ptr) { /* CMD request */
1673*4882a593Smuzhiyun ++count;
1674*4882a593Smuzhiyun spin_lock(&priv->wsm_cmd.lock);
1675*4882a593Smuzhiyun BUG_ON(!priv->wsm_cmd.ptr);
1676*4882a593Smuzhiyun *data = priv->wsm_cmd.ptr;
1677*4882a593Smuzhiyun *tx_len = priv->wsm_cmd.len;
1678*4882a593Smuzhiyun *burst = 1;
1679*4882a593Smuzhiyun spin_unlock(&priv->wsm_cmd.lock);
1680*4882a593Smuzhiyun } else {
1681*4882a593Smuzhiyun for (;;) {
1682*4882a593Smuzhiyun int ret;
1683*4882a593Smuzhiyun
1684*4882a593Smuzhiyun if (atomic_add_return(0, &priv->tx_lock))
1685*4882a593Smuzhiyun break;
1686*4882a593Smuzhiyun
1687*4882a593Smuzhiyun spin_lock_bh(&priv->ps_state_lock);
1688*4882a593Smuzhiyun
1689*4882a593Smuzhiyun ret = wsm_get_tx_queue_and_mask(priv, &queue,
1690*4882a593Smuzhiyun &tx_allowed_mask, &more);
1691*4882a593Smuzhiyun queue_num = queue - priv->tx_queue;
1692*4882a593Smuzhiyun
1693*4882a593Smuzhiyun if (priv->buffered_multicasts &&
1694*4882a593Smuzhiyun (ret || !more) &&
1695*4882a593Smuzhiyun (priv->tx_multicast || !priv->sta_asleep_mask)) {
1696*4882a593Smuzhiyun priv->buffered_multicasts = false;
1697*4882a593Smuzhiyun if (priv->tx_multicast) {
1698*4882a593Smuzhiyun priv->tx_multicast = false;
1699*4882a593Smuzhiyun queue_work(priv->workqueue,
1700*4882a593Smuzhiyun &priv->multicast_stop_work);
1701*4882a593Smuzhiyun }
1702*4882a593Smuzhiyun }
1703*4882a593Smuzhiyun
1704*4882a593Smuzhiyun spin_unlock_bh(&priv->ps_state_lock);
1705*4882a593Smuzhiyun
1706*4882a593Smuzhiyun if (ret)
1707*4882a593Smuzhiyun break;
1708*4882a593Smuzhiyun
1709*4882a593Smuzhiyun if (cw1200_queue_get(queue,
1710*4882a593Smuzhiyun tx_allowed_mask,
1711*4882a593Smuzhiyun &wsm, &tx_info, &txpriv))
1712*4882a593Smuzhiyun continue;
1713*4882a593Smuzhiyun
1714*4882a593Smuzhiyun if (wsm_handle_tx_data(priv, wsm,
1715*4882a593Smuzhiyun tx_info, txpriv, queue))
1716*4882a593Smuzhiyun continue; /* Handled by WSM */
1717*4882a593Smuzhiyun
1718*4882a593Smuzhiyun wsm->hdr.id &= __cpu_to_le16(
1719*4882a593Smuzhiyun ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX));
1720*4882a593Smuzhiyun wsm->hdr.id |= cpu_to_le16(
1721*4882a593Smuzhiyun WSM_TX_LINK_ID(txpriv->raw_link_id));
1722*4882a593Smuzhiyun priv->pspoll_mask &= ~BIT(txpriv->raw_link_id);
1723*4882a593Smuzhiyun
1724*4882a593Smuzhiyun *data = (u8 *)wsm;
1725*4882a593Smuzhiyun *tx_len = __le16_to_cpu(wsm->hdr.len);
1726*4882a593Smuzhiyun
1727*4882a593Smuzhiyun /* allow bursting if txop is set */
1728*4882a593Smuzhiyun if (priv->edca.params[queue_num].txop_limit)
1729*4882a593Smuzhiyun *burst = min(*burst,
1730*4882a593Smuzhiyun (int)cw1200_queue_get_num_queued(queue, tx_allowed_mask) + 1);
1731*4882a593Smuzhiyun else
1732*4882a593Smuzhiyun *burst = 1;
1733*4882a593Smuzhiyun
1734*4882a593Smuzhiyun /* store index of bursting queue */
1735*4882a593Smuzhiyun if (*burst > 1)
1736*4882a593Smuzhiyun priv->tx_burst_idx = queue_num;
1737*4882a593Smuzhiyun else
1738*4882a593Smuzhiyun priv->tx_burst_idx = -1;
1739*4882a593Smuzhiyun
1740*4882a593Smuzhiyun if (more) {
1741*4882a593Smuzhiyun struct ieee80211_hdr *hdr =
1742*4882a593Smuzhiyun (struct ieee80211_hdr *)
1743*4882a593Smuzhiyun &((u8 *)wsm)[txpriv->offset];
1744*4882a593Smuzhiyun /* more buffered multicast/broadcast frames
1745*4882a593Smuzhiyun * ==> set MoreData flag in IEEE 802.11 header
1746*4882a593Smuzhiyun * to inform PS STAs
1747*4882a593Smuzhiyun */
1748*4882a593Smuzhiyun hdr->frame_control |=
1749*4882a593Smuzhiyun cpu_to_le16(IEEE80211_FCTL_MOREDATA);
1750*4882a593Smuzhiyun }
1751*4882a593Smuzhiyun
1752*4882a593Smuzhiyun pr_debug("[WSM] >>> 0x%.4X (%zu) %p %c\n",
1753*4882a593Smuzhiyun 0x0004, *tx_len, *data,
1754*4882a593Smuzhiyun wsm->more ? 'M' : ' ');
1755*4882a593Smuzhiyun ++count;
1756*4882a593Smuzhiyun break;
1757*4882a593Smuzhiyun }
1758*4882a593Smuzhiyun }
1759*4882a593Smuzhiyun
1760*4882a593Smuzhiyun return count;
1761*4882a593Smuzhiyun }
1762*4882a593Smuzhiyun
wsm_txed(struct cw1200_common * priv,u8 * data)1763*4882a593Smuzhiyun void wsm_txed(struct cw1200_common *priv, u8 *data)
1764*4882a593Smuzhiyun {
1765*4882a593Smuzhiyun if (data == priv->wsm_cmd.ptr) {
1766*4882a593Smuzhiyun spin_lock(&priv->wsm_cmd.lock);
1767*4882a593Smuzhiyun priv->wsm_cmd.ptr = NULL;
1768*4882a593Smuzhiyun spin_unlock(&priv->wsm_cmd.lock);
1769*4882a593Smuzhiyun }
1770*4882a593Smuzhiyun }
1771*4882a593Smuzhiyun
1772*4882a593Smuzhiyun /* ******************************************************************** */
1773*4882a593Smuzhiyun /* WSM buffer */
1774*4882a593Smuzhiyun
wsm_buf_init(struct wsm_buf * buf)1775*4882a593Smuzhiyun void wsm_buf_init(struct wsm_buf *buf)
1776*4882a593Smuzhiyun {
1777*4882a593Smuzhiyun BUG_ON(buf->begin);
1778*4882a593Smuzhiyun buf->begin = kmalloc(FWLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
1779*4882a593Smuzhiyun buf->end = buf->begin ? &buf->begin[FWLOAD_BLOCK_SIZE] : buf->begin;
1780*4882a593Smuzhiyun wsm_buf_reset(buf);
1781*4882a593Smuzhiyun }
1782*4882a593Smuzhiyun
wsm_buf_deinit(struct wsm_buf * buf)1783*4882a593Smuzhiyun void wsm_buf_deinit(struct wsm_buf *buf)
1784*4882a593Smuzhiyun {
1785*4882a593Smuzhiyun kfree(buf->begin);
1786*4882a593Smuzhiyun buf->begin = buf->data = buf->end = NULL;
1787*4882a593Smuzhiyun }
1788*4882a593Smuzhiyun
wsm_buf_reset(struct wsm_buf * buf)1789*4882a593Smuzhiyun static void wsm_buf_reset(struct wsm_buf *buf)
1790*4882a593Smuzhiyun {
1791*4882a593Smuzhiyun if (buf->begin) {
1792*4882a593Smuzhiyun buf->data = &buf->begin[4];
1793*4882a593Smuzhiyun *(u32 *)buf->begin = 0;
1794*4882a593Smuzhiyun } else {
1795*4882a593Smuzhiyun buf->data = buf->begin;
1796*4882a593Smuzhiyun }
1797*4882a593Smuzhiyun }
1798*4882a593Smuzhiyun
wsm_buf_reserve(struct wsm_buf * buf,size_t extra_size)1799*4882a593Smuzhiyun static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size)
1800*4882a593Smuzhiyun {
1801*4882a593Smuzhiyun size_t pos = buf->data - buf->begin;
1802*4882a593Smuzhiyun size_t size = pos + extra_size;
1803*4882a593Smuzhiyun u8 *tmp;
1804*4882a593Smuzhiyun
1805*4882a593Smuzhiyun size = round_up(size, FWLOAD_BLOCK_SIZE);
1806*4882a593Smuzhiyun
1807*4882a593Smuzhiyun tmp = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA);
1808*4882a593Smuzhiyun if (!tmp) {
1809*4882a593Smuzhiyun wsm_buf_deinit(buf);
1810*4882a593Smuzhiyun return -ENOMEM;
1811*4882a593Smuzhiyun }
1812*4882a593Smuzhiyun
1813*4882a593Smuzhiyun buf->begin = tmp;
1814*4882a593Smuzhiyun buf->data = &buf->begin[pos];
1815*4882a593Smuzhiyun buf->end = &buf->begin[size];
1816*4882a593Smuzhiyun return 0;
1817*4882a593Smuzhiyun }
1818