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