1 /** @file dbus.c
2 *
3 * Hides details of USB / SDIO / SPI interfaces and OS details. It is intended
4 * to shield details and provide the caller with one common bus interface for
5 * all dongle devices. In practice, it is only used for USB interfaces. DBUS is
6 * not a protocol, but an abstraction layer.
7 *
8 * Copyright (C) 1999-2016, Broadcom Corporation
9 *
10 * Unless you and Broadcom execute a separate written software license
11 * agreement governing use of this software, this software is licensed to you
12 * under the terms of the GNU General Public License version 2 (the "GPL"),
13 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
14 * following added to such license:
15 *
16 * As a special exception, the copyright holders of this software give you
17 * permission to link this software with independent modules, and to copy and
18 * distribute the resulting executable under terms of your choice, provided that
19 * you also meet, for each linked independent module, the terms and conditions
20 * of the license of that module. An independent module is a module which is
21 * not derived from this software. The special exception does not apply to any
22 * modifications of the software.
23 *
24 * Notwithstanding the above, under no circumstances may you combine this
25 * software in any way with any other Broadcom software provided under a license
26 * other than the GPL, without Broadcom's express prior written consent.
27 *
28 *
29 * <<Broadcom-WL-IPTag/Open:>>
30 *
31 * $Id: dbus.c 553311 2015-04-29 10:23:08Z $
32 */
33
34 #include "osl.h"
35 #include "dbus.h"
36 #include <bcmutils.h>
37 #include <dngl_stats.h>
38 #include <dhd.h>
39 #include <dhd_proto.h>
40 #ifdef PROP_TXSTATUS /* a form of flow control between host and dongle */
41 #include <dhd_wlfc.h>
42 #endif
43 #include <dhd_config.h>
44
45 #if defined(BCM_REQUEST_FW)
46 #include <bcmsrom_fmt.h>
47 #include <trxhdr.h>
48 #include <usbrdl.h>
49 #include <bcmendian.h>
50 #include <sbpcmcia.h>
51 #include <bcmnvram.h>
52 #include <bcmdevs.h>
53 #endif
54
55 #if defined(BCM_REQUEST_FW)
56 #ifndef VARS_MAX
57 #define VARS_MAX 8192
58 #endif
59 #endif
60
61 #ifdef DBUS_USB_LOOPBACK
62 extern bool is_loopback_pkt(void *buf);
63 extern int matches_loopback_pkt(void *buf);
64 #endif
65
66 /** General info for all BUS types */
67 typedef struct dbus_irbq {
68 dbus_irb_t *head;
69 dbus_irb_t *tail;
70 int cnt;
71 } dbus_irbq_t;
72
73 /**
74 * This private structure dhd_bus_t is also declared in dbus_usb_linux.c.
75 * All the fields must be consistent in both declarations.
76 */
77 typedef struct dhd_bus {
78 dbus_pub_t pub; /* MUST BE FIRST */
79 dhd_pub_t *dhd;
80
81 void *cbarg;
82 dbus_callbacks_t *cbs; /* callbacks to higher level, e.g. dhd_linux.c */
83 void *bus_info;
84 dbus_intf_t *drvintf; /* callbacks to lower level, e.g. dbus_usb.c or
85 dbus_usb_linux.c */
86 uint8 *fw;
87 int fwlen;
88 uint32 errmask;
89 int rx_low_watermark; /* avoid rx overflow by filling rx with free IRBs */
90 int tx_low_watermark;
91 bool txoff;
92 bool txoverride; /* flow control related */
93 bool rxoff;
94 bool tx_timer_ticking;
95
96 dbus_irbq_t *rx_q;
97 dbus_irbq_t *tx_q;
98
99 uint8 *nvram;
100 int nvram_len;
101 uint8 *image; /* buffer for combine fw and nvram */
102 int image_len;
103 uint8 *orig_fw;
104 int origfw_len;
105 int decomp_memsize;
106 dbus_extdl_t extdl;
107 int nvram_nontxt;
108 #if defined(BCM_REQUEST_FW)
109 void *firmware;
110 void *nvfile;
111 #endif
112 char *fw_path; /* module_param: path to firmware image */
113 char *nv_path; /* module_param: path to nvram vars file */
114 } dhd_bus_t;
115
116 struct exec_parms {
117 union {
118 /* Can consolidate same params, if need be, but this shows
119 * group of parameters per function
120 */
121 struct {
122 dbus_irbq_t *q;
123 dbus_irb_t *b;
124 } qenq;
125
126 struct {
127 dbus_irbq_t *q;
128 } qdeq;
129 };
130 };
131
132 #define EXEC_RXLOCK(info, fn, a) \
133 info->drvintf->exec_rxlock(dhd_bus->bus_info, ((exec_cb_t)fn), \
134 ((struct exec_parms *)a))
135
136 #define EXEC_TXLOCK(info, fn, a) \
137 info->drvintf->exec_txlock(dhd_bus->bus_info, ((exec_cb_t)fn), \
138 ((struct exec_parms *)a))
139
140 /*
141 * Callbacks common for all BUS
142 */
143 static void dbus_if_send_irb_timeout(void *handle, dbus_irb_tx_t *txirb);
144 static void dbus_if_send_irb_complete(void *handle, dbus_irb_tx_t *txirb,
145 int status);
146 static void dbus_if_recv_irb_complete(void *handle, dbus_irb_rx_t *rxirb,
147 int status);
148 static void dbus_if_errhandler(void *handle, int err);
149 static void dbus_if_ctl_complete(void *handle, int type, int status);
150 static void dbus_if_state_change(void *handle, int state);
151 static void *dbus_if_pktget(void *handle, uint len, bool send);
152 static void dbus_if_pktfree(void *handle, void *p, bool send);
153 static struct dbus_irb *dbus_if_getirb(void *cbarg, bool send);
154 static void dbus_if_rxerr_indicate(void *handle, bool on);
155
156 void *dhd_dbus_probe_cb(void *arg, const char *desc, uint32 bustype,
157 uint16 bus_no, uint16 slot, uint32 hdrlen);
158 void dhd_dbus_disconnect_cb(void *arg);
159 void dbus_detach(dhd_bus_t *pub);
160
161 /** functions in this file that are called by lower DBUS levels, e.g. dbus_usb.c
162 */
163 static dbus_intf_callbacks_t dbus_intf_cbs = {dbus_if_send_irb_timeout,
164 dbus_if_send_irb_complete,
165 dbus_if_recv_irb_complete,
166 dbus_if_errhandler,
167 dbus_if_ctl_complete,
168 dbus_if_state_change,
169 NULL, /* isr */
170 NULL, /* dpc */
171 NULL, /* watchdog */
172 dbus_if_pktget,
173 dbus_if_pktfree,
174 dbus_if_getirb,
175 dbus_if_rxerr_indicate};
176
177 /*
178 * Need global for probe() and disconnect() since
179 * attach() is not called at probe and detach()
180 * can be called inside disconnect()
181 */
182 static dbus_intf_t *g_busintf = NULL;
183 static probe_cb_t probe_cb = NULL;
184 static disconnect_cb_t disconnect_cb = NULL;
185 static void *probe_arg = NULL;
186 static void *disc_arg = NULL;
187
188 #if defined(BCM_REQUEST_FW)
189 int8 *nonfwnvram = NULL; /* stand-alone multi-nvram given with driver load */
190 int nonfwnvramlen = 0;
191 #endif /* #if defined(BCM_REQUEST_FW) */
192
193 static void *q_enq(dbus_irbq_t *q, dbus_irb_t *b);
194 static void *q_enq_exec(struct exec_parms *args);
195 static dbus_irb_t *q_deq(dbus_irbq_t *q);
196 static void *q_deq_exec(struct exec_parms *args);
197 static int dbus_tx_timer_init(dhd_bus_t *dhd_bus);
198 static int dbus_tx_timer_start(dhd_bus_t *dhd_bus, uint timeout);
199 static int dbus_tx_timer_stop(dhd_bus_t *dhd_bus);
200 static int dbus_irbq_init(dhd_bus_t *dhd_bus, dbus_irbq_t *q, int nq,
201 int size_irb);
202 static int dbus_irbq_deinit(dhd_bus_t *dhd_bus, dbus_irbq_t *q, int size_irb);
203 static int dbus_rxirbs_fill(dhd_bus_t *dhd_bus);
204 static int dbus_send_irb(dbus_pub_t *pub, uint8 *buf, int len, void *pkt,
205 void *info);
206 static void dbus_disconnect(void *handle);
207 static void *dbus_probe(void *arg, const char *desc, uint32 bustype,
208 uint16 bus_no, uint16 slot, uint32 hdrlen);
209
210 #if defined(BCM_REQUEST_FW)
211 extern char *dngl_firmware;
212 extern unsigned int dngl_fwlen;
213 #ifndef EXTERNAL_FW_PATH
214 static int dbus_get_nvram(dhd_bus_t *dhd_bus);
215 static int dbus_jumbo_nvram(dhd_bus_t *dhd_bus);
216 static int dbus_otp(dhd_bus_t *dhd_bus, uint16 *boardtype, uint16 *boardrev);
217 static int dbus_select_nvram(dhd_bus_t *dhd_bus, int8 *jumbonvram, int jumbolen,
218 uint16 boardtype, uint16 boardrev, int8 **nvram,
219 int *nvram_len);
220 #endif /* !EXTERNAL_FW_PATH */
221 extern int dbus_zlib_decomp(dhd_bus_t *dhd_bus);
222 extern void *dbus_zlib_calloc(int num, int size);
223 extern void dbus_zlib_free(void *ptr);
224 #endif
225
226 /* function */
dbus_flowctrl_tx(void * dbi,bool on)227 void dbus_flowctrl_tx(void *dbi, bool on)
228 {
229 dhd_bus_t *dhd_bus = dbi;
230
231 if (dhd_bus == NULL) {
232 return;
233 }
234
235 DBUSTRACE(("%s on %d\n", __FUNCTION__, on));
236
237 if (dhd_bus->txoff == on) {
238 return;
239 }
240
241 dhd_bus->txoff = on;
242
243 if (dhd_bus->cbs && dhd_bus->cbs->txflowcontrol) {
244 dhd_bus->cbs->txflowcontrol(dhd_bus->cbarg, on);
245 }
246 }
247
248 /**
249 * if lower level DBUS signaled a rx error, more free rx IRBs should be
250 * allocated or flow control should kick in to make more free rx IRBs available.
251 */
dbus_if_rxerr_indicate(void * handle,bool on)252 static void dbus_if_rxerr_indicate(void *handle, bool on)
253 {
254 dhd_bus_t *dhd_bus = (dhd_bus_t *)handle;
255
256 DBUSTRACE(("%s, on %d\n", __FUNCTION__, on));
257
258 if (dhd_bus == NULL) {
259 return;
260 }
261
262 if (dhd_bus->txoverride == on) {
263 return;
264 }
265
266 dhd_bus->txoverride = on; /* flow control */
267
268 if (!on) {
269 dbus_rxirbs_fill(dhd_bus);
270 }
271 }
272
273 /** q_enq()/q_deq() are executed with protection via exec_rxlock()/exec_txlock()
274 */
q_enq(dbus_irbq_t * q,dbus_irb_t * b)275 static void *q_enq(dbus_irbq_t *q, dbus_irb_t *b)
276 {
277 ASSERT(q->tail != b);
278 ASSERT(b->next == NULL);
279 b->next = NULL;
280 if (q->tail) {
281 q->tail->next = b;
282 q->tail = b;
283 } else {
284 q->head = q->tail = b;
285 }
286
287 q->cnt++;
288
289 return b;
290 }
291
q_enq_exec(struct exec_parms * args)292 static void *q_enq_exec(struct exec_parms *args)
293 {
294 return q_enq(args->qenq.q, args->qenq.b);
295 }
296
q_deq(dbus_irbq_t * q)297 static dbus_irb_t *q_deq(dbus_irbq_t *q)
298 {
299 dbus_irb_t *b;
300
301 b = q->head;
302 if (b) {
303 q->head = q->head->next;
304 b->next = NULL;
305
306 if (q->head == NULL) {
307 q->tail = q->head;
308 }
309
310 q->cnt--;
311 }
312 return b;
313 }
314
q_deq_exec(struct exec_parms * args)315 static void *q_deq_exec(struct exec_parms *args)
316 {
317 return q_deq(args->qdeq.q);
318 }
319
320 /**
321 * called during attach phase. Status @ Dec 2012: this function does nothing
322 * since for all of the lower DBUS levels dhd_bus->drvintf->tx_timer_init is
323 * NULL.
324 */
dbus_tx_timer_init(dhd_bus_t * dhd_bus)325 static int dbus_tx_timer_init(dhd_bus_t *dhd_bus)
326 {
327 if (dhd_bus && dhd_bus->drvintf && dhd_bus->drvintf->tx_timer_init) {
328 return dhd_bus->drvintf->tx_timer_init(dhd_bus->bus_info);
329 } else {
330 return DBUS_ERR;
331 }
332 }
333
dbus_tx_timer_start(dhd_bus_t * dhd_bus,uint timeout)334 static int dbus_tx_timer_start(dhd_bus_t *dhd_bus, uint timeout)
335 {
336 if (dhd_bus == NULL) {
337 return DBUS_ERR;
338 }
339
340 if (dhd_bus->tx_timer_ticking) {
341 return DBUS_OK;
342 }
343
344 if (dhd_bus->drvintf && dhd_bus->drvintf->tx_timer_start) {
345 if (dhd_bus->drvintf->tx_timer_start(dhd_bus->bus_info, timeout) ==
346 DBUS_OK) {
347 dhd_bus->tx_timer_ticking = TRUE;
348 return DBUS_OK;
349 }
350 }
351
352 return DBUS_ERR;
353 }
354
dbus_tx_timer_stop(dhd_bus_t * dhd_bus)355 static int dbus_tx_timer_stop(dhd_bus_t *dhd_bus)
356 {
357 if (dhd_bus == NULL) {
358 return DBUS_ERR;
359 }
360
361 if (!dhd_bus->tx_timer_ticking) {
362 return DBUS_OK;
363 }
364
365 if (dhd_bus->drvintf && dhd_bus->drvintf->tx_timer_stop) {
366 if (dhd_bus->drvintf->tx_timer_stop(dhd_bus->bus_info) == DBUS_OK) {
367 dhd_bus->tx_timer_ticking = FALSE;
368 return DBUS_OK;
369 }
370 }
371
372 return DBUS_ERR;
373 }
374
375 /** called during attach phase. */
dbus_irbq_init(dhd_bus_t * dhd_bus,dbus_irbq_t * q,int nq,int size_irb)376 static int dbus_irbq_init(dhd_bus_t *dhd_bus, dbus_irbq_t *q, int nq,
377 int size_irb)
378 {
379 int i;
380 dbus_irb_t *irb;
381
382 ASSERT(q);
383 ASSERT(dhd_bus);
384
385 for (i = 0; i < nq; i++) {
386 /* MALLOC dbus_irb_tx or dbus_irb_rx, but cast to simple dbus_irb_t
387 * linkedlist */
388 irb = (dbus_irb_t *)MALLOC(dhd_bus->pub.osh, size_irb);
389 if (irb == NULL) {
390 ASSERT(irb);
391 return DBUS_ERR;
392 }
393 bzero(irb, size_irb);
394
395 /* q_enq() does not need to go through EXEC_xxLOCK() during init() */
396 q_enq(q, irb);
397 }
398
399 return DBUS_OK;
400 }
401
402 /** called during detach phase or when attach failed */
dbus_irbq_deinit(dhd_bus_t * dhd_bus,dbus_irbq_t * q,int size_irb)403 static int dbus_irbq_deinit(dhd_bus_t *dhd_bus, dbus_irbq_t *q, int size_irb)
404 {
405 dbus_irb_t *irb;
406
407 ASSERT(q);
408 ASSERT(dhd_bus);
409
410 /* q_deq() does not need to go through EXEC_xxLOCK()
411 * during deinit(); all callbacks are stopped by this time
412 */
413 while ((irb = q_deq(q)) != NULL) {
414 MFREE(dhd_bus->pub.osh, irb, size_irb);
415 }
416
417 if (q->cnt) {
418 DBUSERR(("deinit: q->cnt=%d > 0\n", q->cnt));
419 }
420 return DBUS_OK;
421 }
422
423 /** multiple code paths require the rx queue to be filled with more free IRBs */
dbus_rxirbs_fill(dhd_bus_t * dhd_bus)424 static int dbus_rxirbs_fill(dhd_bus_t *dhd_bus)
425 {
426 int err = DBUS_OK;
427
428 dbus_irb_rx_t *rxirb;
429 struct exec_parms args;
430
431 ASSERT(dhd_bus);
432 if (dhd_bus->pub.busstate != DBUS_STATE_UP) {
433 DBUSERR(("dbus_rxirbs_fill: DBUS not up \n"));
434 return DBUS_ERR;
435 } else if (!dhd_bus->drvintf || (dhd_bus->drvintf->recv_irb == NULL)) {
436 /* Lower edge bus interface does not support recv_irb().
437 * No need to pre-submit IRBs in this case.
438 */
439 return DBUS_ERR;
440 }
441
442 /* The dongle recv callback is freerunning without lock. So multiple
443 * callbacks(and this refill) can run in parallel. While the rxoff condition
444 * is triggered outside, below while loop has to check and abort posting
445 * more to avoid RPC rxq overflow.
446 */
447 args.qdeq.q = dhd_bus->rx_q;
448 while ((!dhd_bus->rxoff) &&
449 (rxirb = (EXEC_RXLOCK(dhd_bus, q_deq_exec, &args))) != NULL) {
450 err = dhd_bus->drvintf->recv_irb(dhd_bus->bus_info, rxirb);
451 if (err == DBUS_ERR_RXDROP || err == DBUS_ERR_RXFAIL) {
452 /* Add the the free rxirb back to the queue
453 * and wait till later
454 */
455 bzero(rxirb, sizeof(dbus_irb_rx_t));
456 args.qenq.q = dhd_bus->rx_q;
457 args.qenq.b = (dbus_irb_t *)rxirb;
458 EXEC_RXLOCK(dhd_bus, q_enq_exec, &args);
459 break;
460 } else if (err != DBUS_OK) {
461 int i = 0;
462 while (i++ < 0x64) {
463 DBUSERR(("%s :: memory leak for rxirb note?\n", __FUNCTION__));
464 }
465 }
466 }
467 return err;
468 } /* dbus_rxirbs_fill */
469
470 /** called when the DBUS interface state changed. */
dbus_flowctrl_rx(dbus_pub_t * pub,bool on)471 void dbus_flowctrl_rx(dbus_pub_t *pub, bool on)
472 {
473 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
474
475 if (dhd_bus == NULL) {
476 return;
477 }
478
479 DBUSTRACE(("%s\n", __FUNCTION__));
480
481 if (dhd_bus->rxoff == on) {
482 return;
483 }
484
485 dhd_bus->rxoff = on;
486
487 if (dhd_bus->pub.busstate == DBUS_STATE_UP) {
488 if (!on) {
489 /* post more irbs, resume rx if necessary */
490 dbus_rxirbs_fill(dhd_bus);
491 if (dhd_bus && dhd_bus->drvintf->recv_resume) {
492 dhd_bus->drvintf->recv_resume(dhd_bus->bus_info);
493 }
494 } else {
495 /* ??? cancell posted irbs first */
496
497 if (dhd_bus && dhd_bus->drvintf->recv_stop) {
498 dhd_bus->drvintf->recv_stop(dhd_bus->bus_info);
499 }
500 }
501 }
502 }
503
504 /**
505 * Several code paths in this file want to send a buffer to the dongle. This
506 * function handles both sending of a buffer or a pkt.
507 */
dbus_send_irb(dbus_pub_t * pub,uint8 * buf,int len,void * pkt,void * info)508 static int dbus_send_irb(dbus_pub_t *pub, uint8 *buf, int len, void *pkt,
509 void *info)
510 {
511 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
512 int err = DBUS_OK;
513 dbus_irb_tx_t *txirb = NULL;
514 int txirb_pending;
515 struct exec_parms args;
516
517 if (dhd_bus == NULL) {
518 return DBUS_ERR;
519 }
520
521 DBUSTRACE(("%s\n", __FUNCTION__));
522
523 if (dhd_bus->pub.busstate == DBUS_STATE_UP ||
524 dhd_bus->pub.busstate == DBUS_STATE_SLEEP) {
525 args.qdeq.q = dhd_bus->tx_q;
526 if (dhd_bus->drvintf) {
527 txirb = EXEC_TXLOCK(dhd_bus, q_deq_exec, &args);
528 }
529
530 if (txirb == NULL) {
531 DBUSERR(("Out of tx dbus_bufs\n"));
532 return DBUS_ERR;
533 }
534
535 if (pkt != NULL) {
536 txirb->pkt = pkt;
537 txirb->buf = NULL;
538 txirb->len = 0;
539 } else if (buf != NULL) {
540 txirb->pkt = NULL;
541 txirb->buf = buf;
542 txirb->len = len;
543 } else {
544 ASSERT(0); /* Should not happen */
545 }
546 txirb->info = info;
547 txirb->arg = NULL;
548 txirb->retry_count = 0;
549
550 if (dhd_bus->drvintf && dhd_bus->drvintf->send_irb) {
551 /* call lower DBUS level send_irb function */
552 err = dhd_bus->drvintf->send_irb(dhd_bus->bus_info, txirb);
553 if (err == DBUS_ERR_TXDROP) {
554 /* tx fail and no completion routine to clean up, reclaim irb
555 * NOW */
556 DBUSERR(
557 ("%s: send_irb failed, status = %d\n", __FUNCTION__, err));
558 bzero(txirb, sizeof(dbus_irb_tx_t));
559 args.qenq.q = dhd_bus->tx_q;
560 args.qenq.b = (dbus_irb_t *)txirb;
561 EXEC_TXLOCK(dhd_bus, q_enq_exec, &args);
562 } else {
563 dbus_tx_timer_start(dhd_bus, DBUS_TX_TIMEOUT_INTERVAL);
564 txirb_pending = dhd_bus->pub.ntxq - dhd_bus->tx_q->cnt;
565 if (txirb_pending > (dhd_bus->tx_low_watermark * 0x3)) {
566 dbus_flowctrl_tx(dhd_bus, TRUE);
567 }
568 }
569 }
570 } else {
571 err = DBUS_ERR_TXFAIL;
572 DBUSTRACE(("%s: bus down, send_irb failed\n", __FUNCTION__));
573 }
574
575 return err;
576 } /* dbus_send_irb */
577
578 #if defined(BCM_REQUEST_FW)
579
580 /**
581 * Before downloading a firmware image into the dongle, the validity of the
582 * image must be checked.
583 */
check_file(osl_t * osh,unsigned char * headers)584 static int check_file(osl_t *osh, unsigned char *headers)
585 {
586 struct trx_header *trx;
587 int actual_len = -1;
588
589 /* Extract trx header */
590 trx = (struct trx_header *)headers;
591 if (ltoh32(trx->magic) != TRX_MAGIC) {
592 printf("Error: trx bad hdr %x\n", ltoh32(trx->magic));
593 return -1;
594 }
595
596 headers += SIZEOF_TRX(trx);
597
598 /* TRX V1: get firmware len */
599 /* TRX V2: get firmware len and DSG/CFG lengths */
600 if (ltoh32(trx->flag_version) & TRX_UNCOMP_IMAGE) {
601 actual_len =
602 ltoh32(trx->offsets[TRX_OFFSETS_DLFWLEN_IDX]) + SIZEOF_TRX(trx);
603 #ifdef BCMTRXV2
604 if (ISTRX_V2(trx)) {
605 actual_len += ltoh32(trx->offsets[TRX_OFFSETS_DSG_LEN_IDX]) +
606 ltoh32(trx->offsets[TRX_OFFSETS_CFG_LEN_IDX]);
607 }
608 #endif
609 return actual_len;
610 } else {
611 printf("compressed image\n");
612 }
613
614 return -1;
615 }
616
617 #ifdef EXTERNAL_FW_PATH
dbus_get_fw_nvram(dhd_bus_t * dhd_bus,char * pfw_path,char * pnv_path)618 static int dbus_get_fw_nvram(dhd_bus_t *dhd_bus, char *pfw_path, char *pnv_path)
619 {
620 int bcmerror = -1, i;
621 uint len, total_len;
622 void *nv_image = NULL, *fw_image = NULL;
623 char *nv_memblock = NULL, *fw_memblock = NULL;
624 char *bufp;
625 bool file_exists;
626 uint8 nvram_words_pad = 0;
627 uint memblock_size = 2048;
628 uint8 *memptr;
629 int actual_fwlen;
630 struct trx_header *hdr;
631 uint32 img_offset = 0;
632 int offset = 0;
633
634 /* For Get nvram */
635 file_exists = ((pnv_path != NULL) && (pnv_path[0] != '\0'));
636 if (file_exists) {
637 nv_image = dhd_os_open_image1(dhd_bus->dhd, pnv_path);
638 if (nv_image == NULL) {
639 printf("%s: Open nvram file failed %s\n", __FUNCTION__, pnv_path);
640 goto err;
641 }
642 }
643 nv_memblock = MALLOC(dhd_bus->pub.osh, MAX_NVRAMBUF_SIZE);
644 if (nv_memblock == NULL) {
645 DBUSERR(("%s: Failed to allocate memory %d bytes\n", __FUNCTION__,
646 MAX_NVRAMBUF_SIZE));
647 goto err;
648 }
649 len = dhd_os_get_image_block(nv_memblock, MAX_NVRAMBUF_SIZE, nv_image);
650 if (len > 0 && len < MAX_NVRAMBUF_SIZE) {
651 bufp = (char *)nv_memblock;
652 bufp[len] = 0;
653 dhd_bus->nvram_len = process_nvram_vars(bufp, len);
654 if (dhd_bus->nvram_len % 0x4) {
655 nvram_words_pad = 0x4 - dhd_bus->nvram_len % 0x4;
656 }
657 } else {
658 DBUSERR(("%s: error reading nvram file: %d\n", __FUNCTION__, len));
659 bcmerror = DBUS_ERR_NVRAM;
660 goto err;
661 }
662 if (nv_image) {
663 dhd_os_close_image1(dhd_bus->dhd, nv_image);
664 nv_image = NULL;
665 }
666
667 /* For Get first block of fw to calculate total_len */
668 file_exists = ((pfw_path != NULL) && (pfw_path[0] != '\0'));
669 if (file_exists) {
670 fw_image = dhd_os_open_image1(dhd_bus->dhd, pfw_path);
671 if (fw_image == NULL) {
672 printf("%s: Open fw file failed %s\n", __FUNCTION__, pfw_path);
673 goto err;
674 }
675 }
676 memptr = fw_memblock = MALLOC(dhd_bus->pub.osh, memblock_size);
677 if (fw_memblock == NULL) {
678 DBUSERR(("%s: Failed to allocate memory %d bytes\n", __FUNCTION__,
679 memblock_size));
680 goto err;
681 }
682 len = dhd_os_get_image_block((char *)memptr, memblock_size, fw_image);
683 if ((actual_fwlen = check_file(dhd_bus->pub.osh, memptr)) <= 0) {
684 DBUSERR(("%s: bad firmware format!\n", __FUNCTION__));
685 goto err;
686 }
687
688 total_len = actual_fwlen + dhd_bus->nvram_len + nvram_words_pad;
689 #if defined(CONFIG_DHD_USE_STATIC_BUF)
690 dhd_bus->image = (uint8 *)DHD_OS_PREALLOC(
691 dhd_bus->dhd, DHD_PREALLOC_MEMDUMP_RAM, total_len);
692 #else
693 dhd_bus->image = MALLOC(dhd_bus->pub.osh, total_len);
694 #endif /* CONFIG_DHD_USE_STATIC_BUF */
695 dhd_bus->image_len = total_len;
696 if (dhd_bus->image == NULL) {
697 DBUSERR(("%s: malloc failed! size=%d\n", __FUNCTION__, total_len));
698 goto err;
699 }
700
701 /* Step1: Copy trx header + firmwre */
702 memptr = fw_memblock;
703 do {
704 if (len < 0) {
705 DBUSERR(("%s: dhd_os_get_image_block failed (%d)\n", __FUNCTION__,
706 len));
707 bcmerror = BCME_ERROR;
708 goto err;
709 }
710 bcopy(memptr, dhd_bus->image + offset, len);
711 offset += len;
712 } while ((
713 len = dhd_os_get_image_block((char *)memptr, memblock_size, fw_image)));
714 /* Step2: Copy NVRAM + pad */
715 hdr = (struct trx_header *)dhd_bus->image;
716 img_offset = SIZEOF_TRX(hdr) + hdr->offsets[TRX_OFFSETS_DLFWLEN_IDX];
717 bcopy(nv_memblock, (uint8 *)(dhd_bus->image + img_offset),
718 dhd_bus->nvram_len);
719 img_offset += dhd_bus->nvram_len;
720 if (nvram_words_pad) {
721 bzero(&dhd_bus->image[img_offset], nvram_words_pad);
722 img_offset += nvram_words_pad;
723 }
724 #ifdef BCMTRXV2
725 /* Step3: Copy DSG/CFG for V2 */
726 if (ISTRX_V2(hdr) && (hdr->offsets[TRX_OFFSETS_DSG_LEN_IDX] ||
727 hdr->offsets[TRX_OFFSETS_CFG_LEN_IDX])) {
728 DBUSERR(("%s: fix me\n", __FUNCTION__));
729 }
730 #endif /* BCMTRXV2 */
731 /* Step4: update TRX header for nvram size */
732 hdr = (struct trx_header *)dhd_bus->image;
733 hdr->len = htol32(total_len);
734 /* Pass the actual fw len */
735 hdr->offsets[TRX_OFFSETS_NVM_LEN_IDX] =
736 htol32(dhd_bus->nvram_len + nvram_words_pad);
737 /* Calculate CRC over header */
738 hdr->crc32 =
739 hndcrc32((uint8 *)&hdr->flag_version,
740 SIZEOF_TRX(hdr) - OFFSETOF(struct trx_header, flag_version),
741 CRC32_INIT_VALUE);
742
743 /* Calculate CRC over data */
744 for (i = SIZEOF_TRX(hdr); i < total_len; ++i) {
745 hdr->crc32 = hndcrc32((uint8 *)&dhd_bus->image[i], 1, hdr->crc32);
746 }
747 hdr->crc32 = htol32(hdr->crc32);
748
749 bcmerror = DBUS_OK;
750
751 err:
752 if (fw_memblock) {
753 MFREE(dhd_bus->pub.osh, fw_memblock, MAX_NVRAMBUF_SIZE);
754 }
755 if (fw_image) {
756 dhd_os_close_image1(dhd_bus->dhd, fw_image);
757 }
758 if (nv_memblock) {
759 MFREE(dhd_bus->pub.osh, nv_memblock, MAX_NVRAMBUF_SIZE);
760 }
761 if (nv_image) {
762 dhd_os_close_image1(dhd_bus->dhd, nv_image);
763 }
764
765 return bcmerror;
766 }
767
768 /**
769 * during driver initialization ('attach') or after PnP 'resume', firmware needs
770 * to be loaded into the dongle
771 */
dbus_do_download(dhd_bus_t * dhd_bus,char * pfw_path,char * pnv_path)772 static int dbus_do_download(dhd_bus_t *dhd_bus, char *pfw_path, char *pnv_path)
773 {
774 int err = DBUS_OK;
775
776 err = dbus_get_fw_nvram(dhd_bus, pfw_path, pnv_path);
777 if (err) {
778 DBUSERR(("dbus_do_download: fail to get nvram %d\n", err));
779 return err;
780 }
781
782 if (dhd_bus->drvintf->dlstart && dhd_bus->drvintf->dlrun) {
783 err = dhd_bus->drvintf->dlstart(dhd_bus->bus_info, dhd_bus->image,
784 dhd_bus->image_len);
785 if (err == DBUS_OK) {
786 err = dhd_bus->drvintf->dlrun(dhd_bus->bus_info);
787 }
788 } else {
789 err = DBUS_ERR;
790 }
791
792 if (dhd_bus->image) {
793 #if defined(CONFIG_DHD_USE_STATIC_BUF)
794 DHD_OS_PREFREE(dhd_bus->dhd, dhd_bus->image, dhd_bus->image_len);
795 #else
796 MFREE(dhd_bus->pub.osh, dhd_bus->image, dhd_bus->image_len);
797 #endif /* CONFIG_DHD_USE_STATIC_BUF */
798 dhd_bus->image = NULL;
799 dhd_bus->image_len = 0;
800 }
801
802 return err;
803 } /* dbus_do_download */
804 #else
805
806 /**
807 * It is easy for the user to pass one jumbo nvram file to the driver than a set
808 * of smaller files. The 'jumbo nvram' file format is essentially a set of nvram
809 * files. Before commencing firmware download, the dongle needs to be probed so
810 * that the correct nvram contents within the jumbo nvram file is selected.
811 */
dbus_jumbo_nvram(dhd_bus_t * dhd_bus)812 static int dbus_jumbo_nvram(dhd_bus_t *dhd_bus)
813 {
814 int8 *nvram = NULL;
815 int nvram_len = 0;
816 int ret = DBUS_OK;
817 uint16 boardrev = 0xFFFF;
818 uint16 boardtype = 0xFFFF;
819
820 /* read the otp for boardrev & boardtype
821 * if boardtype/rev are present in otp
822 * select nvram data for that boardtype/rev
823 */
824 dbus_otp(dhd_bus, &boardtype, &boardrev);
825
826 ret =
827 dbus_select_nvram(dhd_bus, dhd_bus->extdl.vars, dhd_bus->extdl.varslen,
828 boardtype, boardrev, &nvram, &nvram_len);
829 if (ret == DBUS_JUMBO_BAD_FORMAT) {
830 return DBUS_ERR_NVRAM;
831 } else if (ret == DBUS_JUMBO_NOMATCH &&
832 (boardtype != 0xFFFF || boardrev != 0xFFFF)) {
833 DBUSERR(("No matching NVRAM for boardtype 0x%02x boardrev 0x%02x\n",
834 boardtype, boardrev));
835 return DBUS_ERR_NVRAM;
836 }
837 dhd_bus->nvram = nvram;
838 dhd_bus->nvram_len = nvram_len;
839
840 return DBUS_OK;
841 }
842
843 /** before commencing fw download, the correct NVRAM image to download has to be
844 * picked */
dbus_get_nvram(dhd_bus_t * dhd_bus)845 static int dbus_get_nvram(dhd_bus_t *dhd_bus)
846 {
847 int len, i;
848 struct trx_header *hdr;
849 int actual_fwlen;
850 uint32 img_offset = 0;
851
852 dhd_bus->nvram_len = 0;
853 if (dhd_bus->extdl.varslen) {
854 if (DBUS_OK != dbus_jumbo_nvram(dhd_bus)) {
855 return DBUS_ERR_NVRAM;
856 }
857 DBUSERR(("NVRAM %d bytes downloaded\n", dhd_bus->nvram_len));
858 }
859 #if defined(BCM_REQUEST_FW)
860 else if (nonfwnvram) {
861 dhd_bus->nvram = nonfwnvram;
862 dhd_bus->nvram_len = nonfwnvramlen;
863 DBUSERR(("NVRAM %d bytes downloaded\n", dhd_bus->nvram_len));
864 }
865 #endif
866 if (dhd_bus->nvram) {
867 uint8 nvram_words_pad = 0;
868 /* Validate the format/length etc of the file */
869 if ((actual_fwlen = check_file(dhd_bus->pub.osh, dhd_bus->fw)) <= 0) {
870 DBUSERR(("%s: bad firmware format!\n", __FUNCTION__));
871 return DBUS_ERR_NVRAM;
872 }
873
874 if (!dhd_bus->nvram_nontxt) {
875 /* host supplied nvram could be in .txt format
876 * with all the comments etc...
877 */
878 dhd_bus->nvram_len =
879 process_nvram_vars(dhd_bus->nvram, dhd_bus->nvram_len);
880 }
881 if (dhd_bus->nvram_len % 0x4) {
882 nvram_words_pad = 0x4 - dhd_bus->nvram_len % 0x4;
883 }
884
885 len = actual_fwlen + dhd_bus->nvram_len + nvram_words_pad;
886 dhd_bus->image = MALLOC(dhd_bus->pub.osh, len);
887 dhd_bus->image_len = len;
888 if (dhd_bus->image == NULL) {
889 DBUSERR(("%s: malloc failed!\n", __FUNCTION__));
890 return DBUS_ERR_NVRAM;
891 }
892 hdr = (struct trx_header *)dhd_bus->fw;
893 /* Step1: Copy trx header + firmwre */
894 img_offset = SIZEOF_TRX(hdr) + hdr->offsets[TRX_OFFSETS_DLFWLEN_IDX];
895 bcopy(dhd_bus->fw, dhd_bus->image, img_offset);
896 /* Step2: Copy NVRAM + pad */
897 bcopy(dhd_bus->nvram, (uint8 *)(dhd_bus->image + img_offset),
898 dhd_bus->nvram_len);
899 img_offset += dhd_bus->nvram_len;
900 if (nvram_words_pad) {
901 bzero(&dhd_bus->image[img_offset], nvram_words_pad);
902 img_offset += nvram_words_pad;
903 }
904 #ifdef BCMTRXV2
905 /* Step3: Copy DSG/CFG for V2 */
906 if (ISTRX_V2(hdr) && (hdr->offsets[TRX_OFFSETS_DSG_LEN_IDX] ||
907 hdr->offsets[TRX_OFFSETS_CFG_LEN_IDX])) {
908 bcopy(dhd_bus->fw + SIZEOF_TRX(hdr) +
909 hdr->offsets[TRX_OFFSETS_DLFWLEN_IDX] +
910 hdr->offsets[TRX_OFFSETS_NVM_LEN_IDX],
911 dhd_bus->image + img_offset,
912 hdr->offsets[TRX_OFFSETS_DSG_LEN_IDX] +
913 hdr->offsets[TRX_OFFSETS_CFG_LEN_IDX]);
914
915 img_offset += hdr->offsets[TRX_OFFSETS_DSG_LEN_IDX] +
916 hdr->offsets[TRX_OFFSETS_CFG_LEN_IDX];
917 }
918 #endif /* BCMTRXV2 */
919 /* Step4: update TRX header for nvram size */
920 hdr = (struct trx_header *)dhd_bus->image;
921 hdr->len = htol32(len);
922 /* Pass the actual fw len */
923 hdr->offsets[TRX_OFFSETS_NVM_LEN_IDX] =
924 htol32(dhd_bus->nvram_len + nvram_words_pad);
925 /* Calculate CRC over header */
926 hdr->crc32 = hndcrc32((uint8 *)&hdr->flag_version,
927 SIZEOF_TRX(hdr) -
928 OFFSETOF(struct trx_header, flag_version),
929 CRC32_INIT_VALUE);
930
931 /* Calculate CRC over data */
932 for (i = SIZEOF_TRX(hdr); i < len; ++i) {
933 hdr->crc32 = hndcrc32((uint8 *)&dhd_bus->image[i], 1, hdr->crc32);
934 }
935 hdr->crc32 = htol32(hdr->crc32);
936 } else {
937 dhd_bus->image = dhd_bus->fw;
938 dhd_bus->image_len = (uint32)dhd_bus->fwlen;
939 }
940
941 return DBUS_OK;
942 } /* dbus_get_nvram */
943
944 /**
945 * during driver initialization ('attach') or after PnP 'resume', firmware needs
946 * to be loaded into the dongle
947 */
dbus_do_download(dhd_bus_t * dhd_bus)948 static int dbus_do_download(dhd_bus_t *dhd_bus)
949 {
950 int err = DBUS_OK;
951 #ifndef BCM_REQUEST_FW
952 int decomp_override = 0;
953 #endif
954 #ifdef BCM_REQUEST_FW
955 uint16 boardrev = 0xFFFF, boardtype = 0xFFFF;
956 int8 *temp_nvram;
957 int temp_len;
958 #endif
959
960 #if defined(BCM_REQUEST_FW)
961 dhd_bus->firmware = dbus_get_fw_nvfile(
962 dhd_bus->pub.attrib.devid, dhd_bus->pub.attrib.chiprev, &dhd_bus->fw,
963 &dhd_bus->fwlen, DBUS_FIRMWARE, 0, 0);
964 if (!dhd_bus->firmware) {
965 return DBUS_ERR;
966 }
967 #endif
968
969 dhd_bus->image = dhd_bus->fw;
970 dhd_bus->image_len = (uint32)dhd_bus->fwlen;
971
972 #ifndef BCM_REQUEST_FW
973 if (UNZIP_ENAB(dhd_bus) && !decomp_override) {
974 err = dbus_zlib_decomp(dhd_bus);
975 if (err) {
976 DBUSERR(("dbus_attach: fw decompress fail %d\n", err));
977 return err;
978 }
979 }
980 #endif
981
982 #if defined(BCM_REQUEST_FW)
983 /* check if firmware is appended with nvram file */
984 err = dbus_otp(dhd_bus, &boardtype, &boardrev);
985 /* check if nvram is provided as separte file */
986 nonfwnvram = NULL;
987 nonfwnvramlen = 0;
988 dhd_bus->nvfile = dbus_get_fw_nvfile(
989 dhd_bus->pub.attrib.devid, dhd_bus->pub.attrib.chiprev,
990 (void *)&temp_nvram, &temp_len, DBUS_NVFILE, boardtype, boardrev);
991 if (dhd_bus->nvfile) {
992 int8 *tmp = MALLOC(dhd_bus->pub.osh, temp_len);
993 if (tmp) {
994 bcopy(temp_nvram, tmp, temp_len);
995 nonfwnvram = tmp;
996 nonfwnvramlen = temp_len;
997 } else {
998 err = DBUS_ERR;
999 goto fail;
1000 }
1001 }
1002 #endif /* defined(BCM_REQUEST_FW) */
1003
1004 err = dbus_get_nvram(dhd_bus);
1005 if (err) {
1006 DBUSERR(("dbus_do_download: fail to get nvram %d\n", err));
1007 return err;
1008 }
1009
1010 if (dhd_bus->drvintf->dlstart && dhd_bus->drvintf->dlrun) {
1011 err = dhd_bus->drvintf->dlstart(dhd_bus->bus_info, dhd_bus->image,
1012 dhd_bus->image_len);
1013 if (err == DBUS_OK) {
1014 err = dhd_bus->drvintf->dlrun(dhd_bus->bus_info);
1015 }
1016 } else {
1017 err = DBUS_ERR;
1018 }
1019
1020 if (dhd_bus->nvram) {
1021 MFREE(dhd_bus->pub.osh, dhd_bus->image, dhd_bus->image_len);
1022 dhd_bus->image = dhd_bus->fw;
1023 dhd_bus->image_len = (uint32)dhd_bus->fwlen;
1024 }
1025
1026 #ifndef BCM_REQUEST_FW
1027 if (UNZIP_ENAB(dhd_bus) && (!decomp_override) && dhd_bus->orig_fw) {
1028 MFREE(dhd_bus->pub.osh, dhd_bus->fw, dhd_bus->decomp_memsize);
1029 dhd_bus->image = dhd_bus->fw = dhd_bus->orig_fw;
1030 dhd_bus->image_len = dhd_bus->fwlen = dhd_bus->origfw_len;
1031 }
1032 #endif
1033
1034 #if defined(BCM_REQUEST_FW)
1035 fail:
1036 if (dhd_bus->firmware) {
1037 dbus_release_fw_nvfile(dhd_bus->firmware);
1038 dhd_bus->firmware = NULL;
1039 }
1040 if (dhd_bus->nvfile) {
1041 dbus_release_fw_nvfile(dhd_bus->nvfile);
1042 dhd_bus->nvfile = NULL;
1043 }
1044 if (nonfwnvram) {
1045 MFREE(dhd_bus->pub.osh, nonfwnvram, nonfwnvramlen);
1046 nonfwnvram = NULL;
1047 nonfwnvramlen = 0;
1048 }
1049 #endif
1050 return err;
1051 } /* dbus_do_download */
1052 #endif /* EXTERNAL_FW_PATH */
1053 #endif
1054
1055 /** required for DBUS deregistration */
dbus_disconnect(void * handle)1056 static void dbus_disconnect(void *handle)
1057 {
1058 DBUSTRACE(("%s\n", __FUNCTION__));
1059
1060 if (disconnect_cb) {
1061 disconnect_cb(disc_arg);
1062 }
1063 }
1064
1065 /**
1066 * This function is called when the sent irb times out without a tx response
1067 * status. DBUS adds reliability by resending timed out IRBs DBUS_TX_RETRY_LIMIT
1068 * times.
1069 */
dbus_if_send_irb_timeout(void * handle,dbus_irb_tx_t * txirb)1070 static void dbus_if_send_irb_timeout(void *handle, dbus_irb_tx_t *txirb)
1071 {
1072 dhd_bus_t *dhd_bus = (dhd_bus_t *)handle;
1073
1074 if ((dhd_bus == NULL) || (dhd_bus->drvintf == NULL) || (txirb == NULL)) {
1075 return;
1076 }
1077
1078 DBUSTRACE(("%s\n", __FUNCTION__));
1079
1080 return;
1081 } /* dbus_if_send_irb_timeout */
1082
1083 /**
1084 * When lower DBUS level signals that a send IRB completed, either successful or
1085 * not, the higher level (e.g. dhd_linux.c) has to be notified, and transmit
1086 * flow control has to be evaluated.
1087 */
dbus_if_send_irb_complete(void * handle,dbus_irb_tx_t * txirb,int status)1088 static void BCMFASTPATH dbus_if_send_irb_complete(void *handle,
1089 dbus_irb_tx_t *txirb,
1090 int status)
1091 {
1092 dhd_bus_t *dhd_bus = (dhd_bus_t *)handle;
1093 int txirb_pending;
1094 struct exec_parms args;
1095 void *pktinfo;
1096
1097 if ((dhd_bus == NULL) || (txirb == NULL)) {
1098 return;
1099 }
1100
1101 DBUSTRACE(("%s: status = %d\n", __FUNCTION__, status));
1102
1103 dbus_tx_timer_stop(dhd_bus);
1104
1105 /* re-queue BEFORE calling send_complete which will assume that this irb
1106 is now available.
1107 */
1108 pktinfo = txirb->info;
1109 bzero(txirb, sizeof(dbus_irb_tx_t));
1110 args.qenq.q = dhd_bus->tx_q;
1111 args.qenq.b = (dbus_irb_t *)txirb;
1112 EXEC_TXLOCK(dhd_bus, q_enq_exec, &args);
1113
1114 if (dhd_bus->pub.busstate != DBUS_STATE_DOWN) {
1115 if ((status == DBUS_OK) || (status == DBUS_ERR_NODEVICE)) {
1116 if (dhd_bus->cbs && dhd_bus->cbs->send_complete) {
1117 dhd_bus->cbs->send_complete(dhd_bus->cbarg, pktinfo, status);
1118 }
1119
1120 if (status == DBUS_OK) {
1121 txirb_pending = dhd_bus->pub.ntxq - dhd_bus->tx_q->cnt;
1122 if (txirb_pending) {
1123 dbus_tx_timer_start(dhd_bus, DBUS_TX_TIMEOUT_INTERVAL);
1124 }
1125 if ((txirb_pending < dhd_bus->tx_low_watermark) &&
1126 dhd_bus->txoff && !dhd_bus->txoverride) {
1127 dbus_flowctrl_tx(dhd_bus, OFF);
1128 }
1129 }
1130 } else {
1131 DBUSERR(("%s: %d WARNING freeing orphan pkt %p\n", __FUNCTION__,
1132 __LINE__, pktinfo));
1133 #if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_TXNOCOPY) || defined(BCM_RPC_TOC)
1134 if (pktinfo) {
1135 if (dhd_bus->cbs && dhd_bus->cbs->send_complete) {
1136 dhd_bus->cbs->send_complete(dhd_bus->cbarg, pktinfo,
1137 status);
1138 }
1139 }
1140 #else
1141 dbus_if_pktfree(dhd_bus, (void *)pktinfo, TRUE);
1142 #endif /* defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_TXNOCOPY) || \
1143 defined(BCM_RPC_TOC) */
1144 }
1145 } else {
1146 DBUSERR(("%s: %d WARNING freeing orphan pkt %p\n", __FUNCTION__,
1147 __LINE__, pktinfo));
1148 #if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_TXNOCOPY) || defined(BCM_RPC_TOC)
1149 if (pktinfo) {
1150 if (dhd_bus->cbs && dhd_bus->cbs->send_complete) {
1151 dhd_bus->cbs->send_complete(dhd_bus->cbarg, pktinfo, status);
1152 }
1153 }
1154 #else
1155 dbus_if_pktfree(dhd_bus, (void *)pktinfo, TRUE);
1156 #endif /* defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_TXNOCOPY) \
1157 defined(BCM_RPC_TOC) */
1158 }
1159 } /* dbus_if_send_irb_complete */
1160
1161 /**
1162 * When lower DBUS level signals that a receive IRB completed, either successful
1163 * or not, the higher level (e.g. dhd_linux.c) has to be notified, and fresh
1164 * free receive IRBs may have to be given to lower levels.
1165 */
dbus_if_recv_irb_complete(void * handle,dbus_irb_rx_t * rxirb,int status)1166 static void BCMFASTPATH dbus_if_recv_irb_complete(void *handle,
1167 dbus_irb_rx_t *rxirb,
1168 int status)
1169 {
1170 dhd_bus_t *dhd_bus = (dhd_bus_t *)handle;
1171 int rxirb_pending;
1172 struct exec_parms args;
1173
1174 if ((dhd_bus == NULL) || (rxirb == NULL)) {
1175 return;
1176 }
1177 DBUSTRACE(("%s\n", __FUNCTION__));
1178 if (dhd_bus->pub.busstate != DBUS_STATE_DOWN &&
1179 dhd_bus->pub.busstate != DBUS_STATE_SLEEP) {
1180 if (status == DBUS_OK) {
1181 if ((rxirb->buf != NULL) && (rxirb->actual_len > 0)) {
1182 #ifdef DBUS_USB_LOOPBACK
1183 if (is_loopback_pkt(rxirb->buf)) {
1184 matches_loopback_pkt(rxirb->buf);
1185 } else
1186 #endif
1187 if (dhd_bus->cbs && dhd_bus->cbs->recv_buf) {
1188 dhd_bus->cbs->recv_buf(dhd_bus->cbarg, rxirb->buf,
1189 rxirb->actual_len);
1190 }
1191 } else if (rxirb->pkt != NULL) {
1192 if (dhd_bus->cbs && dhd_bus->cbs->recv_pkt) {
1193 dhd_bus->cbs->recv_pkt(dhd_bus->cbarg, rxirb->pkt);
1194 }
1195 } else {
1196 ASSERT(0); /* Should not happen */
1197 }
1198
1199 rxirb_pending = dhd_bus->pub.nrxq - dhd_bus->rx_q->cnt - 1;
1200 if ((rxirb_pending <= dhd_bus->rx_low_watermark) &&
1201 !dhd_bus->rxoff) {
1202 DBUSTRACE(("Low watermark so submit more %d <= %d \n",
1203 dhd_bus->rx_low_watermark, rxirb_pending));
1204 dbus_rxirbs_fill(dhd_bus);
1205 } else if (dhd_bus->rxoff) {
1206 DBUSTRACE(("rx flow controlled. not filling more. cut_rxq=%d\n",
1207 dhd_bus->rx_q->cnt));
1208 }
1209 } else if (status == DBUS_ERR_NODEVICE) {
1210 DBUSERR(("%s: %d status = %d, buf %p\n", __FUNCTION__, __LINE__,
1211 status, rxirb->buf));
1212 #if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY)
1213 if (rxirb->buf) {
1214 PKTFRMNATIVE(dhd_bus->pub.osh, rxirb->buf);
1215 PKTFREE(dhd_bus->pub.osh, rxirb->buf, FALSE);
1216 }
1217 #endif /* BCM_RPC_NOCOPY || BCM_RPC_TXNOCOPY || BCM_RPC_TOC */
1218 } else {
1219 if (status != DBUS_ERR_RXZLP) {
1220 DBUSERR(("%s: %d status = %d, buf %p\n", __FUNCTION__, __LINE__,
1221 status, rxirb->buf));
1222 }
1223 #if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY)
1224 if (rxirb->buf) {
1225 PKTFRMNATIVE(dhd_bus->pub.osh, rxirb->buf);
1226 PKTFREE(dhd_bus->pub.osh, rxirb->buf, FALSE);
1227 }
1228 #endif /* BCM_RPC_NOCOPY || BCM_RPC_TXNOCOPY || BCM_RPC_TOC */
1229 }
1230 } else {
1231 DBUSTRACE(("%s: DBUS down, ignoring recv callback. buf %p\n",
1232 __FUNCTION__, rxirb->buf));
1233 #if defined(BCM_RPC_NOCOPY) || defined(BCM_RPC_RXNOCOPY)
1234 if (rxirb->buf) {
1235 PKTFRMNATIVE(dhd_bus->pub.osh, rxirb->buf);
1236 PKTFREE(dhd_bus->pub.osh, rxirb->buf, FALSE);
1237 }
1238 #endif /* BCM_RPC_NOCOPY || BCM_RPC_TXNOCOPY || BCM_RPC_TOC */
1239 }
1240 if (dhd_bus->rx_q != NULL) {
1241 bzero(rxirb, sizeof(dbus_irb_rx_t));
1242 args.qenq.q = dhd_bus->rx_q;
1243 args.qenq.b = (dbus_irb_t *)rxirb;
1244 EXEC_RXLOCK(dhd_bus, q_enq_exec, &args);
1245 } else {
1246 MFREE(dhd_bus->pub.osh, rxirb, sizeof(dbus_irb_tx_t));
1247 }
1248 } /* dbus_if_recv_irb_complete */
1249
1250 /**
1251 * Accumulate errors signaled by lower DBUS levels and signal them to higher
1252 * (e.g. dhd_linux.c) level.
1253 */
dbus_if_errhandler(void * handle,int err)1254 static void dbus_if_errhandler(void *handle, int err)
1255 {
1256 dhd_bus_t *dhd_bus = handle;
1257 uint32 mask = 0;
1258
1259 if (dhd_bus == NULL) {
1260 return;
1261 }
1262
1263 switch (err) {
1264 case DBUS_ERR_TXFAIL:
1265 dhd_bus->pub.stats.tx_errors++;
1266 mask |= ERR_CBMASK_TXFAIL;
1267 break;
1268 case DBUS_ERR_TXDROP:
1269 dhd_bus->pub.stats.tx_dropped++;
1270 mask |= ERR_CBMASK_TXFAIL;
1271 break;
1272 case DBUS_ERR_RXFAIL:
1273 dhd_bus->pub.stats.rx_errors++;
1274 mask |= ERR_CBMASK_RXFAIL;
1275 break;
1276 case DBUS_ERR_RXDROP:
1277 dhd_bus->pub.stats.rx_dropped++;
1278 mask |= ERR_CBMASK_RXFAIL;
1279 break;
1280 default:
1281 break;
1282 }
1283
1284 if (dhd_bus->cbs && dhd_bus->cbs->errhandler && (dhd_bus->errmask & mask)) {
1285 dhd_bus->cbs->errhandler(dhd_bus->cbarg, err);
1286 }
1287 }
1288
1289 /**
1290 * When lower DBUS level signals control IRB completed, higher level (e.g.
1291 * dhd_linux.c) has to be notified.
1292 */
dbus_if_ctl_complete(void * handle,int type,int status)1293 static void dbus_if_ctl_complete(void *handle, int type, int status)
1294 {
1295 dhd_bus_t *dhd_bus = (dhd_bus_t *)handle;
1296
1297 DBUSTRACE(("%s\n", __FUNCTION__));
1298
1299 if (dhd_bus == NULL) {
1300 DBUSERR(("%s: dhd_bus is NULL\n", __FUNCTION__));
1301 return;
1302 }
1303
1304 if (dhd_bus->pub.busstate != DBUS_STATE_DOWN) {
1305 if (dhd_bus->cbs && dhd_bus->cbs->ctl_complete) {
1306 dhd_bus->cbs->ctl_complete(dhd_bus->cbarg, type, status);
1307 }
1308 }
1309 }
1310
1311 /**
1312 * Rx related functionality (flow control, posting of free IRBs to rx queue) is
1313 * dependent upon the bus state. When lower DBUS level signals a change in the
1314 * interface state, take appropriate action and forward the signaling to the
1315 * higher (e.g. dhd_linux.c) level.
1316 */
dbus_if_state_change(void * handle,int state)1317 static void dbus_if_state_change(void *handle, int state)
1318 {
1319 dhd_bus_t *dhd_bus = (dhd_bus_t *)handle;
1320 int old_state;
1321
1322 if (dhd_bus == NULL) {
1323 return;
1324 }
1325
1326 if (dhd_bus->pub.busstate == state) {
1327 return;
1328 }
1329 old_state = dhd_bus->pub.busstate;
1330 if (state == DBUS_STATE_DISCONNECT) {
1331 DBUSERR(("DBUS disconnected\n"));
1332 }
1333
1334 /* Ignore USB SUSPEND while not up yet */
1335 if (state == DBUS_STATE_SLEEP && old_state != DBUS_STATE_UP) {
1336 return;
1337 }
1338
1339 DBUSTRACE(("dbus state change from %d to to %d\n", old_state, state));
1340
1341 /* Don't update state if it's PnP firmware re-download */
1342 if (state != DBUS_STATE_PNP_FWDL) {
1343 dhd_bus->pub.busstate = state;
1344 } else {
1345 dbus_flowctrl_rx(handle, FALSE);
1346 }
1347 if (state == DBUS_STATE_SLEEP) {
1348 dbus_flowctrl_rx(handle, TRUE);
1349 }
1350 if (state == DBUS_STATE_UP) {
1351 dbus_rxirbs_fill(dhd_bus);
1352 dbus_flowctrl_rx(handle, FALSE);
1353 }
1354
1355 if (dhd_bus->cbs && dhd_bus->cbs->state_change) {
1356 dhd_bus->cbs->state_change(dhd_bus->cbarg, state);
1357 }
1358 }
1359
1360 /** Forward request for packet from lower DBUS layer to higher layer (e.g.
1361 * dhd_linux.c) */
dbus_if_pktget(void * handle,uint len,bool send)1362 static void *dbus_if_pktget(void *handle, uint len, bool send)
1363 {
1364 dhd_bus_t *dhd_bus = (dhd_bus_t *)handle;
1365 void *p = NULL;
1366
1367 if (dhd_bus == NULL) {
1368 return NULL;
1369 }
1370
1371 if (dhd_bus->cbs && dhd_bus->cbs->pktget) {
1372 p = dhd_bus->cbs->pktget(dhd_bus->cbarg, len, send);
1373 } else {
1374 ASSERT(0);
1375 }
1376
1377 return p;
1378 }
1379
1380 /** Forward request to free packet from lower DBUS layer to higher layer (e.g.
1381 * dhd_linux.c) */
dbus_if_pktfree(void * handle,void * p,bool send)1382 static void dbus_if_pktfree(void *handle, void *p, bool send)
1383 {
1384 dhd_bus_t *dhd_bus = (dhd_bus_t *)handle;
1385
1386 if (dhd_bus == NULL) {
1387 return;
1388 }
1389
1390 if (dhd_bus->cbs && dhd_bus->cbs->pktfree) {
1391 dhd_bus->cbs->pktfree(dhd_bus->cbarg, p, send);
1392 } else {
1393 ASSERT(0);
1394 }
1395 }
1396
1397 /** Lower DBUS level requests either a send or receive IRB */
dbus_if_getirb(void * cbarg,bool send)1398 static struct dbus_irb *dbus_if_getirb(void *cbarg, bool send)
1399 {
1400 dhd_bus_t *dhd_bus = (dhd_bus_t *)cbarg;
1401 struct exec_parms args;
1402 struct dbus_irb *irb;
1403
1404 if ((dhd_bus == NULL) || (dhd_bus->pub.busstate != DBUS_STATE_UP)) {
1405 return NULL;
1406 }
1407
1408 if (send == TRUE) {
1409 args.qdeq.q = dhd_bus->tx_q;
1410 irb = EXEC_TXLOCK(dhd_bus, q_deq_exec, &args);
1411 } else {
1412 args.qdeq.q = dhd_bus->rx_q;
1413 irb = EXEC_RXLOCK(dhd_bus, q_deq_exec, &args);
1414 }
1415
1416 return irb;
1417 }
1418
1419 /**
1420 * Called as part of DBUS bus registration. Calls back into higher level (e.g.
1421 * dhd_linux.c) probe function.
1422 */
dbus_probe(void * arg,const char * desc,uint32 bustype,uint16 bus_no,uint16 slot,uint32 hdrlen)1423 static void *dbus_probe(void *arg, const char *desc, uint32 bustype,
1424 uint16 bus_no, uint16 slot, uint32 hdrlen)
1425 {
1426 DBUSTRACE(("%s\n", __FUNCTION__));
1427 if (probe_cb) {
1428 disc_arg = probe_cb(probe_arg, desc, bustype, bus_no, slot, hdrlen);
1429 return disc_arg;
1430 }
1431
1432 return (void *)DBUS_ERR;
1433 }
1434
1435 /**
1436 * As part of initialization, higher level (e.g. dhd_linux.c) requests DBUS to
1437 * prepare for action.
1438 */
dhd_bus_register(void)1439 int dhd_bus_register(void)
1440 {
1441 int err;
1442
1443 DBUSTRACE(("%s: Enter\n", __FUNCTION__));
1444
1445 probe_cb = dhd_dbus_probe_cb;
1446 disconnect_cb = dhd_dbus_disconnect_cb;
1447 probe_arg = NULL;
1448
1449 err = dbus_bus_register(
1450 0xa5c, 0x48f, dbus_probe, /* call lower DBUS level register function */
1451 dbus_disconnect, NULL, &g_busintf, NULL, NULL);
1452 /* Device not detected */
1453 if (err == DBUS_ERR_NODEVICE) {
1454 err = DBUS_OK;
1455 }
1456
1457 return err;
1458 }
1459
1460 dhd_pub_t *g_pub = NULL;
dhd_bus_unregister(void)1461 void dhd_bus_unregister(void)
1462 {
1463 int ret;
1464
1465 DBUSTRACE(("%s\n", __FUNCTION__));
1466
1467 DHD_MUTEX_LOCK();
1468 if (g_pub) {
1469 g_pub->dhd_remove = TRUE;
1470 if (!g_pub->bus) {
1471 dhd_dbus_disconnect_cb(g_pub->bus);
1472 }
1473 }
1474 probe_cb = NULL;
1475 DHD_MUTEX_UNLOCK();
1476 ret = dbus_bus_deregister();
1477 disconnect_cb = NULL;
1478 probe_arg = NULL;
1479 }
1480
1481 /** As part of initialization, data structures have to be allocated and
1482 * initialized */
dbus_attach(osl_t * osh,int rxsize,int nrxq,int ntxq,dhd_pub_t * pub,dbus_callbacks_t * cbs,dbus_extdl_t * extdl,struct shared_info * sh)1483 dhd_bus_t *dbus_attach(osl_t *osh, int rxsize, int nrxq, int ntxq,
1484 dhd_pub_t *pub, dbus_callbacks_t *cbs,
1485 dbus_extdl_t *extdl, struct shared_info *sh)
1486 {
1487 dhd_bus_t *dhd_bus;
1488 int err;
1489
1490 if ((g_busintf == NULL) || (g_busintf->attach == NULL) || (cbs == NULL)) {
1491 return NULL;
1492 }
1493
1494 DBUSTRACE(("%s\n", __FUNCTION__));
1495
1496 if ((nrxq <= 0) || (ntxq <= 0)) {
1497 return NULL;
1498 }
1499
1500 dhd_bus = MALLOC(osh, sizeof(dhd_bus_t));
1501 if (dhd_bus == NULL) {
1502 DBUSERR(("%s: malloc failed %zu\n", __FUNCTION__, sizeof(dhd_bus_t)));
1503 return NULL;
1504 }
1505
1506 bzero(dhd_bus, sizeof(dhd_bus_t));
1507
1508 /* BUS-specific driver interface (at a lower DBUS level) */
1509 dhd_bus->drvintf = g_busintf;
1510 dhd_bus->cbarg = pub;
1511 dhd_bus->cbs = cbs;
1512
1513 dhd_bus->pub.sh = sh;
1514 dhd_bus->pub.osh = osh;
1515 dhd_bus->pub.rxsize = rxsize;
1516
1517 dhd_bus->pub.nrxq = nrxq;
1518 dhd_bus->rx_low_watermark = nrxq / 0x2; /* keep enough posted rx urbs */
1519 dhd_bus->pub.ntxq = ntxq;
1520 dhd_bus->tx_low_watermark =
1521 ntxq / 0x4; /* flow control when too many tx urbs posted */
1522
1523 dhd_bus->tx_q = MALLOC(osh, sizeof(dbus_irbq_t));
1524 if (dhd_bus->tx_q == NULL) {
1525 goto error;
1526 } else {
1527 bzero(dhd_bus->tx_q, sizeof(dbus_irbq_t));
1528 err =
1529 dbus_irbq_init(dhd_bus, dhd_bus->tx_q, ntxq, sizeof(dbus_irb_tx_t));
1530 if (err != DBUS_OK) {
1531 goto error;
1532 }
1533 }
1534
1535 dhd_bus->rx_q = MALLOC(osh, sizeof(dbus_irbq_t));
1536 if (dhd_bus->rx_q == NULL) {
1537 goto error;
1538 } else {
1539 bzero(dhd_bus->rx_q, sizeof(dbus_irbq_t));
1540 err =
1541 dbus_irbq_init(dhd_bus, dhd_bus->rx_q, nrxq, sizeof(dbus_irb_rx_t));
1542 if (err != DBUS_OK) {
1543 goto error;
1544 }
1545 }
1546
1547 dhd_bus->bus_info =
1548 (void *)g_busintf->attach(&dhd_bus->pub, dhd_bus, &dbus_intf_cbs);
1549 if (dhd_bus->bus_info == NULL) {
1550 goto error;
1551 }
1552
1553 dbus_tx_timer_init(dhd_bus);
1554
1555 #if defined(BCM_REQUEST_FW)
1556 /* Need to copy external image for re-download */
1557 if (extdl && extdl->fw && (extdl->fwlen > 0)) {
1558 dhd_bus->extdl.fw = MALLOC(osh, extdl->fwlen);
1559 if (dhd_bus->extdl.fw) {
1560 bcopy(extdl->fw, dhd_bus->extdl.fw, extdl->fwlen);
1561 dhd_bus->extdl.fwlen = extdl->fwlen;
1562 }
1563 }
1564
1565 if (extdl && extdl->vars && (extdl->varslen > 0)) {
1566 dhd_bus->extdl.vars = MALLOC(osh, extdl->varslen);
1567 if (dhd_bus->extdl.vars) {
1568 bcopy(extdl->vars, dhd_bus->extdl.vars, extdl->varslen);
1569 dhd_bus->extdl.varslen = extdl->varslen;
1570 }
1571 }
1572 #endif
1573
1574 return (dhd_bus_t *)dhd_bus;
1575
1576 error:
1577 DBUSERR(("%s: Failed\n", __FUNCTION__));
1578 dbus_detach(dhd_bus);
1579 return NULL;
1580 } /* dbus_attach */
1581
dbus_detach(dhd_bus_t * pub)1582 void dbus_detach(dhd_bus_t *pub)
1583 {
1584 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1585 osl_t *osh;
1586
1587 DBUSTRACE(("%s\n", __FUNCTION__));
1588
1589 if (dhd_bus == NULL) {
1590 return;
1591 }
1592
1593 dbus_tx_timer_stop(dhd_bus);
1594
1595 osh = pub->pub.osh;
1596
1597 if (dhd_bus->drvintf && dhd_bus->drvintf->detach) {
1598 dhd_bus->drvintf->detach((dbus_pub_t *)dhd_bus, dhd_bus->bus_info);
1599 }
1600
1601 if (dhd_bus->tx_q) {
1602 dbus_irbq_deinit(dhd_bus, dhd_bus->tx_q, sizeof(dbus_irb_tx_t));
1603 MFREE(osh, dhd_bus->tx_q, sizeof(dbus_irbq_t));
1604 dhd_bus->tx_q = NULL;
1605 }
1606
1607 if (dhd_bus->rx_q) {
1608 dbus_irbq_deinit(dhd_bus, dhd_bus->rx_q, sizeof(dbus_irb_rx_t));
1609 MFREE(osh, dhd_bus->rx_q, sizeof(dbus_irbq_t));
1610 dhd_bus->rx_q = NULL;
1611 }
1612
1613 if (dhd_bus->extdl.fw && (dhd_bus->extdl.fwlen > 0)) {
1614 MFREE(osh, dhd_bus->extdl.fw, dhd_bus->extdl.fwlen);
1615 dhd_bus->extdl.fw = NULL;
1616 dhd_bus->extdl.fwlen = 0;
1617 }
1618
1619 if (dhd_bus->extdl.vars && (dhd_bus->extdl.varslen > 0)) {
1620 MFREE(osh, dhd_bus->extdl.vars, dhd_bus->extdl.varslen);
1621 dhd_bus->extdl.vars = NULL;
1622 dhd_bus->extdl.varslen = 0;
1623 }
1624
1625 MFREE(osh, dhd_bus, sizeof(dhd_bus_t));
1626 } /* dbus_detach */
1627
dbus_dlneeded(dhd_bus_t * pub)1628 int dbus_dlneeded(dhd_bus_t *pub)
1629 {
1630 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1631 int dlneeded = DBUS_ERR;
1632
1633 if (!dhd_bus) {
1634 DBUSERR(("%s: dhd_bus is NULL\n", __FUNCTION__));
1635 return DBUS_ERR;
1636 }
1637
1638 DBUSTRACE(("%s: state %d\n", __FUNCTION__, dhd_bus->pub.busstate));
1639
1640 if (dhd_bus->drvintf->dlneeded) {
1641 dlneeded = dhd_bus->drvintf->dlneeded(dhd_bus->bus_info);
1642 }
1643 printf("%s: dlneeded=%d\n", __FUNCTION__, dlneeded);
1644
1645 /* dlneeded > 0: need to download
1646 * dlneeded = 0: downloaded
1647 * dlneeded < 0: bus error */
1648 return dlneeded;
1649 }
1650
1651 #if defined(BCM_REQUEST_FW)
dbus_download_firmware(dhd_bus_t * pub,char * pfw_path,char * pnv_path)1652 int dbus_download_firmware(dhd_bus_t *pub, char *pfw_path, char *pnv_path)
1653 {
1654 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1655 int err = DBUS_OK;
1656
1657 if (!dhd_bus) {
1658 DBUSERR(("%s: dhd_bus is NULL\n", __FUNCTION__));
1659 return DBUS_ERR;
1660 }
1661
1662 DBUSTRACE(("%s: state %d\n", __FUNCTION__, dhd_bus->pub.busstate));
1663
1664 dhd_bus->pub.busstate = DBUS_STATE_DL_PENDING;
1665 #ifdef EXTERNAL_FW_PATH
1666 err = dbus_do_download(dhd_bus, pfw_path, pnv_path);
1667 #else
1668 err = dbus_do_download(dhd_bus);
1669 #endif /* EXTERNAL_FW_PATH */
1670 if (err == DBUS_OK) {
1671 dhd_bus->pub.busstate = DBUS_STATE_DL_DONE;
1672 } else {
1673 DBUSERR(("%s: download failed (%d)\n", __FUNCTION__, err));
1674 }
1675
1676 return err;
1677 }
1678 #endif
1679
1680 /**
1681 * higher layer requests us to 'up' the interface to the dongle. Prerequisite is
1682 * that firmware (not bootloader) must be active in the dongle.
1683 */
dbus_up(struct dhd_bus * pub)1684 int dbus_up(struct dhd_bus *pub)
1685 {
1686 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1687 int err = DBUS_OK;
1688
1689 DBUSTRACE(("%s\n", __FUNCTION__));
1690
1691 if (dhd_bus == NULL) {
1692 DBUSERR(("%s: dhd_bus is NULL\n", __FUNCTION__));
1693 return DBUS_ERR;
1694 }
1695
1696 if ((dhd_bus->pub.busstate == DBUS_STATE_DL_DONE) ||
1697 (dhd_bus->pub.busstate == DBUS_STATE_DOWN) ||
1698 (dhd_bus->pub.busstate == DBUS_STATE_SLEEP)) {
1699 if (dhd_bus->drvintf && dhd_bus->drvintf->up) {
1700 err = dhd_bus->drvintf->up(dhd_bus->bus_info);
1701 if (err == DBUS_OK) {
1702 dbus_rxirbs_fill(dhd_bus);
1703 }
1704 }
1705 } else {
1706 err = DBUS_ERR;
1707 }
1708 return err;
1709 }
1710
1711 /** higher layer requests us to 'down' the interface to the dongle. */
dbus_down(dbus_pub_t * pub)1712 int dbus_down(dbus_pub_t *pub)
1713 {
1714 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1715
1716 DBUSTRACE(("%s\n", __FUNCTION__));
1717
1718 if (dhd_bus == NULL) {
1719 return DBUS_ERR;
1720 }
1721
1722 dbus_tx_timer_stop(dhd_bus);
1723
1724 if (dhd_bus->pub.busstate == DBUS_STATE_UP ||
1725 dhd_bus->pub.busstate == DBUS_STATE_SLEEP) {
1726 if (dhd_bus->drvintf && dhd_bus->drvintf->down) {
1727 return dhd_bus->drvintf->down(dhd_bus->bus_info);
1728 }
1729 }
1730
1731 return DBUS_ERR;
1732 }
1733
dbus_shutdown(dbus_pub_t * pub)1734 int dbus_shutdown(dbus_pub_t *pub)
1735 {
1736 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1737
1738 DBUSTRACE(("%s\n", __FUNCTION__));
1739
1740 if (dhd_bus == NULL) {
1741 return DBUS_ERR;
1742 }
1743
1744 if (dhd_bus->drvintf && dhd_bus->drvintf->shutdown) {
1745 return dhd_bus->drvintf->shutdown(dhd_bus->bus_info);
1746 }
1747
1748 return DBUS_OK;
1749 }
1750
dbus_stop(struct dhd_bus * pub)1751 int dbus_stop(struct dhd_bus *pub)
1752 {
1753 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1754
1755 DBUSTRACE(("%s\n", __FUNCTION__));
1756
1757 if (dhd_bus == NULL) {
1758 return DBUS_ERR;
1759 }
1760
1761 if (dhd_bus->pub.busstate == DBUS_STATE_UP ||
1762 dhd_bus->pub.busstate == DBUS_STATE_SLEEP) {
1763 if (dhd_bus->drvintf && dhd_bus->drvintf->stop) {
1764 return dhd_bus->drvintf->stop(dhd_bus->bus_info);
1765 }
1766 }
1767
1768 return DBUS_ERR;
1769 }
1770
dbus_send_txdata(dbus_pub_t * dbus,void * pktbuf)1771 int dbus_send_txdata(dbus_pub_t *dbus, void *pktbuf)
1772 {
1773 return dbus_send_pkt(dbus, pktbuf, pktbuf /* pktinfo */);
1774 }
1775
dbus_send_buf(dbus_pub_t * pub,uint8 * buf,int len,void * info)1776 int dbus_send_buf(dbus_pub_t *pub, uint8 *buf, int len, void *info)
1777 {
1778 return dbus_send_irb(pub, buf, len, NULL, info);
1779 }
1780
dbus_send_pkt(dbus_pub_t * pub,void * pkt,void * info)1781 int dbus_send_pkt(dbus_pub_t *pub, void *pkt, void *info)
1782 {
1783 return dbus_send_irb(pub, NULL, 0, pkt, info);
1784 }
1785
dbus_send_ctl(struct dhd_bus * pub,uint8 * buf,int len)1786 int dbus_send_ctl(struct dhd_bus *pub, uint8 *buf, int len)
1787 {
1788 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1789
1790 if (dhd_bus == NULL) {
1791 DBUSERR(("%s: dhd_bus is NULL\n", __FUNCTION__));
1792 return DBUS_ERR;
1793 }
1794
1795 if (dhd_bus->pub.busstate == DBUS_STATE_UP ||
1796 dhd_bus->pub.busstate == DBUS_STATE_SLEEP) {
1797 if (dhd_bus->drvintf && dhd_bus->drvintf->send_ctl) {
1798 return dhd_bus->drvintf->send_ctl(dhd_bus->bus_info, buf, len);
1799 }
1800 } else {
1801 DBUSERR(("%s: bustate=%d\n", __FUNCTION__, dhd_bus->pub.busstate));
1802 }
1803
1804 return DBUS_ERR;
1805 }
1806
dbus_recv_ctl(struct dhd_bus * pub,uint8 * buf,int len)1807 int dbus_recv_ctl(struct dhd_bus *pub, uint8 *buf, int len)
1808 {
1809 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1810
1811 if ((dhd_bus == NULL) || (buf == NULL)) {
1812 return DBUS_ERR;
1813 }
1814
1815 if (dhd_bus->pub.busstate == DBUS_STATE_UP ||
1816 dhd_bus->pub.busstate == DBUS_STATE_SLEEP) {
1817 if (dhd_bus->drvintf && dhd_bus->drvintf->recv_ctl) {
1818 return dhd_bus->drvintf->recv_ctl(dhd_bus->bus_info, buf, len);
1819 }
1820 }
1821
1822 return DBUS_ERR;
1823 }
1824
1825 /** Only called via RPC (Dec 2012) */
dbus_recv_bulk(dbus_pub_t * pub,uint32 ep_idx)1826 int dbus_recv_bulk(dbus_pub_t *pub, uint32 ep_idx)
1827 {
1828 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1829
1830 dbus_irb_rx_t *rxirb;
1831 struct exec_parms args;
1832 int status;
1833
1834 if (dhd_bus == NULL) {
1835 return DBUS_ERR;
1836 }
1837
1838 args.qdeq.q = dhd_bus->rx_q;
1839 if (dhd_bus->pub.busstate == DBUS_STATE_UP) {
1840 if (dhd_bus->drvintf && dhd_bus->drvintf->recv_irb_from_ep) {
1841 if ((rxirb = (EXEC_RXLOCK(dhd_bus, q_deq_exec, &args))) != NULL) {
1842 status = dhd_bus->drvintf->recv_irb_from_ep(dhd_bus->bus_info,
1843 rxirb, ep_idx);
1844 if (status == DBUS_ERR_RXDROP) {
1845 bzero(rxirb, sizeof(dbus_irb_rx_t));
1846 args.qenq.q = dhd_bus->rx_q;
1847 args.qenq.b = (dbus_irb_t *)rxirb;
1848 EXEC_RXLOCK(dhd_bus, q_enq_exec, &args);
1849 }
1850 }
1851 }
1852 }
1853
1854 return DBUS_ERR;
1855 }
1856
1857 /** only called by dhd_cdc.c (Dec 2012) */
dbus_poll_intr(dbus_pub_t * pub)1858 int dbus_poll_intr(dbus_pub_t *pub)
1859 {
1860 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1861
1862 int status = DBUS_ERR;
1863
1864 if (dhd_bus == NULL) {
1865 return DBUS_ERR;
1866 }
1867
1868 if (dhd_bus->pub.busstate == DBUS_STATE_UP) {
1869 if (dhd_bus->drvintf && dhd_bus->drvintf->recv_irb_from_ep) {
1870 status = dhd_bus->drvintf->recv_irb_from_ep(dhd_bus->bus_info, NULL,
1871 0xff);
1872 }
1873 }
1874 return status;
1875 }
1876
1877 /** called by nobody (Dec 2012) */
dbus_pktget(dbus_pub_t * pub,int len)1878 void *dbus_pktget(dbus_pub_t *pub, int len)
1879 {
1880 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1881
1882 if ((dhd_bus == NULL) || (len < 0)) {
1883 return NULL;
1884 }
1885
1886 return PKTGET(dhd_bus->pub.osh, len, TRUE);
1887 }
1888
1889 /** called by nobody (Dec 2012) */
dbus_pktfree(dbus_pub_t * pub,void * pkt)1890 void dbus_pktfree(dbus_pub_t *pub, void *pkt)
1891 {
1892 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1893
1894 if ((dhd_bus == NULL) || (pkt == NULL)) {
1895 return;
1896 }
1897
1898 PKTFREE(dhd_bus->pub.osh, pkt, TRUE);
1899 }
1900
1901 /** called by nobody (Dec 2012) */
dbus_get_stats(dbus_pub_t * pub,dbus_stats_t * stats)1902 int dbus_get_stats(dbus_pub_t *pub, dbus_stats_t *stats)
1903 {
1904 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1905
1906 if ((dhd_bus == NULL) || (stats == NULL)) {
1907 return DBUS_ERR;
1908 }
1909
1910 bcopy(&dhd_bus->pub.stats, stats, sizeof(dbus_stats_t));
1911
1912 return DBUS_OK;
1913 }
1914
dbus_get_attrib(dhd_bus_t * pub,dbus_attrib_t * attrib)1915 int dbus_get_attrib(dhd_bus_t *pub, dbus_attrib_t *attrib)
1916 {
1917 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1918 int err = DBUS_ERR;
1919
1920 if ((dhd_bus == NULL) || (attrib == NULL)) {
1921 return DBUS_ERR;
1922 }
1923
1924 if (dhd_bus->drvintf && dhd_bus->drvintf->get_attrib) {
1925 err = dhd_bus->drvintf->get_attrib(dhd_bus->bus_info,
1926 &dhd_bus->pub.attrib);
1927 }
1928
1929 bcopy(&dhd_bus->pub.attrib, attrib, sizeof(dbus_attrib_t));
1930 return err;
1931 }
1932
dbus_get_device_speed(dbus_pub_t * pub)1933 int dbus_get_device_speed(dbus_pub_t *pub)
1934 {
1935 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1936
1937 if (dhd_bus == NULL) {
1938 return INVALID_SPEED;
1939 }
1940
1941 return (dhd_bus->pub.device_speed);
1942 }
1943
dbus_set_config(dbus_pub_t * pub,dbus_config_t * config)1944 int dbus_set_config(dbus_pub_t *pub, dbus_config_t *config)
1945 {
1946 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1947 int err = DBUS_ERR;
1948
1949 if ((dhd_bus == NULL) || (config == NULL)) {
1950 return DBUS_ERR;
1951 }
1952
1953 if (dhd_bus->drvintf && dhd_bus->drvintf->set_config) {
1954 err = dhd_bus->drvintf->set_config(dhd_bus->bus_info, config);
1955 if ((config->config_id == DBUS_CONFIG_ID_AGGR_LIMIT) && (!err) &&
1956 (dhd_bus->pub.busstate == DBUS_STATE_UP)) {
1957 dbus_rxirbs_fill(dhd_bus);
1958 }
1959 }
1960 return err;
1961 }
1962
dbus_get_config(dbus_pub_t * pub,dbus_config_t * config)1963 int dbus_get_config(dbus_pub_t *pub, dbus_config_t *config)
1964 {
1965 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1966 int err = DBUS_ERR;
1967
1968 if ((dhd_bus == NULL) || (config == NULL)) {
1969 return DBUS_ERR;
1970 }
1971
1972 if (dhd_bus->drvintf && dhd_bus->drvintf->get_config) {
1973 err = dhd_bus->drvintf->get_config(dhd_bus->bus_info, config);
1974 }
1975
1976 return err;
1977 }
1978
dbus_set_errmask(dbus_pub_t * pub,uint32 mask)1979 int dbus_set_errmask(dbus_pub_t *pub, uint32 mask)
1980 {
1981 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1982 int err = DBUS_OK;
1983
1984 if (dhd_bus == NULL) {
1985 return DBUS_ERR;
1986 }
1987
1988 dhd_bus->errmask = mask;
1989 return err;
1990 }
1991
dbus_pnp_resume(dbus_pub_t * pub,int * fw_reload)1992 int dbus_pnp_resume(dbus_pub_t *pub, int *fw_reload)
1993 {
1994 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
1995 int err = DBUS_ERR;
1996 bool fwdl = FALSE;
1997
1998 DBUSTRACE(("%s\n", __FUNCTION__));
1999
2000 if (dhd_bus == NULL) {
2001 return DBUS_ERR;
2002 }
2003
2004 if (dhd_bus->pub.busstate == DBUS_STATE_UP) {
2005 return DBUS_OK;
2006 }
2007
2008 if (dhd_bus->drvintf->pnp) {
2009 err = dhd_bus->drvintf->pnp(dhd_bus->bus_info, DBUS_PNP_RESUME);
2010 }
2011
2012 if (dhd_bus->drvintf->recv_needed) {
2013 if (dhd_bus->drvintf->recv_needed(dhd_bus->bus_info)) {
2014 /* Refill after sleep/hibernate */
2015 dbus_rxirbs_fill(dhd_bus);
2016 }
2017 }
2018
2019 if (fw_reload) {
2020 *fw_reload = fwdl;
2021 }
2022
2023 return err;
2024 } /* dbus_pnp_resume */
2025
dbus_pnp_sleep(dbus_pub_t * pub)2026 int dbus_pnp_sleep(dbus_pub_t *pub)
2027 {
2028 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
2029 int err = DBUS_ERR;
2030
2031 DBUSTRACE(("%s\n", __FUNCTION__));
2032
2033 if (dhd_bus == NULL) {
2034 return DBUS_ERR;
2035 }
2036
2037 dbus_tx_timer_stop(dhd_bus);
2038
2039 if (dhd_bus->drvintf && dhd_bus->drvintf->pnp) {
2040 err = dhd_bus->drvintf->pnp(dhd_bus->bus_info, DBUS_PNP_SLEEP);
2041 }
2042
2043 return err;
2044 }
2045
dbus_pnp_disconnect(dbus_pub_t * pub)2046 int dbus_pnp_disconnect(dbus_pub_t *pub)
2047 {
2048 dhd_bus_t *dhd_bus = (dhd_bus_t *)pub;
2049 int err = DBUS_ERR;
2050
2051 DBUSTRACE(("%s\n", __FUNCTION__));
2052
2053 if (dhd_bus == NULL) {
2054 return DBUS_ERR;
2055 }
2056
2057 dbus_tx_timer_stop(dhd_bus);
2058
2059 if (dhd_bus->drvintf && dhd_bus->drvintf->pnp) {
2060 err = dhd_bus->drvintf->pnp(dhd_bus->bus_info, DBUS_PNP_DISCONNECT);
2061 }
2062
2063 return err;
2064 }
2065
dhd_bus_iovar_op(dhd_pub_t * dhdp,const char * name,void * params,int plen,void * arg,int len,bool set)2066 int dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name, void *params, int plen,
2067 void *arg, int len, bool set)
2068 {
2069 dhd_bus_t *dhd_bus = (dhd_bus_t *)dhdp->bus;
2070 int err = DBUS_ERR;
2071
2072 DBUSTRACE(("%s\n", __FUNCTION__));
2073
2074 if (dhd_bus == NULL) {
2075 return DBUS_ERR;
2076 }
2077
2078 if (dhd_bus->drvintf && dhd_bus->drvintf->iovar_op) {
2079 err = dhd_bus->drvintf->iovar_op(dhd_bus->bus_info, name, params, plen,
2080 arg, len, set);
2081 }
2082
2083 return err;
2084 }
2085
dhd_dbus_txq(const dbus_pub_t * pub)2086 void *dhd_dbus_txq(const dbus_pub_t *pub)
2087 {
2088 return NULL;
2089 }
2090
dhd_dbus_hdrlen(const dbus_pub_t * pub)2091 uint dhd_dbus_hdrlen(const dbus_pub_t *pub)
2092 {
2093 return 0;
2094 }
2095
dbus_get_devinfo(dbus_pub_t * pub)2096 void *dbus_get_devinfo(dbus_pub_t *pub)
2097 {
2098 return pub->dev_info;
2099 }
2100
2101 #if defined(BCM_REQUEST_FW) && !defined(EXTERNAL_FW_PATH)
dbus_otp(dhd_bus_t * dhd_bus,uint16 * boardtype,uint16 * boardrev)2102 static int dbus_otp(dhd_bus_t *dhd_bus, uint16 *boardtype, uint16 *boardrev)
2103 {
2104 uint32 value = 0;
2105 uint8 *cis;
2106 uint16 *otpinfo;
2107 uint32 i;
2108 bool standard_cis = TRUE;
2109 uint8 tup, tlen;
2110 bool btype_present = FALSE;
2111 bool brev_present = FALSE;
2112 int ret;
2113 int devid;
2114 uint16 btype = 0;
2115 uint16 brev = 0;
2116 uint32 otp_size = 0, otp_addr = 0, otp_sw_rgn = 0;
2117
2118 if (dhd_bus == NULL || dhd_bus->drvintf == NULL ||
2119 dhd_bus->drvintf->readreg == NULL) {
2120 return DBUS_ERR;
2121 }
2122
2123 devid = dhd_bus->pub.attrib.devid;
2124
2125 if ((devid == BCM43234_CHIP_ID) || (devid == BCM43235_CHIP_ID) ||
2126 (devid == BCM43236_CHIP_ID)) {
2127 otp_size = BCM_OTP_SIZE_43236;
2128 otp_sw_rgn = BCM_OTP_SW_RGN_43236;
2129 otp_addr = BCM_OTP_ADDR_43236;
2130 } else {
2131 return DBUS_ERR_NVRAM;
2132 }
2133
2134 cis = MALLOC(dhd_bus->pub.osh, otp_size * 0x2);
2135 if (cis == NULL) {
2136 return DBUS_ERR;
2137 }
2138
2139 otpinfo = (uint16 *)cis;
2140
2141 for (i = 0; i < otp_size; i++) {
2142 ret = dhd_bus->drvintf->readreg(
2143 dhd_bus->bus_info, otp_addr + ((otp_sw_rgn + i) << 1), 0x2, &value);
2144 if (ret != DBUS_OK) {
2145 MFREE(dhd_bus->pub.osh, cis, otp_size * 0x2);
2146 return ret;
2147 }
2148 otpinfo[i] = (uint16)value;
2149 }
2150
2151 for (i = 0; i < (otp_size << 1);) {
2152 if (standard_cis) {
2153 tup = cis[i++];
2154 if (tup == CISTPL_NULL || tup == CISTPL_END) {
2155 tlen = 0;
2156 } else {
2157 tlen = cis[i++];
2158 }
2159 } else {
2160 if (cis[i] == CISTPL_NULL || cis[i] == CISTPL_END) {
2161 tlen = 0;
2162 tup = cis[i];
2163 } else {
2164 tlen = cis[i];
2165 tup = CISTPL_BRCM_HNBU;
2166 }
2167 ++i;
2168 }
2169
2170 if (tup == CISTPL_END || (i + tlen) >= (otp_size << 1)) {
2171 break;
2172 }
2173
2174 if (tup == CISTPL_BRCM_HNBU) {
2175 switch (cis[i]) {
2176 case HNBU_BOARDTYPE:
2177 btype = (uint16)((cis[i + 0x2] << 0x8) + cis[i + 1]);
2178 btype_present = TRUE;
2179 DBUSTRACE(("%s: HNBU_BOARDTYPE = 0x%2x\n", __FUNCTION__,
2180 (uint32)btype));
2181 break;
2182 case HNBU_BOARDREV:
2183 if (tlen == 0x2) {
2184 brev = (uint16)cis[i + 1];
2185 } else {
2186 brev = (uint16)((cis[i + 0x2] << 0x8) + cis[i + 1]);
2187 }
2188 brev_present = TRUE;
2189 DBUSTRACE(("%s: HNBU_BOARDREV = 0x%2x\n", __FUNCTION__,
2190 (uint32)*boardrev));
2191 break;
2192 case HNBU_HNBUCIS:
2193 DBUSTRACE(("%s: HNBU_HNBUCIS\n", __FUNCTION__));
2194 tlen++;
2195 standard_cis = FALSE;
2196 break;
2197 }
2198 }
2199 i += tlen;
2200 }
2201
2202 MFREE(dhd_bus->pub.osh, cis, otp_size * 0x2);
2203
2204 if (btype_present == TRUE && brev_present == TRUE) {
2205 *boardtype = btype;
2206 *boardrev = brev;
2207 DBUSERR(("otp boardtype = 0x%2x boardrev = 0x%2x\n", *boardtype,
2208 *boardrev));
2209
2210 return DBUS_OK;
2211 } else {
2212 return DBUS_ERR;
2213 }
2214 } /* dbus_otp */
2215
dbus_select_nvram(dhd_bus_t * dhd_bus,int8 * jumbonvram,int jumbolen,uint16 boardtype,uint16 boardrev,int8 ** nvram,int * nvram_len)2216 static int dbus_select_nvram(dhd_bus_t *dhd_bus, int8 *jumbonvram, int jumbolen,
2217 uint16 boardtype, uint16 boardrev, int8 **nvram,
2218 int *nvram_len)
2219 {
2220 /* Multi board nvram file format is contenation of nvram info with \r
2221 * The file format for two contatenated set is
2222 * \nBroadcom Jumbo Nvram file\nfirst_set\nsecond_set\nthird_set\n
2223 */
2224 uint8 *nvram_start = NULL, *nvram_end = NULL;
2225 uint8 *nvram_start_prev = NULL, *nvram_end_prev = NULL;
2226 uint16 btype = 0, brev = 0;
2227 int len = 0;
2228 char *field;
2229
2230 *nvram = NULL;
2231 *nvram_len = 0;
2232
2233 if (strncmp(BCM_JUMBO_START, jumbonvram, strlen(BCM_JUMBO_START))) {
2234 /* single nvram file in the native format */
2235 DBUSTRACE(("%s: Non-Jumbo NVRAM File \n", __FUNCTION__));
2236 *nvram = jumbonvram;
2237 *nvram_len = jumbolen;
2238 return DBUS_OK;
2239 } else {
2240 DBUSTRACE(("%s: Jumbo NVRAM File \n", __FUNCTION__));
2241 }
2242
2243 /* sanity test the end of the config sets for proper ending */
2244 if (jumbonvram[jumbolen - 1] != BCM_JUMBO_NVRAM_DELIMIT ||
2245 jumbonvram[jumbolen - 0x2] != '\0') {
2246 DBUSERR(("%s: Bad Jumbo NVRAM file format\n", __FUNCTION__));
2247 return DBUS_JUMBO_BAD_FORMAT;
2248 }
2249
2250 dhd_bus->nvram_nontxt = DBUS_NVRAM_NONTXT;
2251
2252 nvram_start = jumbonvram;
2253
2254 while (*nvram_start != BCM_JUMBO_NVRAM_DELIMIT && len < jumbolen) {
2255 /* consume the first file info line
2256 * \nBroadcom Jumbo Nvram file\nfile1\n ...
2257 */
2258 len++;
2259 nvram_start++;
2260 }
2261
2262 nvram_end = nvram_start;
2263
2264 /* search for "boardrev=0xabcd" and "boardtype=0x1234" information in
2265 * the concatenated nvram config files /sets
2266 */
2267
2268 while (len < jumbolen) {
2269 if (*nvram_end == '\0') {
2270 /* end of a config set is marked by multiple null characters */
2271 len++;
2272 nvram_end++;
2273 DBUSTRACE(("%s: NULL chr len = %d char = 0x%x\n", __FUNCTION__, len,
2274 *nvram_end));
2275 continue;
2276 } else if (*nvram_end == BCM_JUMBO_NVRAM_DELIMIT) {
2277 /* config set delimiter is reached */
2278 /* check if next config set is present or not
2279 * return if next config is not present
2280 */
2281
2282 /* start search the next config set */
2283 nvram_start_prev = nvram_start;
2284 nvram_end_prev = nvram_end;
2285
2286 nvram_end++;
2287 nvram_start = nvram_end;
2288 btype = brev = 0;
2289 DBUSTRACE(("%s: going to next record len = %d "
2290 "char = 0x%x \n",
2291 __FUNCTION__, len, *nvram_end));
2292 len++;
2293 if (len >= jumbolen) {
2294 *nvram = nvram_start_prev;
2295 *nvram_len = (int)(nvram_end_prev - nvram_start_prev);
2296
2297 DBUSTRACE(("%s: no more len = %d nvram_end = 0x%p",
2298 __FUNCTION__, len, nvram_end));
2299
2300 return DBUS_JUMBO_NOMATCH;
2301 } else {
2302 continue;
2303 }
2304 } else {
2305 DBUSTRACE(("%s: config str = %s\n", __FUNCTION__, nvram_end));
2306 if (bcmp(nvram_end, "boardtype", strlen("boardtype")) == 0) {
2307 field = strchr(nvram_end, '=');
2308 field++;
2309 btype = (uint16)bcm_strtoul(field, NULL, 0);
2310
2311 DBUSTRACE(("%s: btype = 0x%x boardtype = 0x%x \n", __FUNCTION__,
2312 btype, boardtype));
2313 }
2314
2315 if (bcmp(nvram_end, "boardrev", strlen("boardrev")) == 0) {
2316 field = strchr(nvram_end, '=');
2317 field++;
2318 brev = (uint16)bcm_strtoul(field, NULL, 0);
2319
2320 DBUSTRACE(("%s: brev = 0x%x boardrev = 0x%x \n", __FUNCTION__,
2321 brev, boardrev));
2322 }
2323 if (btype == boardtype && brev == boardrev) {
2324 /* locate nvram config set end - ie.find '\r' char */
2325 while (*nvram_end != BCM_JUMBO_NVRAM_DELIMIT) {
2326 nvram_end++;
2327 }
2328 *nvram = nvram_start;
2329 *nvram_len = (int)(nvram_end - nvram_start);
2330 DBUSTRACE(("found len = %d nvram_start = 0x%p "
2331 "nvram_end = 0x%p\n",
2332 *nvram_len, nvram_start, nvram_end));
2333 return DBUS_OK;
2334 }
2335
2336 len += (strlen(nvram_end) + 1);
2337 nvram_end += (strlen(nvram_end) + 1);
2338 }
2339 }
2340 return DBUS_JUMBO_NOMATCH;
2341 } /* dbus_select_nvram */
2342
2343 #endif
2344
2345 #define DBUS_NRXQ 50
2346 #define DBUS_NTXQ 100
2347
dhd_dbus_send_complete(void * handle,void * info,int status)2348 static void dhd_dbus_send_complete(void *handle, void *info, int status)
2349 {
2350 dhd_pub_t *dhd = (dhd_pub_t *)handle;
2351 void *pkt = info;
2352
2353 if ((dhd == NULL) || (pkt == NULL)) {
2354 DBUSERR(("dhd or pkt is NULL\n"));
2355 return;
2356 }
2357
2358 if (status == DBUS_OK) {
2359 dhd->dstats.tx_packets++;
2360 } else {
2361 DBUSERR(("TX error=%d\n", status));
2362 dhd->dstats.tx_errors++;
2363 }
2364 #ifdef PROP_TXSTATUS
2365 if (DHD_PKTTAG_WLFCPKT(PKTTAG(pkt)) &&
2366 (dhd_wlfc_txcomplete(dhd, pkt, status == 0) != WLFC_UNSUPPORTED)) {
2367 return;
2368 }
2369 #endif /* PROP_TXSTATUS */
2370 PKTFREE(dhd->osh, pkt, TRUE);
2371 }
2372
dhd_dbus_recv_pkt(void * handle,void * pkt)2373 static void dhd_dbus_recv_pkt(void *handle, void *pkt)
2374 {
2375 uchar reorder_info_buf[WLHOST_REORDERDATA_TOTLEN];
2376 uint reorder_info_len;
2377 uint pkt_count;
2378 dhd_pub_t *dhd = (dhd_pub_t *)handle;
2379 int ifidx = 0;
2380
2381 if (dhd == NULL) {
2382 DBUSERR(("%s: dhd is NULL\n", __FUNCTION__));
2383 return;
2384 }
2385
2386 /* If the protocol uses a data header, check and remove it */
2387 if (dhd_prot_hdrpull(dhd, &ifidx, pkt, reorder_info_buf,
2388 &reorder_info_len) != 0) {
2389 DBUSERR(("rx protocol error\n"));
2390 PKTFREE(dhd->osh, pkt, FALSE);
2391 dhd->rx_errors++;
2392 return;
2393 }
2394
2395 if (reorder_info_len) {
2396 /* Reordering info from the firmware */
2397 dhd_process_pkt_reorder_info(dhd, reorder_info_buf, reorder_info_len,
2398 &pkt, &pkt_count);
2399 if (pkt_count == 0) {
2400 return;
2401 }
2402 } else {
2403 pkt_count = 1;
2404 }
2405 dhd_rx_frame(dhd, ifidx, pkt, pkt_count, 0);
2406 }
2407
dhd_dbus_recv_buf(void * handle,uint8 * buf,int len)2408 static void dhd_dbus_recv_buf(void *handle, uint8 *buf, int len)
2409 {
2410 dhd_pub_t *dhd = (dhd_pub_t *)handle;
2411 void *pkt;
2412
2413 if (dhd == NULL) {
2414 DBUSERR(("%s: dhd is NULL\n", __FUNCTION__));
2415 return;
2416 }
2417
2418 if ((pkt = PKTGET(dhd->osh, len, FALSE)) == NULL) {
2419 DBUSERR(("PKTGET (rx) failed=%d\n", len));
2420 return;
2421 }
2422
2423 bcopy(buf, PKTDATA(dhd->osh, pkt), len);
2424 dhd_dbus_recv_pkt(dhd, pkt);
2425 }
2426
dhd_dbus_txflowcontrol(void * handle,bool onoff)2427 static void dhd_dbus_txflowcontrol(void *handle, bool onoff)
2428 {
2429 dhd_pub_t *dhd = (dhd_pub_t *)handle;
2430 bool wlfc_enabled = FALSE;
2431
2432 if (dhd == NULL) {
2433 DBUSERR(("%s: dhd is NULL\n", __FUNCTION__));
2434 return;
2435 }
2436
2437 #ifdef PROP_TXSTATUS
2438 wlfc_enabled =
2439 (dhd_wlfc_flowcontrol(dhd, onoff, !onoff) != WLFC_UNSUPPORTED);
2440 #endif
2441 if (!wlfc_enabled) {
2442 dhd_txflowcontrol(dhd, ALL_INTERFACES, onoff);
2443 }
2444 }
2445
dhd_dbus_errhandler(void * handle,int err)2446 static void dhd_dbus_errhandler(void *handle, int err)
2447 {
2448 }
2449
dhd_dbus_ctl_complete(void * handle,int type,int status)2450 static void dhd_dbus_ctl_complete(void *handle, int type, int status)
2451 {
2452 dhd_pub_t *dhd = (dhd_pub_t *)handle;
2453
2454 if (dhd == NULL) {
2455 DBUSERR(("%s: dhd is NULL\n", __FUNCTION__));
2456 return;
2457 }
2458
2459 if (type == DBUS_CBCTL_READ) {
2460 if (status == DBUS_OK) {
2461 dhd->rx_ctlpkts++;
2462 } else {
2463 dhd->rx_ctlerrs++;
2464 }
2465 } else if (type == DBUS_CBCTL_WRITE) {
2466 if (status == DBUS_OK) {
2467 dhd->tx_ctlpkts++;
2468 } else {
2469 dhd->tx_ctlerrs++;
2470 }
2471 }
2472
2473 dhd_prot_ctl_complete(dhd);
2474 }
2475
dhd_dbus_state_change(void * handle,int state)2476 static void dhd_dbus_state_change(void *handle, int state)
2477 {
2478 dhd_pub_t *dhd = (dhd_pub_t *)handle;
2479
2480 if (dhd == NULL) {
2481 DBUSERR(("%s: dhd is NULL\n", __FUNCTION__));
2482 return;
2483 }
2484
2485 switch (state) {
2486 case DBUS_STATE_DL_NEEDED:
2487 DBUSERR(("%s: firmware request cannot be handled\n", __FUNCTION__));
2488 break;
2489 case DBUS_STATE_DOWN:
2490 DBUSTRACE(("%s: DBUS is down\n", __FUNCTION__));
2491 dhd->busstate = DHD_BUS_DOWN;
2492 break;
2493 case DBUS_STATE_UP:
2494 DBUSTRACE(("%s: DBUS is up\n", __FUNCTION__));
2495 dhd->busstate = DHD_BUS_DATA;
2496 break;
2497 default:
2498 break;
2499 }
2500
2501 DBUSERR(("%s: DBUS current state=%d\n", __FUNCTION__, state));
2502 }
2503
dhd_dbus_pktget(void * handle,uint len,bool send)2504 static void *dhd_dbus_pktget(void *handle, uint len, bool send)
2505 {
2506 dhd_pub_t *dhd = (dhd_pub_t *)handle;
2507 void *p = NULL;
2508
2509 if (dhd == NULL) {
2510 DBUSERR(("%s: dhd is NULL\n", __FUNCTION__));
2511 return NULL;
2512 }
2513
2514 if (send == TRUE) {
2515 dhd_os_sdlock_txq(dhd);
2516 p = PKTGET(dhd->osh, len, TRUE);
2517 dhd_os_sdunlock_txq(dhd);
2518 } else {
2519 dhd_os_sdlock_rxq(dhd);
2520 p = PKTGET(dhd->osh, len, FALSE);
2521 dhd_os_sdunlock_rxq(dhd);
2522 }
2523
2524 return p;
2525 }
2526
dhd_dbus_pktfree(void * handle,void * p,bool send)2527 static void dhd_dbus_pktfree(void *handle, void *p, bool send)
2528 {
2529 dhd_pub_t *dhd = (dhd_pub_t *)handle;
2530
2531 if (dhd == NULL) {
2532 DBUSERR(("%s: dhd is NULL\n", __FUNCTION__));
2533 return;
2534 }
2535
2536 if (send == TRUE) {
2537 #ifdef PROP_TXSTATUS
2538 if (DHD_PKTTAG_WLFCPKT(PKTTAG(p)) &&
2539 (dhd_wlfc_txcomplete(dhd, p, FALSE) != WLFC_UNSUPPORTED)) {
2540 return;
2541 }
2542 #endif /* PROP_TXSTATUS */
2543
2544 dhd_os_sdlock_txq(dhd);
2545 PKTFREE(dhd->osh, p, TRUE);
2546 dhd_os_sdunlock_txq(dhd);
2547 } else {
2548 dhd_os_sdlock_rxq(dhd);
2549 PKTFREE(dhd->osh, p, FALSE);
2550 dhd_os_sdunlock_rxq(dhd);
2551 }
2552 }
2553
2554 static dbus_callbacks_t dhd_dbus_cbs = {
2555 dhd_dbus_send_complete, dhd_dbus_recv_buf, dhd_dbus_recv_pkt,
2556 dhd_dbus_txflowcontrol, dhd_dbus_errhandler, dhd_dbus_ctl_complete,
2557 dhd_dbus_state_change, dhd_dbus_pktget, dhd_dbus_pktfree};
2558
dhd_bus_chip(struct dhd_bus * bus)2559 uint dhd_bus_chip(struct dhd_bus *bus)
2560 {
2561 ASSERT(bus != NULL);
2562 return bus->pub.attrib.devid;
2563 }
2564
dhd_bus_chiprev(struct dhd_bus * bus)2565 uint dhd_bus_chiprev(struct dhd_bus *bus)
2566 {
2567 ASSERT(bus);
2568 ASSERT(bus != NULL);
2569 return bus->pub.attrib.chiprev;
2570 }
2571
dhd_bus_dump(dhd_pub_t * dhdp,struct bcmstrbuf * strbuf)2572 void dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
2573 {
2574 bcm_bprintf(strbuf, "Bus USB\n");
2575 }
2576
dhd_bus_clearcounts(dhd_pub_t * dhdp)2577 void dhd_bus_clearcounts(dhd_pub_t *dhdp)
2578 {
2579 }
2580
dhd_bus_txdata(struct dhd_bus * bus,void * pktbuf)2581 int dhd_bus_txdata(struct dhd_bus *bus, void *pktbuf)
2582 {
2583 DBUSTRACE(("%s\n", __FUNCTION__));
2584 if (bus->txoff) {
2585 DBUSTRACE(("txoff\n"));
2586 return BCME_EPERM;
2587 }
2588 return dbus_send_txdata(&bus->pub, pktbuf);
2589 }
2590
dhd_dbus_advertise_bus_cleanup(dhd_pub_t * dhdp)2591 static void dhd_dbus_advertise_bus_cleanup(dhd_pub_t *dhdp)
2592 {
2593 unsigned long flags;
2594 int timeleft;
2595
2596 DHD_LINUX_GENERAL_LOCK(dhdp, flags);
2597 dhdp->busstate = DHD_BUS_DOWN_IN_PROGRESS;
2598 DHD_LINUX_GENERAL_UNLOCK(dhdp, flags);
2599
2600 timeleft = dhd_os_busbusy_wait_negation(dhdp, &dhdp->dhd_bus_busy_state);
2601 if ((timeleft == 0) || (timeleft == 1)) {
2602 DBUSERR(("%s : Timeout due to dhd_bus_busy_state=0x%x\n", __FUNCTION__,
2603 dhdp->dhd_bus_busy_state));
2604 ASSERT(0);
2605 }
2606
2607 return;
2608 }
2609
dhd_dbus_advertise_bus_remove(dhd_pub_t * dhdp)2610 static void dhd_dbus_advertise_bus_remove(dhd_pub_t *dhdp)
2611 {
2612 unsigned long flags;
2613 int timeleft;
2614
2615 DHD_LINUX_GENERAL_LOCK(dhdp, flags);
2616 dhdp->busstate = DHD_BUS_REMOVE;
2617 DHD_LINUX_GENERAL_UNLOCK(dhdp, flags);
2618
2619 timeleft = dhd_os_busbusy_wait_negation(dhdp, &dhdp->dhd_bus_busy_state);
2620 if ((timeleft == 0) || (timeleft == 1)) {
2621 DBUSERR(("%s : Timeout due to dhd_bus_busy_state=0x%x\n", __FUNCTION__,
2622 dhdp->dhd_bus_busy_state));
2623 ASSERT(0);
2624 }
2625
2626 return;
2627 }
2628
dhd_bus_devreset(dhd_pub_t * dhdp,uint8 flag)2629 int dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag)
2630 {
2631 int bcmerror = 0;
2632 unsigned long flags;
2633 wifi_adapter_info_t *adapter = (wifi_adapter_info_t *)dhdp->adapter;
2634
2635 if (flag == TRUE) {
2636 if (!dhdp->dongle_reset) {
2637 DBUSERR(("%s: == Power OFF ==\n", __FUNCTION__));
2638 dhd_dbus_advertise_bus_cleanup(dhdp);
2639 dhd_os_wd_timer(dhdp, 0);
2640 #if !defined(IGNORE_ETH0_DOWN)
2641 /* Force flow control as protection when stop come before
2642 * ifconfig_down */
2643 dhd_txflowcontrol(dhdp, ALL_INTERFACES, ON);
2644 #endif /* !defined(IGNORE_ETH0_DOWN) */
2645 dbus_stop(dhdp->bus);
2646
2647 dhdp->dongle_reset = TRUE;
2648 dhdp->up = FALSE;
2649
2650 DHD_LINUX_GENERAL_LOCK(dhdp, flags);
2651 dhdp->busstate = DHD_BUS_DOWN;
2652 DHD_LINUX_GENERAL_UNLOCK(dhdp, flags);
2653 wifi_clr_adapter_status(adapter, WIFI_STATUS_FW_READY);
2654
2655 printf("%s: WLAN OFF DONE\n", __FUNCTION__);
2656 /* App can now remove power from device */
2657 } else {
2658 bcmerror = BCME_ERROR;
2659 }
2660 } else {
2661 /* App must have restored power to device before calling */
2662 printf("\n\n%s: == WLAN ON ==\n", __FUNCTION__);
2663 if (dhdp->dongle_reset) {
2664 /* Turn on WLAN */
2665 DHD_MUTEX_UNLOCK();
2666 wait_event_interruptible_timeout(
2667 adapter->status_event,
2668 wifi_get_adapter_status(adapter, WIFI_STATUS_FW_READY),
2669 msecs_to_jiffies(DHD_FW_READY_TIMEOUT));
2670 DHD_MUTEX_LOCK();
2671 bcmerror = dbus_up(dhdp->bus);
2672 if (bcmerror == BCME_OK) {
2673 dhdp->dongle_reset = FALSE;
2674 dhdp->up = TRUE;
2675 #if !defined(IGNORE_ETH0_DOWN)
2676 /* Restore flow control */
2677 dhd_txflowcontrol(dhdp, ALL_INTERFACES, OFF);
2678 #endif
2679 dhd_os_wd_timer(dhdp, dhd_watchdog_ms);
2680
2681 DBUSTRACE(("%s: WLAN ON DONE\n", __FUNCTION__));
2682 } else {
2683 DBUSERR(("%s: failed to dbus_up with code %d\n", __FUNCTION__,
2684 bcmerror));
2685 }
2686 }
2687 }
2688
2689 return bcmerror;
2690 }
2691
dhd_bus_update_fw_nv_path(struct dhd_bus * bus,char * pfw_path,char * pnv_path,char * pclm_path,char * pconf_path)2692 void dhd_bus_update_fw_nv_path(struct dhd_bus *bus, char *pfw_path,
2693 char *pnv_path, char *pclm_path,
2694 char *pconf_path)
2695 {
2696 DBUSTRACE(("%s\n", __FUNCTION__));
2697
2698 if (bus == NULL) {
2699 DBUSERR(("%s: bus is NULL\n", __FUNCTION__));
2700 return;
2701 }
2702
2703 bus->fw_path = pfw_path;
2704 bus->nv_path = pnv_path;
2705 bus->dhd->clm_path = pclm_path;
2706 bus->dhd->conf_path = pconf_path;
2707
2708 dhd_conf_set_path_params(bus->dhd, bus->fw_path, bus->nv_path);
2709 }
2710
2711 /*
2712 * hdrlen is space to reserve in pkt headroom for DBUS
2713 */
dhd_dbus_probe_cb(void * arg,const char * desc,uint32 bustype,uint16 bus_no,uint16 slot,uint32 hdrlen)2714 void *dhd_dbus_probe_cb(void *arg, const char *desc, uint32 bustype,
2715 uint16 bus_no, uint16 slot, uint32 hdrlen)
2716 {
2717 osl_t *osh = NULL;
2718 dhd_bus_t *bus = NULL;
2719 dhd_pub_t *pub = NULL;
2720 uint rxsz;
2721 int dlneeded = 0;
2722 wifi_adapter_info_t *adapter = NULL;
2723
2724 DBUSTRACE(("%s: Enter\n", __FUNCTION__));
2725
2726 adapter = dhd_wifi_platform_get_adapter(bustype, bus_no, slot);
2727
2728 if (!g_pub) {
2729 /* Ask the OS interface part for an OSL handle */
2730 if (!(osh = osl_attach(NULL, bustype, TRUE))) {
2731 DBUSERR(("%s: OSL attach failed\n", __FUNCTION__));
2732 goto fail;
2733 }
2734
2735 /* Attach to the dhd/OS interface */
2736 if (!(pub = dhd_attach(osh, bus, hdrlen, adapter))) {
2737 DBUSERR(("%s: dhd_attach failed\n", __FUNCTION__));
2738 goto fail;
2739 }
2740 } else {
2741 pub = g_pub;
2742 osh = pub->osh;
2743 }
2744
2745 if (pub->bus) {
2746 DBUSERR(("%s: wrong probe\n", __FUNCTION__));
2747 goto fail;
2748 }
2749
2750 rxsz = dhd_get_rxsz(pub);
2751 bus = dbus_attach(osh, rxsz, DBUS_NRXQ, DBUS_NTXQ, pub, &dhd_dbus_cbs, NULL,
2752 NULL);
2753 if (bus) {
2754 pub->bus = bus;
2755 bus->dhd = pub;
2756
2757 dlneeded = dbus_dlneeded(bus);
2758 if (dlneeded >= 0) {
2759 if (!g_pub) {
2760 dhd_conf_reset(pub);
2761 dhd_conf_set_chiprev(pub, bus->pub.attrib.devid,
2762 bus->pub.attrib.chiprev);
2763 dhd_conf_preinit(pub);
2764 }
2765 }
2766
2767 if (g_pub || dhd_download_fw_on_driverload) {
2768 if (dlneeded == 0) {
2769 wifi_set_adapter_status(adapter, WIFI_STATUS_FW_READY);
2770 #ifdef BCM_REQUEST_FW
2771 } else if (dlneeded > 0) {
2772 dhd_set_path(bus->dhd);
2773 if (dbus_download_firmware(bus, bus->fw_path, bus->nv_path) !=
2774 DBUS_OK) {
2775 goto fail;
2776 }
2777 #endif
2778 } else {
2779 goto fail;
2780 }
2781 }
2782 } else {
2783 DBUSERR(("%s: dbus_attach failed\n", __FUNCTION__));
2784 }
2785
2786 if (!g_pub) {
2787 /* Ok, have the per-port tell the stack we're open for business */
2788 if (dhd_attach_net(bus->dhd, TRUE) != 0) {
2789 DBUSERR(("%s: Net attach failed!!\n", __FUNCTION__));
2790 goto fail;
2791 }
2792 pub->hang_report = TRUE;
2793 #if defined(MULTIPLE_SUPPLICANT)
2794 wl_android_post_init(); // terence 20120530: fix critical section in
2795 // dhd_open and dhdsdio_probe
2796 #endif
2797 g_pub = pub;
2798 }
2799
2800 DBUSTRACE(("%s: Exit\n", __FUNCTION__));
2801 wifi_clr_adapter_status(adapter, WIFI_STATUS_DETTACH);
2802 wifi_set_adapter_status(adapter, WIFI_STATUS_ATTACH);
2803 wake_up_interruptible(&adapter->status_event);
2804 /* This is passed to dhd_dbus_disconnect_cb */
2805 return bus;
2806
2807 fail:
2808 if (pub && pub->bus) {
2809 dbus_detach(pub->bus);
2810 pub->bus = NULL;
2811 }
2812 /* Release resources in reverse order */
2813 if (!g_pub) {
2814 if (pub) {
2815 dhd_detach(pub);
2816 dhd_free(pub);
2817 }
2818 if (osh) {
2819 osl_detach(osh);
2820 }
2821 }
2822
2823 printf("%s: Failed\n", __FUNCTION__);
2824 return NULL;
2825 }
2826
dhd_dbus_disconnect_cb(void * arg)2827 void dhd_dbus_disconnect_cb(void *arg)
2828 {
2829 dhd_bus_t *bus = (dhd_bus_t *)arg;
2830 dhd_pub_t *pub = g_pub;
2831 osl_t *osh;
2832 wifi_adapter_info_t *adapter = NULL;
2833
2834 adapter = (wifi_adapter_info_t *)pub->adapter;
2835
2836 if (pub && !pub->dhd_remove && bus == NULL) {
2837 DBUSERR(("%s: bus is NULL\n", __FUNCTION__));
2838 return;
2839 }
2840 if (!adapter) {
2841 DBUSERR(("%s: adapter is NULL\n", __FUNCTION__));
2842 return;
2843 }
2844
2845 printf("%s: Enter dhd_remove=%d on %s\n", __FUNCTION__, pub->dhd_remove,
2846 adapter->name);
2847 if (!pub->dhd_remove) {
2848 /* Advertise bus remove during rmmod */
2849 dhd_dbus_advertise_bus_remove(bus->dhd);
2850 dbus_detach(pub->bus);
2851 pub->bus = NULL;
2852 wifi_clr_adapter_status(adapter, WIFI_STATUS_ATTACH);
2853 wifi_set_adapter_status(adapter, WIFI_STATUS_DETTACH);
2854 wake_up_interruptible(&adapter->status_event);
2855 } else {
2856 osh = pub->osh;
2857 dhd_detach(pub);
2858 if (pub->bus) {
2859 dbus_detach(pub->bus);
2860 pub->bus = NULL;
2861 }
2862 dhd_free(pub);
2863 g_pub = NULL;
2864 if (MALLOCED(osh)) {
2865 DBUSERR(
2866 ("%s: MEMORY LEAK %d bytes\n", __FUNCTION__, MALLOCED(osh)));
2867 }
2868 osl_detach(osh);
2869 }
2870
2871 DBUSTRACE(("%s: Exit\n", __FUNCTION__));
2872 }
2873
2874 #ifdef LINUX_EXTERNAL_MODULE_DBUS
2875
bcm_dbus_module_init(void)2876 static int __init bcm_dbus_module_init(void)
2877 {
2878 printf("Inserting bcm_dbus module \n");
2879 return 0;
2880 }
2881
bcm_dbus_module_exit(void)2882 static void __exit bcm_dbus_module_exit(void)
2883 {
2884 printf("Removing bcm_dbus module \n");
2885 return;
2886 }
2887
2888 EXPORT_SYMBOL(dbus_pnp_sleep);
2889 EXPORT_SYMBOL(dbus_get_devinfo);
2890 EXPORT_SYMBOL(dbus_detach);
2891 EXPORT_SYMBOL(dbus_get_attrib);
2892 EXPORT_SYMBOL(dbus_down);
2893 EXPORT_SYMBOL(dbus_pnp_resume);
2894 EXPORT_SYMBOL(dbus_set_config);
2895 EXPORT_SYMBOL(dbus_flowctrl_rx);
2896 EXPORT_SYMBOL(dbus_up);
2897 EXPORT_SYMBOL(dbus_get_device_speed);
2898 EXPORT_SYMBOL(dbus_send_pkt);
2899 EXPORT_SYMBOL(dbus_recv_ctl);
2900 EXPORT_SYMBOL(dbus_attach);
2901
2902 MODULE_LICENSE("GPL");
2903
2904 module_init(bcm_dbus_module_init);
2905 module_exit(bcm_dbus_module_exit);
2906
2907 #endif /* #ifdef LINUX_EXTERNAL_MODULE_DBUS */
2908