xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/dbus_usb.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * Dongle BUS interface for USB, OS independent
4  *
5  * Copyright (C) 1999-2016, Broadcom Corporation
6  *
7  *      Unless you and Broadcom execute a separate written software license
8  * agreement governing use of this software, this software is licensed to you
9  * under the terms of the GNU General Public License version 2 (the "GPL"),
10  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
11  * following added to such license:
12  *
13  *      As a special exception, the copyright holders of this software give you
14  * permission to link this software with independent modules, and to copy and
15  * distribute the resulting executable under terms of your choice, provided that
16  * you also meet, for each linked independent module, the terms and conditions of
17  * the license of that module.  An independent module is a module which is not
18  * derived from this software.  The special exception does not apply to any
19  * modifications of the software.
20  *
21  *      Notwithstanding the above, under no circumstances may you combine this
22  * software in any way with any other Broadcom software provided under a license
23  * other than the GPL, without Broadcom's express prior written consent.
24  *
25  *
26  * <<Broadcom-WL-IPTag/Open:>>
27  *
28  * $Id: dbus_usb.c 565557 2015-06-22 19:29:44Z $
29  */
30 
31 /**
32  * @file @brief
33  * This file contains DBUS code that is USB, but not OS specific. DBUS is a Broadcom proprietary
34  * host specific abstraction layer.
35  */
36 
37 #include <osl.h>
38 #include <bcmdefs.h>
39 #include <bcmutils.h>
40 #include <dbus.h>
41 #include <usbrdl.h>
42 #include <bcmdevs_legacy.h>
43 #include <bcmdevs.h>
44 #include <bcmendian.h>
45 
46 uint dbus_msglevel = DBUS_ERROR_VAL;
47 module_param(dbus_msglevel, int, 0);
48 
49 
50 #define USB_DLIMAGE_RETRY_TIMEOUT    3000    /* retry Timeout */
51 #define USB_SFLASH_DLIMAGE_SPINWAIT  150     /* in unit of ms */
52 #define USB_SFLASH_DLIMAGE_LIMIT     2000    /* spinwait limit (ms) */
53 #define POSTBOOT_ID                  0xA123  /* ID to detect if dongle has boot up */
54 #define USB_RESETCFG_SPINWAIT        1       /* wait after resetcfg (ms) */
55 #define USB_DEV_ISBAD(u)             (u->pub->attrib.devid == 0xDEAD)
56 #define USB_DLGO_SPINWAIT            100     /* wait after DL_GO (ms) */
57 #define TEST_CHIP                    0x4328
58 
59 typedef struct {
60 	dbus_pub_t  *pub;
61 
62 	void        *cbarg;
63 	dbus_intf_callbacks_t *cbs;  /** callbacks into higher DBUS level (dbus.c) */
64 	dbus_intf_t *drvintf;
65 	void        *usbosl_info;
66 	uint32      rdlram_base_addr;
67 	uint32      rdlram_size;
68 } usb_info_t;
69 
70 /*
71  * Callbacks common to all USB
72  */
73 static void dbus_usb_disconnect(void *handle);
74 static void dbus_usb_send_irb_timeout(void *handle, dbus_irb_tx_t *txirb);
75 static void dbus_usb_send_irb_complete(void *handle, dbus_irb_tx_t *txirb, int status);
76 static void dbus_usb_recv_irb_complete(void *handle, dbus_irb_rx_t *rxirb, int status);
77 static void dbus_usb_errhandler(void *handle, int err);
78 static void dbus_usb_ctl_complete(void *handle, int type, int status);
79 static void dbus_usb_state_change(void *handle, int state);
80 static struct dbus_irb* dbus_usb_getirb(void *handle, bool send);
81 static void dbus_usb_rxerr_indicate(void *handle, bool on);
82 #if !defined(BCM_REQUEST_FW)
83 static int dbus_usb_resetcfg(usb_info_t *usbinfo);
84 #endif
85 static int dbus_usb_iovar_op(void *bus, const char *name,
86 	void *params, int plen, void *arg, int len, bool set);
87 static int dbus_iovar_process(usb_info_t* usbinfo, const char *name,
88                  void *params, int plen, void *arg, int len, bool set);
89 static int dbus_usb_doiovar(usb_info_t *bus, const bcm_iovar_t *vi, uint32 actionid,
90 	const char *name, void *params, int plen, void *arg, int len, int val_size);
91 static int dhdusb_downloadvars(usb_info_t *bus, void *arg, int len);
92 
93 static int dbus_usb_dl_writeimage(usb_info_t *usbinfo, uint8 *fw, int fwlen);
94 static int dbus_usb_dlstart(void *bus, uint8 *fw, int len);
95 static int dbus_usb_dlneeded(void *bus);
96 static int dbus_usb_dlrun(void *bus);
97 static int dbus_usb_rdl_dwnld_state(usb_info_t *usbinfo);
98 
99 
100 /* OS specific */
101 extern bool dbus_usbos_dl_cmd(void *info, uint8 cmd, void *buffer, int buflen);
102 extern int dbus_usbos_wait(void *info, uint16 ms);
103 extern int dbus_write_membytes(usb_info_t *usbinfo, bool set, uint32 address,
104 	uint8 *data, uint size);
105 extern bool dbus_usbos_dl_send_bulk(void *info, void *buffer, int len);
106 extern int dbus_usbos_loopback_tx(void *usbos_info_ptr, int cnt, int size);
107 
108 /**
109  * These functions are called by the lower DBUS level (dbus_usb_os.c) to notify this DBUS level
110  * (dbus_usb.c) of an event.
111  */
112 static dbus_intf_callbacks_t dbus_usb_intf_cbs = {
113 	dbus_usb_send_irb_timeout,
114 	dbus_usb_send_irb_complete,
115 	dbus_usb_recv_irb_complete,
116 	dbus_usb_errhandler,
117 	dbus_usb_ctl_complete,
118 	dbus_usb_state_change,
119 	NULL,			/* isr */
120 	NULL,			/* dpc */
121 	NULL,			/* watchdog */
122 	NULL,			/* dbus_if_pktget */
123 	NULL, 			/* dbus_if_pktfree */
124 	dbus_usb_getirb,
125 	dbus_usb_rxerr_indicate
126 };
127 
128 /* IOVar table */
129 enum {
130 	IOV_SET_DOWNLOAD_STATE = 1,
131 	IOV_DBUS_MSGLEVEL,
132 	IOV_MEMBYTES,
133 	IOV_VARS,
134 	IOV_LOOPBACK_TX
135 };
136 
137 const bcm_iovar_t dhdusb_iovars[] = {
138 	{"vars",	IOV_VARS,	0,	IOVT_BUFFER,	0 },
139 	{"dbus_msglevel",	IOV_DBUS_MSGLEVEL,	0,	IOVT_UINT32,	0 },
140 	{"dwnldstate",	IOV_SET_DOWNLOAD_STATE,	0,	IOVT_BOOL,	0 },
141 	{"membytes",	IOV_MEMBYTES,	0,	IOVT_BUFFER,	2 * sizeof(int) },
142 	{"usb_lb_txfer", IOV_LOOPBACK_TX, 0,    IOVT_BUFFER,    2 * sizeof(int) },
143 	{NULL, 0, 0, 0, 0 }
144 };
145 
146 /*
147  * Need global for probe() and disconnect() since
148  * attach() is not called at probe and detach()
149  * can be called inside disconnect()
150  */
151 static probe_cb_t	probe_cb = NULL;
152 static disconnect_cb_t	disconnect_cb = NULL;
153 static void		*probe_arg = NULL;
154 static void		*disc_arg = NULL;
155 static dbus_intf_t	*g_dbusintf = NULL;
156 static dbus_intf_t	dbus_usb_intf; /** functions called by higher layer DBUS into lower layer */
157 
158 /*
159  * dbus_intf_t common to all USB
160  * These functions override dbus_usb_<os>.c.
161  */
162 static void *dbus_usb_attach(dbus_pub_t *pub, void *cbarg, dbus_intf_callbacks_t *cbs);
163 static void dbus_usb_detach(dbus_pub_t *pub, void *info);
164 static void * dbus_usb_probe(void *arg, const char *desc, uint32 bustype,
165 	uint16 bus_no, uint16 slot, uint32 hdrlen);
166 
167 /* functions */
168 
169 /**
170  * As part of DBUS initialization/registration, the higher level DBUS (dbus.c) needs to know what
171  * lower level DBUS functions to call (in both dbus_usb.c and dbus_usb_os.c).
172  */
173 static void *
dbus_usb_probe(void * arg,const char * desc,uint32 bustype,uint16 bus_no,uint16 slot,uint32 hdrlen)174 dbus_usb_probe(void *arg, const char *desc, uint32 bustype, uint16 bus_no,
175 	uint16 slot, uint32 hdrlen)
176 {
177 	DBUSTRACE(("%s(): \n", __FUNCTION__));
178 	if (probe_cb) {
179 
180 		if (g_dbusintf != NULL) {
181 			/* First, initialize all lower-level functions as default
182 			 * so that dbus.c simply calls directly to dbus_usb_os.c.
183 			 */
184 			bcopy(g_dbusintf, &dbus_usb_intf, sizeof(dbus_intf_t));
185 
186 			/* Second, selectively override functions we need, if any. */
187 			dbus_usb_intf.attach = dbus_usb_attach;
188 			dbus_usb_intf.detach = dbus_usb_detach;
189 			dbus_usb_intf.iovar_op = dbus_usb_iovar_op;
190 			dbus_usb_intf.dlstart = dbus_usb_dlstart;
191 			dbus_usb_intf.dlneeded = dbus_usb_dlneeded;
192 			dbus_usb_intf.dlrun = dbus_usb_dlrun;
193 		}
194 
195 		disc_arg = probe_cb(probe_arg, "DBUS USB", USB_BUS, bus_no, slot, hdrlen);
196 		return disc_arg;
197 	}
198 
199 	return NULL;
200 }
201 
202 /**
203  * On return, *intf contains this or lower-level DBUS functions to be called by higher
204  * level (dbus.c)
205  */
206 int
dbus_bus_register(int vid,int pid,probe_cb_t prcb,disconnect_cb_t discb,void * prarg,dbus_intf_t ** intf,void * param1,void * param2)207 dbus_bus_register(int vid, int pid, probe_cb_t prcb,
208 	disconnect_cb_t discb, void *prarg, dbus_intf_t **intf, void *param1, void *param2)
209 {
210 	int err;
211 
212 	DBUSTRACE(("%s(): \n", __FUNCTION__));
213 	probe_cb = prcb;
214 	disconnect_cb = discb;
215 	probe_arg = prarg;
216 
217 	*intf = &dbus_usb_intf;
218 
219 	err = dbus_bus_osl_register(vid, pid, dbus_usb_probe,
220 		dbus_usb_disconnect, NULL, &g_dbusintf, param1, param2);
221 
222 	ASSERT(g_dbusintf);
223 	return err;
224 }
225 
226 int
dbus_bus_deregister()227 dbus_bus_deregister()
228 {
229 	DBUSTRACE(("%s(): \n", __FUNCTION__));
230 	return dbus_bus_osl_deregister();
231 }
232 
233 /** initialization consists of registration followed by 'attach'. */
234 void *
dbus_usb_attach(dbus_pub_t * pub,void * cbarg,dbus_intf_callbacks_t * cbs)235 dbus_usb_attach(dbus_pub_t *pub, void *cbarg, dbus_intf_callbacks_t *cbs)
236 {
237 	usb_info_t *usb_info;
238 
239 	DBUSTRACE(("%s(): \n", __FUNCTION__));
240 
241 	if ((g_dbusintf == NULL) || (g_dbusintf->attach == NULL))
242 		return NULL;
243 
244 	/* Sanity check for BUS_INFO() */
245 	ASSERT(OFFSETOF(usb_info_t, pub) == 0);
246 
247 	usb_info = MALLOC(pub->osh, sizeof(usb_info_t));
248 	if (usb_info == NULL)
249 		return NULL;
250 
251 	bzero(usb_info, sizeof(usb_info_t));
252 
253 	usb_info->pub = pub;
254 	usb_info->cbarg = cbarg;
255 	usb_info->cbs = cbs;
256 
257 	usb_info->usbosl_info = (dbus_pub_t *)g_dbusintf->attach(pub,
258 		usb_info, &dbus_usb_intf_cbs);
259 	if (usb_info->usbosl_info == NULL) {
260 		MFREE(pub->osh, usb_info, sizeof(usb_info_t));
261 		return NULL;
262 	}
263 
264 	/* Save USB OS-specific driver entry points */
265 	usb_info->drvintf = g_dbusintf;
266 
267 	pub->bus = usb_info;
268 #if !defined(BCM_REQUEST_FW)
269 	if (!dbus_usb_resetcfg(usb_info)) {
270 		usb_info->pub->busstate = DBUS_STATE_DL_DONE;
271 	}
272 #endif
273 	/* Return Lower layer info */
274 	return (void *) usb_info->usbosl_info;
275 }
276 
277 void
dbus_usb_detach(dbus_pub_t * pub,void * info)278 dbus_usb_detach(dbus_pub_t *pub, void *info)
279 {
280 	usb_info_t *usb_info = (usb_info_t *) pub->bus;
281 	osl_t *osh = pub->osh;
282 
283 	if (usb_info == NULL)
284 		return;
285 
286 	if (usb_info->drvintf && usb_info->drvintf->detach)
287 		usb_info->drvintf->detach(pub, usb_info->usbosl_info);
288 
289 	MFREE(osh, usb_info, sizeof(usb_info_t));
290 }
291 
292 void
dbus_usb_disconnect(void * handle)293 dbus_usb_disconnect(void *handle)
294 {
295 	DBUSTRACE(("%s(): \n", __FUNCTION__));
296 	if (disconnect_cb)
297 		disconnect_cb(disc_arg);
298 }
299 
300 /**
301  * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be
302  * notified.
303  */
304 static void
dbus_usb_send_irb_timeout(void * handle,dbus_irb_tx_t * txirb)305 dbus_usb_send_irb_timeout(void *handle, dbus_irb_tx_t *txirb)
306 {
307 	usb_info_t *usb_info = (usb_info_t *) handle;
308 
309 	DBUSTRACE(("%s\n", __FUNCTION__));
310 
311 	if (usb_info == NULL)
312 		return;
313 
314 	if (usb_info->cbs && usb_info->cbs->send_irb_timeout)
315 		usb_info->cbs->send_irb_timeout(usb_info->cbarg, txirb);
316 }
317 
318 /**
319  * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be
320  * notified.
321  */
322 static void
dbus_usb_send_irb_complete(void * handle,dbus_irb_tx_t * txirb,int status)323 dbus_usb_send_irb_complete(void *handle, dbus_irb_tx_t *txirb, int status)
324 {
325 	usb_info_t *usb_info = (usb_info_t *) handle;
326 
327 	if (usb_info == NULL)
328 		return;
329 
330 	if (usb_info->cbs && usb_info->cbs->send_irb_complete)
331 		usb_info->cbs->send_irb_complete(usb_info->cbarg, txirb, status);
332 }
333 
334 /**
335  * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be
336  * notified.
337  */
338 static void
dbus_usb_recv_irb_complete(void * handle,dbus_irb_rx_t * rxirb,int status)339 dbus_usb_recv_irb_complete(void *handle, dbus_irb_rx_t *rxirb, int status)
340 {
341 	usb_info_t *usb_info = (usb_info_t *) handle;
342 
343 	if (usb_info == NULL)
344 		return;
345 
346 	if (usb_info->cbs && usb_info->cbs->recv_irb_complete)
347 		usb_info->cbs->recv_irb_complete(usb_info->cbarg, rxirb, status);
348 }
349 
350 /** Lower DBUS level (dbus_usb_os.c) requests a free IRB. Pass this on to the higher DBUS level. */
351 static struct dbus_irb*
dbus_usb_getirb(void * handle,bool send)352 dbus_usb_getirb(void *handle, bool send)
353 {
354 	usb_info_t *usb_info = (usb_info_t *) handle;
355 
356 	if (usb_info == NULL)
357 		return NULL;
358 
359 	if (usb_info->cbs && usb_info->cbs->getirb)
360 		return usb_info->cbs->getirb(usb_info->cbarg, send);
361 
362 	return NULL;
363 }
364 
365 /**
366  * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be
367  * notified.
368  */
369 static void
dbus_usb_rxerr_indicate(void * handle,bool on)370 dbus_usb_rxerr_indicate(void *handle, bool on)
371 {
372 	usb_info_t *usb_info = (usb_info_t *) handle;
373 
374 	if (usb_info == NULL)
375 		return;
376 
377 	if (usb_info->cbs && usb_info->cbs->rxerr_indicate)
378 		usb_info->cbs->rxerr_indicate(usb_info->cbarg, on);
379 }
380 
381 /**
382  * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be
383  * notified.
384  */
385 static void
dbus_usb_errhandler(void * handle,int err)386 dbus_usb_errhandler(void *handle, int err)
387 {
388 	usb_info_t *usb_info = (usb_info_t *) handle;
389 
390 	if (usb_info == NULL)
391 		return;
392 
393 	if (usb_info->cbs && usb_info->cbs->errhandler)
394 		usb_info->cbs->errhandler(usb_info->cbarg, err);
395 }
396 
397 /**
398  * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be
399  * notified.
400  */
401 static void
dbus_usb_ctl_complete(void * handle,int type,int status)402 dbus_usb_ctl_complete(void *handle, int type, int status)
403 {
404 	usb_info_t *usb_info = (usb_info_t *) handle;
405 
406 	DBUSTRACE(("%s\n", __FUNCTION__));
407 
408 	if (usb_info == NULL) {
409 		DBUSERR(("%s: usb_info is NULL\n", __FUNCTION__));
410 		return;
411 	}
412 
413 	if (usb_info->cbs && usb_info->cbs->ctl_complete)
414 		usb_info->cbs->ctl_complete(usb_info->cbarg, type, status);
415 }
416 
417 /**
418  * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be
419  * notified.
420  */
421 static void
dbus_usb_state_change(void * handle,int state)422 dbus_usb_state_change(void *handle, int state)
423 {
424 	usb_info_t *usb_info = (usb_info_t *) handle;
425 
426 	if (usb_info == NULL)
427 		return;
428 
429 	if (usb_info->cbs && usb_info->cbs->state_change)
430 		usb_info->cbs->state_change(usb_info->cbarg, state);
431 }
432 
433 /** called by higher DBUS level (dbus.c) */
434 static int
dbus_usb_iovar_op(void * bus,const char * name,void * params,int plen,void * arg,int len,bool set)435 dbus_usb_iovar_op(void *bus, const char *name,
436 	void *params, int plen, void *arg, int len, bool set)
437 {
438 	int err = DBUS_OK;
439 
440 	err = dbus_iovar_process((usb_info_t*)bus, name, params, plen, arg, len, set);
441 	return err;
442 }
443 
444 /** process iovar request from higher DBUS level */
445 static int
dbus_iovar_process(usb_info_t * usbinfo,const char * name,void * params,int plen,void * arg,int len,bool set)446 dbus_iovar_process(usb_info_t* usbinfo, const char *name,
447                  void *params, int plen, void *arg, int len, bool set)
448 {
449 	const bcm_iovar_t *vi = NULL;
450 	int bcmerror = 0;
451 	int val_size;
452 	uint32 actionid;
453 
454 	DBUSTRACE(("%s: Enter\n", __FUNCTION__));
455 
456 	ASSERT(name);
457 	ASSERT(len >= 0);
458 
459 	/* Get MUST have return space */
460 	ASSERT(set || (arg && len));
461 
462 	/* Set does NOT take qualifiers */
463 	ASSERT(!set || (!params && !plen));
464 
465 	/* Look up var locally; if not found pass to host driver */
466 	if ((vi = bcm_iovar_lookup(dhdusb_iovars, name)) == NULL) {
467 		/* Not Supported */
468 		bcmerror = BCME_UNSUPPORTED;
469 		DBUSTRACE(("%s: IOVAR %s is not supported\n", name, __FUNCTION__));
470 		goto exit;
471 
472 	}
473 
474 	DBUSTRACE(("%s: %s %s, len %d plen %d\n", __FUNCTION__,
475 	         name, (set ? "set" : "get"), len, plen));
476 
477 	/* set up 'params' pointer in case this is a set command so that
478 	 * the convenience int and bool code can be common to set and get
479 	 */
480 	if (params == NULL) {
481 		params = arg;
482 		plen = len;
483 	}
484 
485 	if (vi->type == IOVT_VOID)
486 		val_size = 0;
487 	else if (vi->type == IOVT_BUFFER)
488 		val_size = len;
489 	else
490 		/* all other types are integer sized */
491 		val_size = sizeof(int);
492 
493 	actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
494 	bcmerror = dbus_usb_doiovar(usbinfo, vi, actionid,
495 		name, params, plen, arg, len, val_size);
496 
497 exit:
498 	return bcmerror;
499 } /* dbus_iovar_process */
500 
501 static int
dbus_usb_doiovar(usb_info_t * bus,const bcm_iovar_t * vi,uint32 actionid,const char * name,void * params,int plen,void * arg,int len,int val_size)502 dbus_usb_doiovar(usb_info_t *bus, const bcm_iovar_t *vi, uint32 actionid, const char *name,
503                 void *params, int plen, void *arg, int len, int val_size)
504 {
505 	int bcmerror = 0;
506 	int32 int_val = 0;
507 	int32 int_val2 = 0;
508 	bool bool_val = 0;
509 
510 	DBUSTRACE(("%s: Enter, action %d name %s params %p plen %d arg %p len %d val_size %d\n",
511 	           __FUNCTION__, actionid, name, params, plen, arg, len, val_size));
512 
513 	if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0)
514 		goto exit;
515 
516 	if (plen >= (int)sizeof(int_val))
517 		bcopy(params, &int_val, sizeof(int_val));
518 
519 	if (plen >= (int)sizeof(int_val) * 2)
520 		bcopy((void*)((uintptr)params + sizeof(int_val)), &int_val2, sizeof(int_val2));
521 
522 	bool_val = (int_val != 0) ? TRUE : FALSE;
523 
524 	switch (actionid) {
525 
526 	case IOV_SVAL(IOV_MEMBYTES):
527 	case IOV_GVAL(IOV_MEMBYTES):
528 	{
529 		uint32 address;
530 		uint size, dsize;
531 		uint8 *data;
532 
533 		bool set = (actionid == IOV_SVAL(IOV_MEMBYTES));
534 
535 		ASSERT(plen >= 2*sizeof(int));
536 
537 		address = (uint32)int_val;
538 		BCM_REFERENCE(address);
539 		bcopy((char *)params + sizeof(int_val), &int_val, sizeof(int_val));
540 		size = (uint)int_val;
541 
542 		/* Do some validation */
543 		dsize = set ? plen - (2 * sizeof(int)) : len;
544 		if (dsize < size) {
545 			DBUSTRACE(("%s: error on %s membytes, addr 0x%08x size %d dsize %d\n",
546 			           __FUNCTION__, (set ? "set" : "get"), address, size, dsize));
547 			bcmerror = BCME_BADARG;
548 			break;
549 		}
550 		DBUSTRACE(("%s: Request to %s %d bytes at address 0x%08x\n", __FUNCTION__,
551 		          (set ? "write" : "read"), size, address));
552 
553 		/* Generate the actual data pointer */
554 		data = set ? (uint8*)params + 2 * sizeof(int): (uint8*)arg;
555 
556 		/* Call to do the transfer */
557 		bcmerror = dbus_usb_dl_writeimage(BUS_INFO(bus, usb_info_t), data, size);
558 	}
559 		break;
560 
561 
562 	case IOV_SVAL(IOV_SET_DOWNLOAD_STATE):
563 
564 		if (bool_val == TRUE) {
565 			bcmerror = dbus_usb_dlneeded(bus);
566 			dbus_usb_rdl_dwnld_state(BUS_INFO(bus, usb_info_t));
567 		} else {
568 			usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t);
569 			bcmerror = dbus_usb_dlrun(bus);
570 			usbinfo->pub->busstate = DBUS_STATE_DL_DONE;
571 		}
572 		break;
573 
574 	case IOV_SVAL(IOV_VARS):
575 		bcmerror = dhdusb_downloadvars(BUS_INFO(bus, usb_info_t), arg, len);
576 		break;
577 
578 	case IOV_GVAL(IOV_DBUS_MSGLEVEL):
579 		int_val = (int32)dbus_msglevel;
580 		bcopy(&int_val, arg, val_size);
581 		break;
582 
583 	case IOV_SVAL(IOV_DBUS_MSGLEVEL):
584 		dbus_msglevel = int_val;
585 		break;
586 
587 #ifdef DBUS_USB_LOOPBACK
588 	case IOV_SVAL(IOV_LOOPBACK_TX):
589 			bcmerror = dbus_usbos_loopback_tx(BUS_INFO(bus, usb_info_t), int_val,
590 			  int_val2);
591 			break;
592 #endif
593 	default:
594 		bcmerror = BCME_UNSUPPORTED;
595 		break;
596 	}
597 
598 exit:
599 	return bcmerror;
600 } /* dbus_usb_doiovar */
601 
602 /** higher DBUS level (dbus.c) wants to set NVRAM variables in dongle */
603 static int
dhdusb_downloadvars(usb_info_t * bus,void * arg,int len)604 dhdusb_downloadvars(usb_info_t *bus, void *arg, int len)
605 {
606 	int bcmerror = 0;
607 	uint32 varsize;
608 	uint32 varaddr;
609 	uint32 varsizew;
610 
611 	if (!len) {
612 		bcmerror = BCME_BUFTOOSHORT;
613 		goto err;
614 	}
615 
616 	/* RAM size is not set. Set it at dbus_usb_dlneeded */
617 	if (!bus->rdlram_size)
618 		bcmerror = BCME_ERROR;
619 
620 	/* Even if there are no vars are to be written, we still need to set the ramsize. */
621 	varsize = len ? ROUNDUP(len, 4) : 0;
622 	varaddr = (bus->rdlram_size - 4) - varsize;
623 
624 	/* Write the vars list */
625 	DBUSTRACE(("WriteVars: @%x varsize=%d\n", varaddr, varsize));
626 	bcmerror = dbus_write_membytes(bus->usbosl_info, TRUE, (varaddr + bus->rdlram_base_addr),
627 		arg, varsize);
628 
629 	/* adjust to the user specified RAM */
630 	DBUSTRACE(("Usable memory size: %d\n", bus->rdlram_size));
631 	DBUSTRACE(("Vars are at %d, orig varsize is %d\n", varaddr, varsize));
632 
633 	varsize = ((bus->rdlram_size - 4) - varaddr);
634 
635 	/*
636 	 * Determine the length token:
637 	 * Varsize, converted to words, in lower 16-bits, checksum in upper 16-bits.
638 	 */
639 	if (bcmerror) {
640 		varsizew = 0;
641 	} else {
642 		varsizew = varsize / 4;
643 		varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF);
644 		varsizew = htol32(varsizew);
645 	}
646 
647 	DBUSTRACE(("New varsize is %d, length token=0x%08x\n", varsize, varsizew));
648 
649 	/* Write the length token to the last word */
650 	bcmerror = dbus_write_membytes(bus->usbosl_info, TRUE, ((bus->rdlram_size - 4) +
651 		bus->rdlram_base_addr), (uint8*)&varsizew, 4);
652 err:
653 	return bcmerror;
654 } /* dbus_usb_doiovar */
655 
656 #if !defined(BCM_REQUEST_FW)
657 /**
658  * After downloading firmware into dongle and starting it, we need to know if the firmware is
659  * indeed up and running.
660  */
661 static int
dbus_usb_resetcfg(usb_info_t * usbinfo)662 dbus_usb_resetcfg(usb_info_t *usbinfo)
663 {
664 	void *osinfo;
665 	bootrom_id_t id;
666 	uint16 waittime = 0;
667 
668 	uint32 starttime = 0;
669 	uint32 endtime = 0;
670 
671 	DBUSTRACE(("%s\n", __FUNCTION__));
672 
673 	if (usbinfo == NULL)
674 		return DBUS_ERR;
675 
676 	osinfo = usbinfo->usbosl_info;
677 	ASSERT(osinfo);
678 
679 	/* Give dongle chance to boot */
680 	dbus_usbos_wait(osinfo, USB_SFLASH_DLIMAGE_SPINWAIT);
681 	waittime = USB_SFLASH_DLIMAGE_SPINWAIT;
682 	while (waittime < USB_DLIMAGE_RETRY_TIMEOUT) {
683 
684 		starttime = OSL_SYSUPTIME();
685 
686 		id.chip = 0xDEAD;       /* Get the ID */
687 		dbus_usbos_dl_cmd(osinfo, DL_GETVER, &id, sizeof(bootrom_id_t));
688 		id.chip = ltoh32(id.chip);
689 
690 		endtime = OSL_SYSUPTIME();
691 		waittime += (endtime - starttime);
692 
693 		if (id.chip == POSTBOOT_ID)
694 			break;
695 	}
696 
697 	if (id.chip == POSTBOOT_ID) {
698 		DBUSERR(("%s: download done. Bootup time = %d ms postboot chip 0x%x/rev 0x%x\n",
699 			__FUNCTION__, waittime, id.chip, id.chiprev));
700 
701 		dbus_usbos_dl_cmd(osinfo, DL_RESETCFG, &id, sizeof(bootrom_id_t));
702 
703 		dbus_usbos_wait(osinfo, USB_RESETCFG_SPINWAIT);
704 		return DBUS_OK;
705 	} else {
706 		DBUSERR(("%s: Cannot talk to Dongle. Wait time = %d ms. Firmware is not UP \n",
707 			__FUNCTION__, waittime));
708 		return DBUS_ERR;
709 	}
710 
711 	return DBUS_OK;
712 }
713 #endif
714 
715 /** before firmware download, the dongle has to be prepared to receive the fw image */
716 static int
dbus_usb_rdl_dwnld_state(usb_info_t * usbinfo)717 dbus_usb_rdl_dwnld_state(usb_info_t *usbinfo)
718 {
719 	void *osinfo = usbinfo->usbosl_info;
720 	rdl_state_t state;
721 	int err = DBUS_OK;
722 
723 	/* 1) Prepare USB boot loader for runtime image */
724 	dbus_usbos_dl_cmd(osinfo, DL_START, &state, sizeof(rdl_state_t));
725 
726 	state.state = ltoh32(state.state);
727 	state.bytes = ltoh32(state.bytes);
728 
729 	/* 2) Check we are in the Waiting state */
730 	if (state.state != DL_WAITING) {
731 		DBUSERR(("%s: Failed to DL_START\n", __FUNCTION__));
732 		err = DBUS_ERR;
733 		goto fail;
734 	}
735 
736 fail:
737 	return err;
738 }
739 
740 /**
741  * Dongle contains bootcode in ROM but firmware is (partially) contained in dongle RAM. Therefore,
742  * firmware has to be downloaded into dongle RAM.
743  */
744 static int
dbus_usb_dl_writeimage(usb_info_t * usbinfo,uint8 * fw,int fwlen)745 dbus_usb_dl_writeimage(usb_info_t *usbinfo, uint8 *fw, int fwlen)
746 {
747 	osl_t *osh = usbinfo->pub->osh;
748 	void *osinfo = usbinfo->usbosl_info;
749 	unsigned int sendlen, sent, dllen;
750 	char *bulkchunk = NULL, *dlpos;
751 	rdl_state_t state;
752 	int err = DBUS_OK;
753 	bootrom_id_t id;
754 	uint16 wait, wait_time;
755 	uint32 dl_trunk_size = RDL_CHUNK;
756 
757 	if (BCM4350_CHIP(usbinfo->pub->attrib.devid))
758 		dl_trunk_size = RDL_CHUNK_MAX;
759 
760 	while (!bulkchunk) {
761 		bulkchunk = MALLOC(osh, dl_trunk_size);
762 		if (dl_trunk_size == RDL_CHUNK)
763 			break;
764 		if (!bulkchunk) {
765 			dl_trunk_size /= 2;
766 			if (dl_trunk_size < RDL_CHUNK)
767 				dl_trunk_size = RDL_CHUNK;
768 		}
769 	}
770 
771 	if (bulkchunk == NULL) {
772 		err = DBUS_ERR;
773 		goto fail;
774 	}
775 
776 	sent = 0;
777 	dlpos = fw;
778 	dllen = fwlen;
779 
780 	/* Get chip id and rev */
781 	id.chip = usbinfo->pub->attrib.devid;
782 	id.chiprev = usbinfo->pub->attrib.chiprev;
783 
784 	DBUSTRACE(("enter %s: fwlen=%d\n", __FUNCTION__, fwlen));
785 
786 	dbus_usbos_dl_cmd(osinfo, DL_GETSTATE, &state, sizeof(rdl_state_t));
787 
788 	/* 3) Load the image */
789 	while ((sent < dllen)) {
790 		/* Wait until the usb device reports it received all the bytes we sent */
791 
792 		if (sent < dllen) {
793 			if ((dllen-sent) < dl_trunk_size)
794 				sendlen = dllen-sent;
795 			else
796 				sendlen = dl_trunk_size;
797 
798 			/* simply avoid having to send a ZLP by ensuring we never have an even
799 			 * multiple of 64
800 			 */
801 			if (!(sendlen % 64))
802 				sendlen -= 4;
803 
804 			/* send data */
805 			memcpy(bulkchunk, dlpos, sendlen);
806 			if (!dbus_usbos_dl_send_bulk(osinfo, bulkchunk, sendlen)) {
807 				err = DBUS_ERR;
808 				goto fail;
809 			}
810 
811 			dlpos += sendlen;
812 			sent += sendlen;
813 			DBUSTRACE(("%s: sendlen %d\n", __FUNCTION__, sendlen));
814 		}
815 
816 		wait = 0;
817 		wait_time = USB_SFLASH_DLIMAGE_SPINWAIT;
818 		while (!dbus_usbos_dl_cmd(osinfo, DL_GETSTATE, &state,
819 			sizeof(rdl_state_t))) {
820 			if ((id.chip == 43236) && (id.chiprev == 0)) {
821 				DBUSERR(("%s: 43236a0 SFlash delay, waiting for dongle crc check "
822 					 "completion!!!\n", __FUNCTION__));
823 				dbus_usbos_wait(osinfo, wait_time);
824 				wait += wait_time;
825 				if (wait >= USB_SFLASH_DLIMAGE_LIMIT) {
826 					DBUSERR(("%s: DL_GETSTATE Failed xxxx\n", __FUNCTION__));
827 					err = DBUS_ERR;
828 					goto fail;
829 					break;
830 				}
831 			} else {
832 				DBUSERR(("%s: DL_GETSTATE Failed xxxx\n", __FUNCTION__));
833 				err = DBUS_ERR;
834 				goto fail;
835 			}
836 		}
837 
838 		state.state = ltoh32(state.state);
839 		state.bytes = ltoh32(state.bytes);
840 
841 		/* restart if an error is reported */
842 		if ((state.state == DL_BAD_HDR) || (state.state == DL_BAD_CRC)) {
843 			DBUSERR(("%s: Bad Hdr or Bad CRC\n", __FUNCTION__));
844 			err = DBUS_ERR;
845 			goto fail;
846 		}
847 
848 	}
849 fail:
850 	if (bulkchunk)
851 		MFREE(osh, bulkchunk, dl_trunk_size);
852 
853 	return err;
854 } /* dbus_usb_dl_writeimage */
855 
856 /** Higher level DBUS layer (dbus.c) requests this layer to download image into dongle */
857 static int
dbus_usb_dlstart(void * bus,uint8 * fw,int len)858 dbus_usb_dlstart(void *bus, uint8 *fw, int len)
859 {
860 	usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t);
861 	int err;
862 
863 	DBUSTRACE(("%s\n", __FUNCTION__));
864 
865 	if (usbinfo == NULL)
866 		return DBUS_ERR;
867 
868 	if (USB_DEV_ISBAD(usbinfo))
869 		return DBUS_ERR;
870 
871 	err = dbus_usb_rdl_dwnld_state(usbinfo);
872 
873 	if (DBUS_OK == err) {
874 		err = dbus_usb_dl_writeimage(usbinfo, fw, len);
875 		if (err == DBUS_OK)
876 			usbinfo->pub->busstate = DBUS_STATE_DL_DONE;
877 		else
878 			usbinfo->pub->busstate = DBUS_STATE_DL_PENDING;
879 	} else
880 		usbinfo->pub->busstate = DBUS_STATE_DL_PENDING;
881 
882 	return err;
883 }
884 
885 static bool
dbus_usb_update_chipinfo(usb_info_t * usbinfo,uint32 chip)886 dbus_usb_update_chipinfo(usb_info_t *usbinfo, uint32 chip)
887 {
888 	bool retval = TRUE;
889 	/* based on the CHIP Id, store the ram size which is needed for NVRAM download. */
890 	switch (chip) {
891 
892 		case 0x4319:
893 			usbinfo->rdlram_size = RDL_RAM_SIZE_4319;
894 			usbinfo->rdlram_base_addr = RDL_RAM_BASE_4319;
895 			break;
896 
897 		case 0x4329:
898 			usbinfo->rdlram_size = RDL_RAM_SIZE_4329;
899 			usbinfo->rdlram_base_addr = RDL_RAM_BASE_4329;
900 			break;
901 
902 		case 43234:
903 		case 43235:
904 		case 43236:
905 			usbinfo->rdlram_size = RDL_RAM_SIZE_43236;
906 			usbinfo->rdlram_base_addr = RDL_RAM_BASE_43236;
907 			break;
908 
909 		case 0x4328:
910 			usbinfo->rdlram_size = RDL_RAM_SIZE_4328;
911 			usbinfo->rdlram_base_addr = RDL_RAM_BASE_4328;
912 			break;
913 
914 		case 0x4322:
915 			usbinfo->rdlram_size = RDL_RAM_SIZE_4322;
916 			usbinfo->rdlram_base_addr = RDL_RAM_BASE_4322;
917 			break;
918 
919 		case 0x4360:
920 		case 0xAA06:
921 			usbinfo->rdlram_size = RDL_RAM_SIZE_4360;
922 			usbinfo->rdlram_base_addr = RDL_RAM_BASE_4360;
923 			break;
924 
925 		case 43242:
926 		case 43243:
927 			usbinfo->rdlram_size = RDL_RAM_SIZE_43242;
928 			usbinfo->rdlram_base_addr = RDL_RAM_BASE_43242;
929 			break;
930 
931 		case 43143:
932 			usbinfo->rdlram_size = RDL_RAM_SIZE_43143;
933 			usbinfo->rdlram_base_addr = RDL_RAM_BASE_43143;
934 			break;
935 
936 		case 0x4350:
937 		case 43556:
938 		case 43558:
939 		case 43569:
940 			usbinfo->rdlram_size = RDL_RAM_SIZE_4350;
941 			usbinfo->rdlram_base_addr = RDL_RAM_BASE_4350;
942 			break;
943 
944 		case POSTBOOT_ID:
945 			break;
946 
947 		default:
948 			DBUSERR(("%s: Chip 0x%x Ram size is not known\n", __FUNCTION__, chip));
949 			retval = FALSE;
950 			break;
951 
952 	}
953 
954 	return retval;
955 } /* dbus_usb_update_chipinfo */
956 
957 /** higher DBUS level (dbus.c) wants to know if firmware download is required. */
958 static int
dbus_usb_dlneeded(void * bus)959 dbus_usb_dlneeded(void *bus)
960 {
961 	usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t);
962 	void *osinfo;
963 	bootrom_id_t id;
964 	int dl_needed = 1;
965 
966 	DBUSTRACE(("%s\n", __FUNCTION__));
967 
968 	if (usbinfo == NULL)
969 		return DBUS_ERR;
970 
971 	osinfo = usbinfo->usbosl_info;
972 	ASSERT(osinfo);
973 
974 	/* Check if firmware downloaded already by querying runtime ID */
975 	id.chip = 0xDEAD;
976 	dbus_usbos_dl_cmd(osinfo, DL_GETVER, &id, sizeof(bootrom_id_t));
977 
978 	id.chip = ltoh32(id.chip);
979 	id.chiprev = ltoh32(id.chiprev);
980 
981 	if (FALSE == dbus_usb_update_chipinfo(usbinfo, id.chip)) {
982 		dl_needed = DBUS_ERR;
983 		goto exit;
984 	}
985 
986 	DBUSERR(("%s: chip 0x%x rev 0x%x\n", __FUNCTION__, id.chip, id.chiprev));
987 	if (id.chip == POSTBOOT_ID) {
988 		/* This code is  needed to support two enumerations on USB1.1 scenario */
989 		DBUSERR(("%s: Firmware already downloaded\n", __FUNCTION__));
990 
991 		dbus_usbos_dl_cmd(osinfo, DL_RESETCFG, &id, sizeof(bootrom_id_t));
992 		dl_needed = DBUS_OK;
993 		if (usbinfo->pub->busstate == DBUS_STATE_DL_PENDING)
994 			usbinfo->pub->busstate = DBUS_STATE_DL_DONE;
995 	} else {
996 		usbinfo->pub->attrib.devid = id.chip;
997 		usbinfo->pub->attrib.chiprev = id.chiprev;
998 	}
999 
1000 exit:
1001 	return dl_needed;
1002 }
1003 
1004 /** After issuing firmware download, higher DBUS level (dbus.c) wants to start the firmware. */
1005 static int
dbus_usb_dlrun(void * bus)1006 dbus_usb_dlrun(void *bus)
1007 {
1008 	usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t);
1009 	void *osinfo;
1010 	rdl_state_t state;
1011 	int err = DBUS_OK;
1012 
1013 	DBUSTRACE(("%s\n", __FUNCTION__));
1014 
1015 	if (usbinfo == NULL)
1016 		return DBUS_ERR;
1017 
1018 	if (USB_DEV_ISBAD(usbinfo))
1019 		return DBUS_ERR;
1020 
1021 	osinfo = usbinfo->usbosl_info;
1022 	ASSERT(osinfo);
1023 
1024 	/* Check we are runnable */
1025 	dbus_usbos_dl_cmd(osinfo, DL_GETSTATE, &state, sizeof(rdl_state_t));
1026 
1027 	state.state = ltoh32(state.state);
1028 	state.bytes = ltoh32(state.bytes);
1029 
1030 	/* Start the image */
1031 	if (state.state == DL_RUNNABLE) {
1032 		DBUSTRACE(("%s: Issue DL_GO\n", __FUNCTION__));
1033 		dbus_usbos_dl_cmd(osinfo, DL_GO, &state, sizeof(rdl_state_t));
1034 
1035 		if (usbinfo->pub->attrib.devid == TEST_CHIP)
1036 			dbus_usbos_wait(osinfo, USB_DLGO_SPINWAIT);
1037 
1038 //		dbus_usb_resetcfg(usbinfo);
1039 		/* The Donlge may go for re-enumeration. */
1040 	} else {
1041 		DBUSERR(("%s: Dongle not runnable\n", __FUNCTION__));
1042 		err = DBUS_ERR;
1043 	}
1044 
1045 	return err;
1046 }
1047 
1048 /**
1049  * As preparation for firmware download, higher DBUS level (dbus.c) requests the firmware image
1050  * to be used for the type of dongle detected. Directly called by dbus.c (so not via a callback
1051  * construction)
1052  */
1053 void
dbus_bus_fw_get(void * bus,uint8 ** fw,int * fwlen,int * decomp)1054 dbus_bus_fw_get(void *bus, uint8 **fw, int *fwlen, int *decomp)
1055 {
1056 	usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t);
1057 	unsigned int devid;
1058 	unsigned int crev;
1059 
1060 	devid = usbinfo->pub->attrib.devid;
1061 	crev = usbinfo->pub->attrib.chiprev;
1062 
1063 	*fw = NULL;
1064 	*fwlen = 0;
1065 
1066 	switch (devid) {
1067 	case BCM43236_CHIP_ID:
1068 	case BCM43235_CHIP_ID:
1069 	case BCM43234_CHIP_ID:
1070 	case BCM43238_CHIP_ID: {
1071 		if (crev == 3 || crev == 2 || crev == 1) {
1072 #ifdef EMBED_IMAGE_43236b
1073 			*fw = (uint8 *)dlarray_43236b;
1074 			*fwlen = sizeof(dlarray_43236b);
1075 
1076 #endif
1077 		}
1078 		} break;
1079 	case BCM4360_CHIP_ID:
1080 	case BCM4352_CHIP_ID:
1081 	case BCM43526_CHIP_ID:
1082 #ifdef EMBED_IMAGE_43526a
1083 		if (crev <= 2) {
1084 			*fw = (uint8 *)dlarray_43526a;
1085 			*fwlen = sizeof(dlarray_43526a);
1086 		}
1087 #endif
1088 #ifdef EMBED_IMAGE_43526b
1089 		if (crev > 2) {
1090 			*fw = (uint8 *)dlarray_43526b;
1091 			*fwlen = sizeof(dlarray_43526b);
1092 		}
1093 #endif
1094 		break;
1095 
1096 	case BCM43242_CHIP_ID:
1097 #ifdef EMBED_IMAGE_43242a0
1098 		*fw = (uint8 *)dlarray_43242a0;
1099 		*fwlen = sizeof(dlarray_43242a0);
1100 #endif
1101 		break;
1102 
1103 	case BCM43143_CHIP_ID:
1104 #ifdef EMBED_IMAGE_43143a0
1105 		*fw = (uint8 *)dlarray_43143a0;
1106 		*fwlen = sizeof(dlarray_43143a0);
1107 #endif
1108 #ifdef EMBED_IMAGE_43143b0
1109 		*fw = (uint8 *)dlarray_43143b0;
1110 		*fwlen = sizeof(dlarray_43143b0);
1111 #endif
1112 		break;
1113 
1114 	case BCM4350_CHIP_ID:
1115 	case BCM4354_CHIP_ID:
1116 	case BCM43556_CHIP_ID:
1117 	case BCM43558_CHIP_ID:
1118 	case BCM43566_CHIP_ID:
1119 	case BCM43568_CHIP_ID:
1120 	case BCM43570_CHIP_ID:
1121 	case BCM4358_CHIP_ID:
1122 #ifdef EMBED_IMAGE_4350a0
1123 		if (crev == 0) {
1124 			*fw = (uint8 *)dlarray_4350a0;
1125 			*fwlen = sizeof(dlarray_4350a0);
1126 		}
1127 #endif
1128 #ifdef EMBED_IMAGE_4350b0
1129 		if (crev == 1) {
1130 			*fw = (uint8 *)dlarray_4350b0;
1131 			*fwlen = sizeof(dlarray_4350b0);
1132 		}
1133 #endif
1134 #ifdef EMBED_IMAGE_4350b1
1135 		if (crev == 2) {
1136 			*fw = (uint8 *)dlarray_4350b1;
1137 			*fwlen = sizeof(dlarray_4350b1);
1138 		}
1139 #endif
1140 #ifdef EMBED_IMAGE_43556b1
1141 		if (crev == 2) {
1142 			*fw = (uint8 *)dlarray_43556b1;
1143 			*fwlen = sizeof(dlarray_43556b1);
1144 		}
1145 #endif
1146 #ifdef EMBED_IMAGE_4350c0
1147 		if (crev == 3) {
1148 			*fw = (uint8 *)dlarray_4350c0;
1149 			*fwlen = sizeof(dlarray_4350c0);
1150 		}
1151 #endif /* EMBED_IMAGE_4350c0 */
1152 #ifdef EMBED_IMAGE_4350c1
1153 		if (crev == 4) {
1154 			*fw = (uint8 *)dlarray_4350c1;
1155 			*fwlen = sizeof(dlarray_4350c1);
1156 		}
1157 #endif /* EMBED_IMAGE_4350c1 */
1158 		break;
1159 	case BCM43569_CHIP_ID:
1160 #ifdef EMBED_IMAGE_43569a0
1161 		if (crev == 0) {
1162 			*fw = (uint8 *)dlarray_43569a0;
1163 			*fwlen = sizeof(dlarray_43569a0);
1164 		}
1165 #endif /* EMBED_IMAGE_43569a0 */
1166 		break;
1167 	default:
1168 #ifdef EMBED_IMAGE_GENERIC
1169 		*fw = (uint8 *)dlarray;
1170 		*fwlen = sizeof(dlarray);
1171 #endif
1172 		break;
1173 	}
1174 } /* dbus_bus_fw_get */
1175