PageRenderTime 44ms CodeModel.GetById 26ms app.highlight 15ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/staging/brcm80211/brcmfmac/dhd_cdc.c

https://github.com/BlackMagicMushroom/linux
C | 502 lines | 356 code | 94 blank | 52 comment | 53 complexity | 076a9c808c873bbbcc8b8500aab90a0d MD5 | raw file
  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#include <linux/types.h>
 18#include <linux/netdevice.h>
 19#include <linux/sched.h>
 20#include <defs.h>
 21
 22#include <brcmu_utils.h>
 23#include <brcmu_wifi.h>
 24
 25#include "dhd.h"
 26#include "dhd_proto.h"
 27#include "dhd_bus.h"
 28#include "dhd_dbg.h"
 29
 30struct brcmf_proto_cdc_ioctl {
 31	u32 cmd;	/* ioctl command value */
 32	u32 len;	/* lower 16: output buflen;
 33			 * upper 16: input buflen (excludes header) */
 34	u32 flags;	/* flag defns given below */
 35	u32 status;	/* status code returned from the device */
 36};
 37
 38/* Max valid buffer size that can be sent to the dongle */
 39#define CDC_MAX_MSG_SIZE	(ETH_FRAME_LEN+ETH_FCS_LEN)
 40
 41/* CDC flag definitions */
 42#define CDCF_IOC_ERROR		0x01		/* 1=ioctl cmd failed */
 43#define CDCF_IOC_SET		0x02		/* 0=get, 1=set cmd */
 44#define CDCF_IOC_IF_MASK	0xF000		/* I/F index */
 45#define CDCF_IOC_IF_SHIFT	12
 46#define CDCF_IOC_ID_MASK	0xFFFF0000	/* id an ioctl pairing */
 47#define CDCF_IOC_ID_SHIFT	16		/* ID Mask shift bits */
 48#define CDC_IOC_ID(flags)	\
 49	(((flags) & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT)
 50#define CDC_SET_IF_IDX(hdr, idx) \
 51	((hdr)->flags = (((hdr)->flags & ~CDCF_IOC_IF_MASK) | \
 52	((idx) << CDCF_IOC_IF_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
 59#define BDC_PROTO_VER		1	/* Protocol version */
 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;
 78	u8 rssi;
 79};
 80
 81
 82#define RETRIES 2	/* # of retries to retrieve matching ioctl response */
 83#define BUS_HEADER_LEN	(16+BRCMF_SDALIGN) /* Must be atleast SDPCM_RESERVE
 84					 * (amount of header tha might be added)
 85					 * plus any space that might be needed
 86					 * for alignment padding.
 87					 */
 88#define ROUND_UP_MARGIN	2048	/* Biggest SDIO block size possible for
 89				 * round off at the end of buffer
 90				 */
 91
 92struct brcmf_proto {
 93	u16 reqid;
 94	u8 pending;
 95	u32 lastcmd;
 96	u8 bus_header[BUS_HEADER_LEN];
 97	struct brcmf_proto_cdc_ioctl msg;
 98	unsigned char buf[BRCMF_C_IOCTL_MAXLEN + ROUND_UP_MARGIN];
 99};
100
101static int brcmf_proto_cdc_msg(struct brcmf_pub *drvr)
102{
103	struct brcmf_proto *prot = drvr->prot;
104	int len = le32_to_cpu(prot->msg.len) +
105			sizeof(struct brcmf_proto_cdc_ioctl);
106
107	BRCMF_TRACE(("%s: Enter\n", __func__));
108
109	/* NOTE : cdc->msg.len holds the desired length of the buffer to be
110	 *        returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
111	 *        is actually sent to the dongle
112	 */
113	if (len > CDC_MAX_MSG_SIZE)
114		len = CDC_MAX_MSG_SIZE;
115
116	/* Send request */
117	return brcmf_sdbrcm_bus_txctl(drvr->bus, (unsigned char *)&prot->msg,
118				      len);
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_TRACE(("%s: Enter\n", __func__));
127
128	do {
129		ret = brcmf_sdbrcm_bus_rxctl(drvr->bus,
130				(unsigned char *)&prot->msg,
131				len + sizeof(struct brcmf_proto_cdc_ioctl));
132		if (ret < 0)
133			break;
134	} while (CDC_IOC_ID(le32_to_cpu(prot->msg.flags)) != id);
135
136	return ret;
137}
138
139int
140brcmf_proto_cdc_query_ioctl(struct brcmf_pub *drvr, int ifidx, uint cmd,
141			    void *buf, uint len)
142{
143	struct brcmf_proto *prot = drvr->prot;
144	struct brcmf_proto_cdc_ioctl *msg = &prot->msg;
145	void *info;
146	int ret = 0, retries = 0;
147	u32 id, flags = 0;
148
149	BRCMF_TRACE(("%s: Enter\n", __func__));
150	BRCMF_CTL(("%s: cmd %d len %d\n", __func__, cmd, len));
151
152	/* Respond "bcmerror" and "bcmerrorstr" with local cache */
153	if (cmd == BRCMF_C_GET_VAR && buf) {
154		if (!strcmp((char *)buf, "bcmerrorstr")) {
155			strncpy((char *)buf, "bcm_error",
156				BCME_STRLEN);
157			goto done;
158		} else if (!strcmp((char *)buf, "bcmerror")) {
159			*(int *)buf = drvr->dongle_error;
160			goto done;
161		}
162	}
163
164	memset(msg, 0, sizeof(struct brcmf_proto_cdc_ioctl));
165
166	msg->cmd = cpu_to_le32(cmd);
167	msg->len = cpu_to_le32(len);
168	msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
169	CDC_SET_IF_IDX(msg, ifidx);
170	msg->flags = cpu_to_le32(msg->flags);
171
172	if (buf)
173		memcpy(prot->buf, buf, len);
174
175	ret = brcmf_proto_cdc_msg(drvr);
176	if (ret < 0) {
177		BRCMF_ERROR(("brcmf_proto_cdc_query_ioctl: brcmf_proto_cdc_msg "
178			     "failed w/status %d\n", 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 & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
190
191	if ((id < prot->reqid) && (++retries < RETRIES))
192		goto retry;
193	if (id != prot->reqid) {
194		BRCMF_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
195			     brcmf_ifname(drvr, ifidx), __func__, id,
196			     prot->reqid));
197		ret = -EINVAL;
198		goto done;
199	}
200
201	/* Check info buffer */
202	info = (void *)&msg[1];
203
204	/* Copy info buffer */
205	if (buf) {
206		if (ret < (int)len)
207			len = ret;
208		memcpy(buf, info, len);
209	}
210
211	/* Check the ERROR flag */
212	if (flags & CDCF_IOC_ERROR) {
213		ret = le32_to_cpu(msg->status);
214		/* Cache error from dongle */
215		drvr->dongle_error = ret;
216	}
217
218done:
219	return ret;
220}
221
222int brcmf_proto_cdc_set_ioctl(struct brcmf_pub *drvr, int ifidx, uint cmd,
223			      void *buf, uint len)
224{
225	struct brcmf_proto *prot = drvr->prot;
226	struct brcmf_proto_cdc_ioctl *msg = &prot->msg;
227	int ret = 0;
228	u32 flags, id;
229
230	BRCMF_TRACE(("%s: Enter\n", __func__));
231	BRCMF_CTL(("%s: cmd %d len %d\n", __func__, cmd, len));
232
233	memset(msg, 0, sizeof(struct brcmf_proto_cdc_ioctl));
234
235	msg->cmd = cpu_to_le32(cmd);
236	msg->len = cpu_to_le32(len);
237	msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT) | CDCF_IOC_SET;
238	CDC_SET_IF_IDX(msg, ifidx);
239	msg->flags = cpu_to_le32(msg->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 & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
254
255	if (id != prot->reqid) {
256		BRCMF_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
257			     brcmf_ifname(drvr, ifidx), __func__, id,
258			     prot->reqid));
259		ret = -EINVAL;
260		goto done;
261	}
262
263	/* Check the ERROR flag */
264	if (flags & CDCF_IOC_ERROR) {
265		ret = le32_to_cpu(msg->status);
266		/* Cache error from dongle */
267		drvr->dongle_error = ret;
268	}
269
270done:
271	return ret;
272}
273
274int
275brcmf_proto_ioctl(struct brcmf_pub *drvr, int ifidx, struct brcmf_ioctl *ioc,
276		  void *buf, int len)
277{
278	struct brcmf_proto *prot = drvr->prot;
279	int ret = -1;
280
281	if (drvr->busstate == BRCMF_BUS_DOWN) {
282		BRCMF_ERROR(("%s : bus is down. we have nothing to do\n",
283			     __func__));
284		return ret;
285	}
286	brcmf_os_proto_block(drvr);
287
288	BRCMF_TRACE(("%s: Enter\n", __func__));
289
290	if (len > BRCMF_C_IOCTL_MAXLEN)
291		goto done;
292
293	if (prot->pending == true) {
294		BRCMF_TRACE(("CDC packet is pending!!!! cmd=0x%x (%lu) "
295			     "lastcmd=0x%x (%lu)\n",
296			     ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd,
297			     (unsigned long)prot->lastcmd));
298		if ((ioc->cmd == BRCMF_C_SET_VAR) ||
299		    (ioc->cmd == BRCMF_C_GET_VAR))
300			BRCMF_TRACE(("iovar cmd=%s\n", (char *)buf));
301
302		goto done;
303	}
304
305	prot->pending = true;
306	prot->lastcmd = ioc->cmd;
307	if (ioc->set)
308		ret = brcmf_proto_cdc_set_ioctl(drvr, ifidx, ioc->cmd,
309						buf, len);
310	else {
311		ret = brcmf_proto_cdc_query_ioctl(drvr, ifidx, ioc->cmd,
312						  buf, len);
313		if (ret > 0)
314			ioc->used = ret - sizeof(struct brcmf_proto_cdc_ioctl);
315	}
316
317	/* Too many programs assume ioctl() returns 0 on success */
318	if (ret >= 0)
319		ret = 0;
320	else {
321		struct brcmf_proto_cdc_ioctl *msg = &prot->msg;
322		/* len == needed when set/query fails from dongle */
323		ioc->needed = le32_to_cpu(msg->len);
324	}
325
326	/* Intercept the wme_dp ioctl here */
327	if (!ret && ioc->cmd == BRCMF_C_SET_VAR &&
328	    !strcmp(buf, "wme_dp")) {
329		int slen, val = 0;
330
331		slen = strlen("wme_dp") + 1;
332		if (len >= (int)(slen + sizeof(int)))
333			memcpy(&val, (char *)buf + slen, sizeof(int));
334		drvr->wme_dp = (u8) le32_to_cpu(val);
335	}
336
337	prot->pending = false;
338
339done:
340	brcmf_os_proto_unblock(drvr);
341
342	return ret;
343}
344
345#define PKTSUMNEEDED(skb) \
346		(((struct sk_buff *)(skb))->ip_summed == CHECKSUM_PARTIAL)
347#define PKTSETSUMGOOD(skb, x) \
348		(((struct sk_buff *)(skb))->ip_summed = \
349		((x) ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE))
350
351void brcmf_proto_dump(struct brcmf_pub *drvr, struct brcmu_strbuf *strbuf)
352{
353	brcmu_bprintf(strbuf, "Protocol CDC: reqid %d\n", drvr->prot->reqid);
354}
355
356void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx,
357			 struct sk_buff *pktbuf)
358{
359	struct brcmf_proto_bdc_header *h;
360
361	BRCMF_TRACE(("%s: Enter\n", __func__));
362
363	/* Push BDC header used to convey priority for buses that don't */
364
365	skb_push(pktbuf, BDC_HEADER_LEN);
366
367	h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
368
369	h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
370	if (PKTSUMNEEDED(pktbuf))
371		h->flags |= BDC_FLAG_SUM_NEEDED;
372
373	h->priority = (pktbuf->priority & BDC_PRIORITY_MASK);
374	h->flags2 = 0;
375	h->rssi = 0;
376	BDC_SET_IF_IDX(h, ifidx);
377}
378
379int brcmf_proto_hdrpull(struct brcmf_pub *drvr, int *ifidx,
380			struct sk_buff *pktbuf)
381{
382	struct brcmf_proto_bdc_header *h;
383
384	BRCMF_TRACE(("%s: Enter\n", __func__));
385
386	/* Pop BDC header used to convey priority for buses that don't */
387
388	if (pktbuf->len < BDC_HEADER_LEN) {
389		BRCMF_ERROR(("%s: rx data too short (%d < %d)\n", __func__,
390			     pktbuf->len, BDC_HEADER_LEN));
391		return -EBADE;
392	}
393
394	h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
395
396	*ifidx = BDC_GET_IF_IDX(h);
397	if (*ifidx >= BRCMF_MAX_IFS) {
398		BRCMF_ERROR(("%s: rx data ifnum out of range (%d)\n",
399			     __func__, *ifidx));
400		return -EBADE;
401	}
402
403	if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) !=
404	    BDC_PROTO_VER) {
405		BRCMF_ERROR(("%s: non-BDC packet received, flags 0x%x\n",
406			     brcmf_ifname(drvr, *ifidx), h->flags));
407		return -EBADE;
408	}
409
410	if (h->flags & BDC_FLAG_SUM_GOOD) {
411		BRCMF_INFO(("%s: BDC packet received with good rx-csum, "
412			    "flags 0x%x\n",
413			    brcmf_ifname(drvr, *ifidx), h->flags));
414		PKTSETSUMGOOD(pktbuf, true);
415	}
416
417	pktbuf->priority = h->priority & BDC_PRIORITY_MASK;
418
419	skb_pull(pktbuf, BDC_HEADER_LEN);
420
421	return 0;
422}
423
424int brcmf_proto_attach(struct brcmf_pub *drvr)
425{
426	struct brcmf_proto *cdc;
427
428	cdc = kzalloc(sizeof(struct brcmf_proto), GFP_ATOMIC);
429	if (!cdc) {
430		BRCMF_ERROR(("%s: kmalloc failed\n", __func__));
431		goto fail;
432	}
433
434	/* ensure that the msg buf directly follows the cdc msg struct */
435	if ((unsigned long)(&cdc->msg + 1) != (unsigned long)cdc->buf) {
436		BRCMF_ERROR(("struct brcmf_proto is not correctly defined\n"));
437		goto fail;
438	}
439
440	drvr->prot = cdc;
441	drvr->hdrlen += BDC_HEADER_LEN;
442	drvr->maxctl = BRCMF_C_IOCTL_MAXLEN +
443			sizeof(struct brcmf_proto_cdc_ioctl) + ROUND_UP_MARGIN;
444	return 0;
445
446fail:
447	kfree(cdc);
448	return -ENOMEM;
449}
450
451/* ~NOTE~ What if another thread is waiting on the semaphore?  Holding it? */
452void brcmf_proto_detach(struct brcmf_pub *drvr)
453{
454	kfree(drvr->prot);
455	drvr->prot = NULL;
456}
457
458void brcmf_proto_dstats(struct brcmf_pub *drvr)
459{
460	/* No stats from dongle added yet, copy bus stats */
461	drvr->dstats.tx_packets = drvr->tx_packets;
462	drvr->dstats.tx_errors = drvr->tx_errors;
463	drvr->dstats.rx_packets = drvr->rx_packets;
464	drvr->dstats.rx_errors = drvr->rx_errors;
465	drvr->dstats.rx_dropped = drvr->rx_dropped;
466	drvr->dstats.multicast = drvr->rx_multicast;
467	return;
468}
469
470int brcmf_proto_init(struct brcmf_pub *drvr)
471{
472	int ret = 0;
473	char buf[128];
474
475	BRCMF_TRACE(("%s: Enter\n", __func__));
476
477	brcmf_os_proto_block(drvr);
478
479	/* Get the device MAC address */
480	strcpy(buf, "cur_etheraddr");
481	ret = brcmf_proto_cdc_query_ioctl(drvr, 0, BRCMF_C_GET_VAR,
482					  buf, sizeof(buf));
483	if (ret < 0) {
484		brcmf_os_proto_unblock(drvr);
485		return ret;
486	}
487	memcpy(drvr->mac, buf, ETH_ALEN);
488
489	brcmf_os_proto_unblock(drvr);
490
491	ret = brcmf_c_preinit_ioctls(drvr);
492
493	/* Always assumes wl for now */
494	drvr->iswl = true;
495
496	return ret;
497}
498
499void brcmf_proto_stop(struct brcmf_pub *drvr)
500{
501	/* Nothing to do for CDC */
502}