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>
24 #include <linux/sched.h>
25 #include <defs.h>
26 
27 #include <brcmu_utils.h>
28 #include <brcmu_wifi.h>
29 
30 #include "dhd.h"
31 #include "dhd_proto.h"
32 #include "dhd_bus.h"
33 #include "dhd_dbg.h"
34 
35 struct brcmf_proto_cdc_dcmd {
36 	__le32 cmd;	/* dongle command value */
37 	__le32 len;	/* lower 16: output buflen;
38 			 * upper 16: input buflen (excludes header) */
39 	__le32 flags;	/* flag defns given below */
40 	__le32 status;	/* status code returned from the device */
41 };
42 
43 /* Max valid buffer size that can be sent to the dongle */
44 #define CDC_MAX_MSG_SIZE	(ETH_FRAME_LEN+ETH_FCS_LEN)
45 
46 /* CDC flag definitions */
47 #define CDC_DCMD_ERROR		0x01	/* 1=cmd failed */
48 #define CDC_DCMD_SET		0x02	/* 0=get, 1=set cmd */
49 #define CDC_DCMD_IF_MASK	0xF000		/* I/F index */
50 #define CDC_DCMD_IF_SHIFT	12
51 #define CDC_DCMD_ID_MASK	0xFFFF0000	/* id an cmd pairing */
52 #define CDC_DCMD_ID_SHIFT	16		/* ID Mask shift bits */
53 #define CDC_DCMD_ID(flags)	\
54 	(((flags) & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT)
55 
56 /*
57  * BDC header - Broadcom specific extension of CDC.
58  * Used on data packets to convey priority across USB.
59  */
60 #define	BDC_HEADER_LEN		4
61 #define BDC_PROTO_VER		2	/* Protocol version */
62 #define BDC_FLAG_VER_MASK	0xf0	/* Protocol version mask */
63 #define BDC_FLAG_VER_SHIFT	4	/* Protocol version shift */
64 #define BDC_FLAG_SUM_GOOD	0x04	/* Good RX checksums */
65 #define BDC_FLAG_SUM_NEEDED	0x08	/* Dongle needs to do TX checksums */
66 #define BDC_PRIORITY_MASK	0x7
67 #define BDC_FLAG2_IF_MASK	0x0f	/* packet rx interface in APSTA */
68 #define BDC_FLAG2_IF_SHIFT	0
69 
70 #define BDC_GET_IF_IDX(hdr) \
71 	((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT))
72 #define BDC_SET_IF_IDX(hdr, idx) \
73 	((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | \
74 	((idx) << BDC_FLAG2_IF_SHIFT)))
75 
76 struct brcmf_proto_bdc_header {
77 	u8 flags;
78 	u8 priority;	/* 802.1d Priority, 4:7 flow control info for usb */
79 	u8 flags2;
80 	u8 data_offset;
81 };
82 
83 
84 #define RETRIES 2 /* # of retries to retrieve matching dcmd response */
85 #define BUS_HEADER_LEN	(16+64)		/* Must be atleast SDPCM_RESERVE
86 					 * (amount of header tha might be added)
87 					 * plus any space that might be needed
88 					 * for bus alignment padding.
89 					 */
90 #define ROUND_UP_MARGIN	2048	/* Biggest bus block size possible for
91 				 * round off at the end of buffer
92 				 * Currently is SDIO
93 				 */
94 
95 struct brcmf_proto {
96 	u16 reqid;
97 	u8 pending;
98 	u32 lastcmd;
99 	u8 bus_header[BUS_HEADER_LEN];
100 	struct brcmf_proto_cdc_dcmd msg;
101 	unsigned char buf[BRCMF_DCMD_MAXLEN + ROUND_UP_MARGIN];
102 };
103 
brcmf_proto_cdc_msg(struct brcmf_pub * drvr)104 static int brcmf_proto_cdc_msg(struct brcmf_pub *drvr)
105 {
106 	struct brcmf_proto *prot = drvr->prot;
107 	int len = le32_to_cpu(prot->msg.len) +
108 			sizeof(struct brcmf_proto_cdc_dcmd);
109 
110 	brcmf_dbg(TRACE, "Enter\n");
111 
112 	/* NOTE : cdc->msg.len holds the desired length of the buffer to be
113 	 *        returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
114 	 *        is actually sent to the dongle
115 	 */
116 	if (len > CDC_MAX_MSG_SIZE)
117 		len = CDC_MAX_MSG_SIZE;
118 
119 	/* Send request */
120 	return drvr->bus_if->brcmf_bus_txctl(drvr->dev,
121 					     (unsigned char *)&prot->msg,
122 					     len);
123 }
124 
brcmf_proto_cdc_cmplt(struct brcmf_pub * drvr,u32 id,u32 len)125 static int brcmf_proto_cdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
126 {
127 	int ret;
128 	struct brcmf_proto *prot = drvr->prot;
129 
130 	brcmf_dbg(TRACE, "Enter\n");
131 
132 	do {
133 		ret = drvr->bus_if->brcmf_bus_rxctl(drvr->dev,
134 				(unsigned char *)&prot->msg,
135 				len + sizeof(struct brcmf_proto_cdc_dcmd));
136 		if (ret < 0)
137 			break;
138 	} while (CDC_DCMD_ID(le32_to_cpu(prot->msg.flags)) != id);
139 
140 	return ret;
141 }
142 
143 int
brcmf_proto_cdc_query_dcmd(struct brcmf_pub * drvr,int ifidx,uint cmd,void * buf,uint len)144 brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
145 			       void *buf, uint len)
146 {
147 	struct brcmf_proto *prot = drvr->prot;
148 	struct brcmf_proto_cdc_dcmd *msg = &prot->msg;
149 	void *info;
150 	int ret = 0, retries = 0;
151 	u32 id, flags;
152 
153 	brcmf_dbg(TRACE, "Enter\n");
154 	brcmf_dbg(CTL, "cmd %d len %d\n", cmd, len);
155 
156 	/* Respond "bcmerror" and "bcmerrorstr" with local cache */
157 	if (cmd == BRCMF_C_GET_VAR && buf) {
158 		if (!strcmp((char *)buf, "bcmerrorstr")) {
159 			strncpy((char *)buf, "bcm_error",
160 				BCME_STRLEN);
161 			goto done;
162 		} else if (!strcmp((char *)buf, "bcmerror")) {
163 			*(int *)buf = drvr->dongle_error;
164 			goto done;
165 		}
166 	}
167 
168 	memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd));
169 
170 	msg->cmd = cpu_to_le32(cmd);
171 	msg->len = cpu_to_le32(len);
172 	flags = (++prot->reqid << CDC_DCMD_ID_SHIFT);
173 	flags = (flags & ~CDC_DCMD_IF_MASK) |
174 		(ifidx << CDC_DCMD_IF_SHIFT);
175 	msg->flags = cpu_to_le32(flags);
176 
177 	if (buf)
178 		memcpy(prot->buf, buf, len);
179 
180 	ret = brcmf_proto_cdc_msg(drvr);
181 	if (ret < 0) {
182 		brcmf_dbg(ERROR, "brcmf_proto_cdc_msg failed w/status %d\n",
183 			  ret);
184 		goto done;
185 	}
186 
187 retry:
188 	/* wait for interrupt and get first fragment */
189 	ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len);
190 	if (ret < 0)
191 		goto done;
192 
193 	flags = le32_to_cpu(msg->flags);
194 	id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT;
195 
196 	if ((id < prot->reqid) && (++retries < RETRIES))
197 		goto retry;
198 	if (id != prot->reqid) {
199 		brcmf_dbg(ERROR, "%s: unexpected request id %d (expected %d)\n",
200 			  brcmf_ifname(drvr, ifidx), id, prot->reqid);
201 		ret = -EINVAL;
202 		goto done;
203 	}
204 
205 	/* Check info buffer */
206 	info = (void *)&msg[1];
207 
208 	/* Copy info buffer */
209 	if (buf) {
210 		if (ret < (int)len)
211 			len = ret;
212 		memcpy(buf, info, len);
213 	}
214 
215 	/* Check the ERROR flag */
216 	if (flags & CDC_DCMD_ERROR) {
217 		ret = le32_to_cpu(msg->status);
218 		/* Cache error from dongle */
219 		drvr->dongle_error = ret;
220 	}
221 
222 done:
223 	return ret;
224 }
225 
brcmf_proto_cdc_set_dcmd(struct brcmf_pub * drvr,int ifidx,uint cmd,void * buf,uint len)226 int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
227 				 void *buf, uint len)
228 {
229 	struct brcmf_proto *prot = drvr->prot;
230 	struct brcmf_proto_cdc_dcmd *msg = &prot->msg;
231 	int ret = 0;
232 	u32 flags, id;
233 
234 	brcmf_dbg(TRACE, "Enter\n");
235 	brcmf_dbg(CTL, "cmd %d len %d\n", cmd, len);
236 
237 	memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd));
238 
239 	msg->cmd = cpu_to_le32(cmd);
240 	msg->len = cpu_to_le32(len);
241 	flags = (++prot->reqid << CDC_DCMD_ID_SHIFT) | CDC_DCMD_SET;
242 	flags = (flags & ~CDC_DCMD_IF_MASK) |
243 		(ifidx << CDC_DCMD_IF_SHIFT);
244 	msg->flags = cpu_to_le32(flags);
245 
246 	if (buf)
247 		memcpy(prot->buf, buf, len);
248 
249 	ret = brcmf_proto_cdc_msg(drvr);
250 	if (ret < 0)
251 		goto done;
252 
253 	ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len);
254 	if (ret < 0)
255 		goto done;
256 
257 	flags = le32_to_cpu(msg->flags);
258 	id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT;
259 
260 	if (id != prot->reqid) {
261 		brcmf_dbg(ERROR, "%s: unexpected request id %d (expected %d)\n",
262 			  brcmf_ifname(drvr, ifidx), id, prot->reqid);
263 		ret = -EINVAL;
264 		goto done;
265 	}
266 
267 	/* Check the ERROR flag */
268 	if (flags & CDC_DCMD_ERROR) {
269 		ret = le32_to_cpu(msg->status);
270 		/* Cache error from dongle */
271 		drvr->dongle_error = ret;
272 	}
273 
274 done:
275 	return ret;
276 }
277 
278 int
brcmf_proto_dcmd(struct brcmf_pub * drvr,int ifidx,struct brcmf_dcmd * dcmd,int len)279 brcmf_proto_dcmd(struct brcmf_pub *drvr, int ifidx, struct brcmf_dcmd *dcmd,
280 		  int len)
281 {
282 	struct brcmf_proto *prot = drvr->prot;
283 	int ret = -1;
284 
285 	if (drvr->bus_if->state == BRCMF_BUS_DOWN) {
286 		brcmf_dbg(ERROR, "bus is down. we have nothing to do.\n");
287 		return ret;
288 	}
289 	mutex_lock(&drvr->proto_block);
290 
291 	brcmf_dbg(TRACE, "Enter\n");
292 
293 	if (len > BRCMF_DCMD_MAXLEN)
294 		goto done;
295 
296 	if (prot->pending == true) {
297 		brcmf_dbg(TRACE, "CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n",
298 			  dcmd->cmd, (unsigned long)dcmd->cmd, prot->lastcmd,
299 			  (unsigned long)prot->lastcmd);
300 		if (dcmd->cmd == BRCMF_C_SET_VAR ||
301 		    dcmd->cmd == BRCMF_C_GET_VAR)
302 			brcmf_dbg(TRACE, "iovar cmd=%s\n", (char *)dcmd->buf);
303 
304 		goto done;
305 	}
306 
307 	prot->pending = true;
308 	prot->lastcmd = dcmd->cmd;
309 	if (dcmd->set)
310 		ret = brcmf_proto_cdc_set_dcmd(drvr, ifidx, dcmd->cmd,
311 						   dcmd->buf, len);
312 	else {
313 		ret = brcmf_proto_cdc_query_dcmd(drvr, ifidx, dcmd->cmd,
314 						     dcmd->buf, len);
315 		if (ret > 0)
316 			dcmd->used = ret -
317 					sizeof(struct brcmf_proto_cdc_dcmd);
318 	}
319 
320 	if (ret >= 0)
321 		ret = 0;
322 	else {
323 		struct brcmf_proto_cdc_dcmd *msg = &prot->msg;
324 		/* len == needed when set/query fails from dongle */
325 		dcmd->needed = le32_to_cpu(msg->len);
326 	}
327 
328 	/* Intercept the wme_dp dongle cmd here */
329 	if (!ret && dcmd->cmd == BRCMF_C_SET_VAR &&
330 	    !strcmp(dcmd->buf, "wme_dp")) {
331 		int slen;
332 		__le32 val = 0;
333 
334 		slen = strlen("wme_dp") + 1;
335 		if (len >= (int)(slen + sizeof(int)))
336 			memcpy(&val, (char *)dcmd->buf + slen, sizeof(int));
337 		drvr->wme_dp = (u8) le32_to_cpu(val);
338 	}
339 
340 	prot->pending = false;
341 
342 done:
343 	mutex_unlock(&drvr->proto_block);
344 
345 	return ret;
346 }
347 
pkt_sum_needed(struct sk_buff * skb)348 static bool pkt_sum_needed(struct sk_buff *skb)
349 {
350 	return skb->ip_summed == CHECKSUM_PARTIAL;
351 }
352 
pkt_set_sum_good(struct sk_buff * skb,bool x)353 static void pkt_set_sum_good(struct sk_buff *skb, bool x)
354 {
355 	skb->ip_summed = (x ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE);
356 }
357 
brcmf_proto_hdrpush(struct brcmf_pub * drvr,int ifidx,struct sk_buff * pktbuf)358 void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx,
359 			 struct sk_buff *pktbuf)
360 {
361 	struct brcmf_proto_bdc_header *h;
362 
363 	brcmf_dbg(TRACE, "Enter\n");
364 
365 	/* Push BDC header used to convey priority for buses that don't */
366 
367 	skb_push(pktbuf, BDC_HEADER_LEN);
368 
369 	h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
370 
371 	h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
372 	if (pkt_sum_needed(pktbuf))
373 		h->flags |= BDC_FLAG_SUM_NEEDED;
374 
375 	h->priority = (pktbuf->priority & BDC_PRIORITY_MASK);
376 	h->flags2 = 0;
377 	h->data_offset = 0;
378 	BDC_SET_IF_IDX(h, ifidx);
379 }
380 
brcmf_proto_hdrpull(struct device * dev,int * ifidx,struct sk_buff * pktbuf)381 int brcmf_proto_hdrpull(struct device *dev, int *ifidx,
382 			struct sk_buff *pktbuf)
383 {
384 	struct brcmf_proto_bdc_header *h;
385 	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
386 	struct brcmf_pub *drvr = bus_if->drvr;
387 
388 	brcmf_dbg(TRACE, "Enter\n");
389 
390 	/* Pop BDC header used to convey priority for buses that don't */
391 
392 	if (pktbuf->len < BDC_HEADER_LEN) {
393 		brcmf_dbg(ERROR, "rx data too short (%d < %d)\n",
394 			  pktbuf->len, BDC_HEADER_LEN);
395 		return -EBADE;
396 	}
397 
398 	h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
399 
400 	*ifidx = BDC_GET_IF_IDX(h);
401 	if (*ifidx >= BRCMF_MAX_IFS) {
402 		brcmf_dbg(ERROR, "rx data ifnum out of range (%d)\n", *ifidx);
403 		return -EBADE;
404 	}
405 
406 	if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) !=
407 	    BDC_PROTO_VER) {
408 		brcmf_dbg(ERROR, "%s: non-BDC packet received, flags 0x%x\n",
409 			  brcmf_ifname(drvr, *ifidx), h->flags);
410 		return -EBADE;
411 	}
412 
413 	if (h->flags & BDC_FLAG_SUM_GOOD) {
414 		brcmf_dbg(INFO, "%s: BDC packet received with good rx-csum, flags 0x%x\n",
415 			  brcmf_ifname(drvr, *ifidx), h->flags);
416 		pkt_set_sum_good(pktbuf, true);
417 	}
418 
419 	pktbuf->priority = h->priority & BDC_PRIORITY_MASK;
420 
421 	skb_pull(pktbuf, BDC_HEADER_LEN);
422 
423 	return 0;
424 }
425 
brcmf_proto_attach(struct brcmf_pub * drvr)426 int brcmf_proto_attach(struct brcmf_pub *drvr)
427 {
428 	struct brcmf_proto *cdc;
429 
430 	cdc = kzalloc(sizeof(struct brcmf_proto), GFP_ATOMIC);
431 	if (!cdc)
432 		goto fail;
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_dbg(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->bus_if->maxctl = BRCMF_DCMD_MAXLEN +
443 			sizeof(struct brcmf_proto_cdc_dcmd) + ROUND_UP_MARGIN;
444 	return 0;
445 
446 fail:
447 	kfree(cdc);
448 	return -ENOMEM;
449 }
450 
451 /* ~NOTE~ What if another thread is waiting on the semaphore?  Holding it? */
brcmf_proto_detach(struct brcmf_pub * drvr)452 void brcmf_proto_detach(struct brcmf_pub *drvr)
453 {
454 	kfree(drvr->prot);
455 	drvr->prot = NULL;
456 }
457 
brcmf_proto_init(struct brcmf_pub * drvr)458 int brcmf_proto_init(struct brcmf_pub *drvr)
459 {
460 	int ret = 0;
461 	char buf[128];
462 
463 	brcmf_dbg(TRACE, "Enter\n");
464 
465 	mutex_lock(&drvr->proto_block);
466 
467 	/* Get the device MAC address */
468 	strcpy(buf, "cur_etheraddr");
469 	ret = brcmf_proto_cdc_query_dcmd(drvr, 0, BRCMF_C_GET_VAR,
470 					  buf, sizeof(buf));
471 	if (ret < 0) {
472 		mutex_unlock(&drvr->proto_block);
473 		return ret;
474 	}
475 	memcpy(drvr->mac, buf, ETH_ALEN);
476 
477 	mutex_unlock(&drvr->proto_block);
478 
479 	ret = brcmf_c_preinit_dcmds(drvr);
480 
481 	/* Always assumes wl for now */
482 	drvr->iswl = true;
483 
484 	return ret;
485 }
486 
brcmf_proto_stop(struct brcmf_pub * drvr)487 void brcmf_proto_stop(struct brcmf_pub *drvr)
488 {
489 	/* Nothing to do for CDC */
490 }
491