brcmfmac: remove rx helper function from bus interface
[deliverable/linux.git] / drivers / net / wireless / brcm80211 / brcmfmac / dhd_cdc.c
CommitLineData
5b435de0
AS
1/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/*******************************************************************************
18 * Communicates with the dongle by using dcmd codes.
19 * For certain dcmd codes, the dongle interprets string data from the host.
20 ******************************************************************************/
21
22#include <linux/types.h>
23#include <linux/netdevice.h>
5b435de0
AS
24
25#include <brcmu_utils.h>
26#include <brcmu_wifi.h>
27
28#include "dhd.h"
29#include "dhd_proto.h"
30#include "dhd_bus.h"
31#include "dhd_dbg.h"
32
33struct brcmf_proto_cdc_dcmd {
34 __le32 cmd; /* dongle command value */
35 __le32 len; /* lower 16: output buflen;
36 * upper 16: input buflen (excludes header) */
37 __le32 flags; /* flag defns given below */
38 __le32 status; /* status code returned from the device */
39};
40
41/* Max valid buffer size that can be sent to the dongle */
42#define CDC_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN)
43
44/* CDC flag definitions */
45#define CDC_DCMD_ERROR 0x01 /* 1=cmd failed */
46#define CDC_DCMD_SET 0x02 /* 0=get, 1=set cmd */
47#define CDC_DCMD_IF_MASK 0xF000 /* I/F index */
48#define CDC_DCMD_IF_SHIFT 12
49#define CDC_DCMD_ID_MASK 0xFFFF0000 /* id an cmd pairing */
50#define CDC_DCMD_ID_SHIFT 16 /* ID Mask shift bits */
51#define CDC_DCMD_ID(flags) \
52 (((flags) & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT)
53
54/*
55 * BDC header - Broadcom specific extension of CDC.
56 * Used on data packets to convey priority across USB.
57 */
58#define BDC_HEADER_LEN 4
e40aed06 59#define BDC_PROTO_VER 2 /* Protocol version */
5b435de0
AS
60#define BDC_FLAG_VER_MASK 0xf0 /* Protocol version mask */
61#define BDC_FLAG_VER_SHIFT 4 /* Protocol version shift */
62#define BDC_FLAG_SUM_GOOD 0x04 /* Good RX checksums */
63#define BDC_FLAG_SUM_NEEDED 0x08 /* Dongle needs to do TX checksums */
64#define BDC_PRIORITY_MASK 0x7
65#define BDC_FLAG2_IF_MASK 0x0f /* packet rx interface in APSTA */
66#define BDC_FLAG2_IF_SHIFT 0
67
68#define BDC_GET_IF_IDX(hdr) \
69 ((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT))
70#define BDC_SET_IF_IDX(hdr, idx) \
71 ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | \
72 ((idx) << BDC_FLAG2_IF_SHIFT)))
73
74struct brcmf_proto_bdc_header {
75 u8 flags;
76 u8 priority; /* 802.1d Priority, 4:7 flow control info for usb */
77 u8 flags2;
e40aed06 78 u8 data_offset;
5b435de0
AS
79};
80
81
82#define RETRIES 2 /* # of retries to retrieve matching dcmd response */
6e3c7128 83#define BUS_HEADER_LEN (16+64) /* Must be atleast SDPCM_RESERVE
5b435de0
AS
84 * (amount of header tha might be added)
85 * plus any space that might be needed
6e3c7128 86 * for bus alignment padding.
5b435de0 87 */
6e3c7128 88#define ROUND_UP_MARGIN 2048 /* Biggest bus block size possible for
5b435de0 89 * round off at the end of buffer
6e3c7128 90 * Currently is SDIO
5b435de0
AS
91 */
92
93struct brcmf_proto {
94 u16 reqid;
95 u8 pending;
96 u32 lastcmd;
97 u8 bus_header[BUS_HEADER_LEN];
98 struct brcmf_proto_cdc_dcmd msg;
99 unsigned char buf[BRCMF_DCMD_MAXLEN + ROUND_UP_MARGIN];
100};
101
102static int brcmf_proto_cdc_msg(struct brcmf_pub *drvr)
103{
104 struct brcmf_proto *prot = drvr->prot;
105 int len = le32_to_cpu(prot->msg.len) +
106 sizeof(struct brcmf_proto_cdc_dcmd);
107
108 brcmf_dbg(TRACE, "Enter\n");
109
110 /* NOTE : cdc->msg.len holds the desired length of the buffer to be
111 * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
112 * is actually sent to the dongle
113 */
114 if (len > CDC_MAX_MSG_SIZE)
115 len = CDC_MAX_MSG_SIZE;
116
117 /* Send request */
d9cb2596 118 return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&prot->msg, len);
5b435de0
AS
119}
120
121static int brcmf_proto_cdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
122{
123 int ret;
124 struct brcmf_proto *prot = drvr->prot;
125
126 brcmf_dbg(TRACE, "Enter\n");
d9cb2596 127 len += sizeof(struct brcmf_proto_cdc_dcmd);
5b435de0 128 do {
d9cb2596
AS
129 ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&prot->msg,
130 len);
5b435de0
AS
131 if (ret < 0)
132 break;
133 } while (CDC_DCMD_ID(le32_to_cpu(prot->msg.flags)) != id);
134
135 return ret;
136}
137
138int
139brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
140 void *buf, uint len)
141{
142 struct brcmf_proto *prot = drvr->prot;
143 struct brcmf_proto_cdc_dcmd *msg = &prot->msg;
144 void *info;
145 int ret = 0, retries = 0;
146 u32 id, flags;
147
148 brcmf_dbg(TRACE, "Enter\n");
149 brcmf_dbg(CTL, "cmd %d len %d\n", cmd, len);
150
151 /* Respond "bcmerror" and "bcmerrorstr" with local cache */
152 if (cmd == BRCMF_C_GET_VAR && buf) {
153 if (!strcmp((char *)buf, "bcmerrorstr")) {
154 strncpy((char *)buf, "bcm_error",
155 BCME_STRLEN);
156 goto done;
157 } else if (!strcmp((char *)buf, "bcmerror")) {
158 *(int *)buf = drvr->dongle_error;
159 goto done;
160 }
161 }
162
163 memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd));
164
165 msg->cmd = cpu_to_le32(cmd);
166 msg->len = cpu_to_le32(len);
167 flags = (++prot->reqid << CDC_DCMD_ID_SHIFT);
168 flags = (flags & ~CDC_DCMD_IF_MASK) |
169 (ifidx << CDC_DCMD_IF_SHIFT);
170 msg->flags = cpu_to_le32(flags);
171
172 if (buf)
173 memcpy(prot->buf, buf, len);
174
175 ret = brcmf_proto_cdc_msg(drvr);
176 if (ret < 0) {
5e8149f5 177 brcmf_err("brcmf_proto_cdc_msg failed w/status %d\n",
5b435de0
AS
178 ret);
179 goto done;
180 }
181
182retry:
183 /* wait for interrupt and get first fragment */
184 ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len);
185 if (ret < 0)
186 goto done;
187
188 flags = le32_to_cpu(msg->flags);
189 id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT;
190
191 if ((id < prot->reqid) && (++retries < RETRIES))
192 goto retry;
193 if (id != prot->reqid) {
5e8149f5 194 brcmf_err("%s: unexpected request id %d (expected %d)\n",
5b435de0
AS
195 brcmf_ifname(drvr, ifidx), id, prot->reqid);
196 ret = -EINVAL;
197 goto done;
198 }
199
200 /* Check info buffer */
201 info = (void *)&msg[1];
202
203 /* Copy info buffer */
204 if (buf) {
205 if (ret < (int)len)
206 len = ret;
207 memcpy(buf, info, len);
208 }
209
210 /* Check the ERROR flag */
211 if (flags & CDC_DCMD_ERROR) {
212 ret = le32_to_cpu(msg->status);
213 /* Cache error from dongle */
214 drvr->dongle_error = ret;
215 }
216
217done:
218 return ret;
219}
220
221int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
222 void *buf, uint len)
223{
224 struct brcmf_proto *prot = drvr->prot;
225 struct brcmf_proto_cdc_dcmd *msg = &prot->msg;
226 int ret = 0;
227 u32 flags, id;
228
229 brcmf_dbg(TRACE, "Enter\n");
230 brcmf_dbg(CTL, "cmd %d len %d\n", cmd, len);
231
232 memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd));
233
234 msg->cmd = cpu_to_le32(cmd);
235 msg->len = cpu_to_le32(len);
236 flags = (++prot->reqid << CDC_DCMD_ID_SHIFT) | CDC_DCMD_SET;
237 flags = (flags & ~CDC_DCMD_IF_MASK) |
238 (ifidx << CDC_DCMD_IF_SHIFT);
239 msg->flags = cpu_to_le32(flags);
240
241 if (buf)
242 memcpy(prot->buf, buf, len);
243
244 ret = brcmf_proto_cdc_msg(drvr);
245 if (ret < 0)
246 goto done;
247
248 ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len);
249 if (ret < 0)
250 goto done;
251
252 flags = le32_to_cpu(msg->flags);
253 id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT;
254
255 if (id != prot->reqid) {
5e8149f5 256 brcmf_err("%s: unexpected request id %d (expected %d)\n",
5b435de0
AS
257 brcmf_ifname(drvr, ifidx), id, prot->reqid);
258 ret = -EINVAL;
259 goto done;
260 }
261
262 /* Check the ERROR flag */
263 if (flags & CDC_DCMD_ERROR) {
264 ret = le32_to_cpu(msg->status);
265 /* Cache error from dongle */
266 drvr->dongle_error = ret;
267 }
268
269done:
270 return ret;
271}
272
5b435de0
AS
273static bool pkt_sum_needed(struct sk_buff *skb)
274{
275 return skb->ip_summed == CHECKSUM_PARTIAL;
276}
277
278static void pkt_set_sum_good(struct sk_buff *skb, bool x)
279{
280 skb->ip_summed = (x ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE);
281}
282
283void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx,
284 struct sk_buff *pktbuf)
285{
286 struct brcmf_proto_bdc_header *h;
287
288 brcmf_dbg(TRACE, "Enter\n");
289
290 /* Push BDC header used to convey priority for buses that don't */
291
292 skb_push(pktbuf, BDC_HEADER_LEN);
293
294 h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
295
296 h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
297 if (pkt_sum_needed(pktbuf))
298 h->flags |= BDC_FLAG_SUM_NEEDED;
299
300 h->priority = (pktbuf->priority & BDC_PRIORITY_MASK);
301 h->flags2 = 0;
e40aed06 302 h->data_offset = 0;
5b435de0
AS
303 BDC_SET_IF_IDX(h, ifidx);
304}
305
d5625ee6 306int brcmf_proto_hdrpull(struct device *dev, int *ifidx,
5b435de0
AS
307 struct sk_buff *pktbuf)
308{
309 struct brcmf_proto_bdc_header *h;
d5625ee6
FL
310 struct brcmf_bus *bus_if = dev_get_drvdata(dev);
311 struct brcmf_pub *drvr = bus_if->drvr;
5b435de0
AS
312
313 brcmf_dbg(TRACE, "Enter\n");
314
315 /* Pop BDC header used to convey priority for buses that don't */
316
317 if (pktbuf->len < BDC_HEADER_LEN) {
5e8149f5 318 brcmf_err("rx data too short (%d < %d)\n",
5b435de0
AS
319 pktbuf->len, BDC_HEADER_LEN);
320 return -EBADE;
321 }
322
323 h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
324
325 *ifidx = BDC_GET_IF_IDX(h);
326 if (*ifidx >= BRCMF_MAX_IFS) {
5e8149f5 327 brcmf_err("rx data ifnum out of range (%d)\n", *ifidx);
5b435de0
AS
328 return -EBADE;
329 }
330
331 if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) !=
332 BDC_PROTO_VER) {
5e8149f5 333 brcmf_err("%s: non-BDC packet received, flags 0x%x\n",
5b435de0
AS
334 brcmf_ifname(drvr, *ifidx), h->flags);
335 return -EBADE;
336 }
337
338 if (h->flags & BDC_FLAG_SUM_GOOD) {
339 brcmf_dbg(INFO, "%s: BDC packet received with good rx-csum, flags 0x%x\n",
340 brcmf_ifname(drvr, *ifidx), h->flags);
341 pkt_set_sum_good(pktbuf, true);
342 }
343
344 pktbuf->priority = h->priority & BDC_PRIORITY_MASK;
345
346 skb_pull(pktbuf, BDC_HEADER_LEN);
8f1ab44d 347 skb_pull(pktbuf, h->data_offset << 2);
5b435de0
AS
348
349 return 0;
350}
351
352int brcmf_proto_attach(struct brcmf_pub *drvr)
353{
354 struct brcmf_proto *cdc;
355
356 cdc = kzalloc(sizeof(struct brcmf_proto), GFP_ATOMIC);
357 if (!cdc)
358 goto fail;
359
360 /* ensure that the msg buf directly follows the cdc msg struct */
361 if ((unsigned long)(&cdc->msg + 1) != (unsigned long)cdc->buf) {
5e8149f5 362 brcmf_err("struct brcmf_proto is not correctly defined\n");
5b435de0
AS
363 goto fail;
364 }
365
366 drvr->prot = cdc;
367 drvr->hdrlen += BDC_HEADER_LEN;
b01a6b3c 368 drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN +
5b435de0
AS
369 sizeof(struct brcmf_proto_cdc_dcmd) + ROUND_UP_MARGIN;
370 return 0;
371
372fail:
373 kfree(cdc);
374 return -ENOMEM;
375}
376
377/* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */
378void brcmf_proto_detach(struct brcmf_pub *drvr)
379{
380 kfree(drvr->prot);
381 drvr->prot = NULL;
382}
383
5b435de0
AS
384void brcmf_proto_stop(struct brcmf_pub *drvr)
385{
386 /* Nothing to do for CDC */
387}
This page took 0.153583 seconds and 5 git commands to generate.