xref: /onnv-gate/usr/src/uts/common/io/zyd/zyd_usb.c (revision 8503:4f8506c7efb2)
1*8503SPengcheng.Chen@Sun.COM /*
2*8503SPengcheng.Chen@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3*8503SPengcheng.Chen@Sun.COM  * Use is subject to license terms.
4*8503SPengcheng.Chen@Sun.COM  */
5*8503SPengcheng.Chen@Sun.COM 
6*8503SPengcheng.Chen@Sun.COM /*
7*8503SPengcheng.Chen@Sun.COM  * Copyright (c) 2007 by  Lukas Turek <turek@ksvi.mff.cuni.cz>
8*8503SPengcheng.Chen@Sun.COM  * Copyright (c) 2007 by  Jiri Svoboda <jirik.svoboda@seznam.cz>
9*8503SPengcheng.Chen@Sun.COM  * Copyright (c) 2007 by  Martin Krulis <martin.krulis@matfyz.cz>
10*8503SPengcheng.Chen@Sun.COM  * Copyright (c) 2006 by Damien Bergamini <damien.bergamini@free.fr>
11*8503SPengcheng.Chen@Sun.COM  * Copyright (c) 2006 by Florian Stoehr <ich@florian-stoehr.de>
12*8503SPengcheng.Chen@Sun.COM  *
13*8503SPengcheng.Chen@Sun.COM  * Permission to use, copy, modify, and distribute this software for any
14*8503SPengcheng.Chen@Sun.COM  * purpose with or without fee is hereby granted, provided that the above
15*8503SPengcheng.Chen@Sun.COM  * copyright notice and this permission notice appear in all copies.
16*8503SPengcheng.Chen@Sun.COM  *
17*8503SPengcheng.Chen@Sun.COM  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
18*8503SPengcheng.Chen@Sun.COM  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
19*8503SPengcheng.Chen@Sun.COM  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
20*8503SPengcheng.Chen@Sun.COM  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21*8503SPengcheng.Chen@Sun.COM  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
22*8503SPengcheng.Chen@Sun.COM  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
23*8503SPengcheng.Chen@Sun.COM  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24*8503SPengcheng.Chen@Sun.COM  *
25*8503SPengcheng.Chen@Sun.COM  */
26*8503SPengcheng.Chen@Sun.COM 
27*8503SPengcheng.Chen@Sun.COM /*
28*8503SPengcheng.Chen@Sun.COM  * ZD1211 wLAN driver
29*8503SPengcheng.Chen@Sun.COM  * USB communication
30*8503SPengcheng.Chen@Sun.COM  *
31*8503SPengcheng.Chen@Sun.COM  * Manage USB communication with the ZD-based device.
32*8503SPengcheng.Chen@Sun.COM  */
33*8503SPengcheng.Chen@Sun.COM 
34*8503SPengcheng.Chen@Sun.COM #include <sys/byteorder.h>
35*8503SPengcheng.Chen@Sun.COM #include <sys/stream.h>
36*8503SPengcheng.Chen@Sun.COM #include <sys/strsun.h>
37*8503SPengcheng.Chen@Sun.COM #include <sys/mac_provider.h>
38*8503SPengcheng.Chen@Sun.COM 
39*8503SPengcheng.Chen@Sun.COM #include "zyd.h"
40*8503SPengcheng.Chen@Sun.COM #include "zyd_reg.h"
41*8503SPengcheng.Chen@Sun.COM 
42*8503SPengcheng.Chen@Sun.COM static int zyd_usb_disconnect(dev_info_t *dip);
43*8503SPengcheng.Chen@Sun.COM static int zyd_usb_reconnect(dev_info_t *dip);
44*8503SPengcheng.Chen@Sun.COM static zyd_res zyd_usb_data_in_start_request(struct zyd_usb *uc);
45*8503SPengcheng.Chen@Sun.COM 
46*8503SPengcheng.Chen@Sun.COM static zyd_usb_info_t usb_ids[] = {
47*8503SPengcheng.Chen@Sun.COM 	{0x53, 0x5301, ZYD_ZD1211B},
48*8503SPengcheng.Chen@Sun.COM 	{0x105, 0x145f, ZYD_ZD1211},
49*8503SPengcheng.Chen@Sun.COM 	{0x411, 0xda, ZYD_ZD1211B},
50*8503SPengcheng.Chen@Sun.COM 	{0x471, 0x1236, ZYD_ZD1211B},
51*8503SPengcheng.Chen@Sun.COM 	{0x471, 0x1237, ZYD_ZD1211B},
52*8503SPengcheng.Chen@Sun.COM 	{0x50d, 0x705c, ZYD_ZD1211B},
53*8503SPengcheng.Chen@Sun.COM 	{0x586, 0x3401, ZYD_ZD1211},
54*8503SPengcheng.Chen@Sun.COM 	{0x586, 0x3402, ZYD_ZD1211},
55*8503SPengcheng.Chen@Sun.COM 	{0x586, 0x3407, ZYD_ZD1211},
56*8503SPengcheng.Chen@Sun.COM 	{0x586, 0x3409, ZYD_ZD1211},
57*8503SPengcheng.Chen@Sun.COM 	{0x586, 0x3410, ZYD_ZD1211B},
58*8503SPengcheng.Chen@Sun.COM 	{0x586, 0x3412, ZYD_ZD1211B},
59*8503SPengcheng.Chen@Sun.COM 	{0x586, 0x3413, ZYD_ZD1211B},
60*8503SPengcheng.Chen@Sun.COM 	{0x586, 0x340a, ZYD_ZD1211B},
61*8503SPengcheng.Chen@Sun.COM 	{0x586, 0x340f, ZYD_ZD1211B},
62*8503SPengcheng.Chen@Sun.COM 	{0x79b, 0x4a, ZYD_ZD1211},
63*8503SPengcheng.Chen@Sun.COM 	{0x79b, 0x62, ZYD_ZD1211B},
64*8503SPengcheng.Chen@Sun.COM 	{0x7b8, 0x6001, ZYD_ZD1211},
65*8503SPengcheng.Chen@Sun.COM 	{0x83a, 0x4505, ZYD_ZD1211B},
66*8503SPengcheng.Chen@Sun.COM 	{0xace, 0x1211, ZYD_ZD1211},
67*8503SPengcheng.Chen@Sun.COM 	{0xace, 0x1215, ZYD_ZD1211B},
68*8503SPengcheng.Chen@Sun.COM 	{0xb05, 0x170c, ZYD_ZD1211},
69*8503SPengcheng.Chen@Sun.COM 	{0xb05, 0x171b, ZYD_ZD1211B},
70*8503SPengcheng.Chen@Sun.COM 	{0xb3b, 0x1630, ZYD_ZD1211},
71*8503SPengcheng.Chen@Sun.COM 	{0xb3b, 0x5630, ZYD_ZD1211},
72*8503SPengcheng.Chen@Sun.COM 	{0xbaf, 0x121, ZYD_ZD1211B},
73*8503SPengcheng.Chen@Sun.COM 	{0xcde, 0x1a, ZYD_ZD1211B},
74*8503SPengcheng.Chen@Sun.COM 	{0xdf6, 0x9071, ZYD_ZD1211},
75*8503SPengcheng.Chen@Sun.COM 	{0xdf6, 0x9075, ZYD_ZD1211},
76*8503SPengcheng.Chen@Sun.COM 	{0x126f, 0xa006, ZYD_ZD1211},
77*8503SPengcheng.Chen@Sun.COM 	{0x129b, 0x1666, ZYD_ZD1211},
78*8503SPengcheng.Chen@Sun.COM 	{0x129b, 0x1667, ZYD_ZD1211B},
79*8503SPengcheng.Chen@Sun.COM 	{0x13b1, 0x1e, ZYD_ZD1211},
80*8503SPengcheng.Chen@Sun.COM 	{0x13b1, 0x24, ZYD_ZD1211B},
81*8503SPengcheng.Chen@Sun.COM 	{0x1435, 0x711, ZYD_ZD1211},
82*8503SPengcheng.Chen@Sun.COM 	{0x14ea, 0xab13, ZYD_ZD1211},
83*8503SPengcheng.Chen@Sun.COM 	{0x157e, 0x300b, ZYD_ZD1211},
84*8503SPengcheng.Chen@Sun.COM 	{0x157e, 0x300d, ZYD_ZD1211B},
85*8503SPengcheng.Chen@Sun.COM 	{0x157e, 0x3204, ZYD_ZD1211},
86*8503SPengcheng.Chen@Sun.COM 	{0x1582, 0x6003, ZYD_ZD1211B},
87*8503SPengcheng.Chen@Sun.COM 	{0x1740, 0x2000, ZYD_ZD1211},
88*8503SPengcheng.Chen@Sun.COM 	{0x2019, 0x5303, ZYD_ZD1211B},
89*8503SPengcheng.Chen@Sun.COM 	{0x6891, 0xa727, ZYD_ZD1211}
90*8503SPengcheng.Chen@Sun.COM };
91*8503SPengcheng.Chen@Sun.COM 
92*8503SPengcheng.Chen@Sun.COM /*
93*8503SPengcheng.Chen@Sun.COM  * Get mac rev for usb vendor/product id.
94*8503SPengcheng.Chen@Sun.COM  */
95*8503SPengcheng.Chen@Sun.COM zyd_mac_rev_t
zyd_usb_mac_rev(uint16_t vendor,uint16_t product)96*8503SPengcheng.Chen@Sun.COM zyd_usb_mac_rev(uint16_t vendor, uint16_t product)
97*8503SPengcheng.Chen@Sun.COM {
98*8503SPengcheng.Chen@Sun.COM 	int i;
99*8503SPengcheng.Chen@Sun.COM 
100*8503SPengcheng.Chen@Sun.COM 	ZYD_DEBUG((ZYD_DBG_USB, "matching device usb%x,%x\n", vendor, product));
101*8503SPengcheng.Chen@Sun.COM 	for (i = 0; i < sizeof (usb_ids) / sizeof (zyd_usb_info_t); i++) {
102*8503SPengcheng.Chen@Sun.COM 		if (vendor == usb_ids[i].vendor_id &&
103*8503SPengcheng.Chen@Sun.COM 		    product == usb_ids[i].product_id)
104*8503SPengcheng.Chen@Sun.COM 			return (usb_ids[i].mac_rev);
105*8503SPengcheng.Chen@Sun.COM 	}
106*8503SPengcheng.Chen@Sun.COM 
107*8503SPengcheng.Chen@Sun.COM 	ZYD_DEBUG((ZYD_DBG_USB, "assuming ZD1211B\n"));
108*8503SPengcheng.Chen@Sun.COM 	return (ZYD_ZD1211B);
109*8503SPengcheng.Chen@Sun.COM }
110*8503SPengcheng.Chen@Sun.COM 
111*8503SPengcheng.Chen@Sun.COM /*
112*8503SPengcheng.Chen@Sun.COM  * Vendor-specific write to the default control pipe.
113*8503SPengcheng.Chen@Sun.COM  */
114*8503SPengcheng.Chen@Sun.COM static zyd_res
zyd_usb_ctrl_send(struct zyd_usb * uc,uint8_t request,uint16_t value,uint8_t * data,uint16_t len)115*8503SPengcheng.Chen@Sun.COM zyd_usb_ctrl_send(struct zyd_usb *uc, uint8_t request, uint16_t value,
116*8503SPengcheng.Chen@Sun.COM     uint8_t *data, uint16_t len)
117*8503SPengcheng.Chen@Sun.COM {
118*8503SPengcheng.Chen@Sun.COM 	int err;
119*8503SPengcheng.Chen@Sun.COM 	int retry = 0;
120*8503SPengcheng.Chen@Sun.COM 	mblk_t *msg;
121*8503SPengcheng.Chen@Sun.COM 	usb_ctrl_setup_t setup;
122*8503SPengcheng.Chen@Sun.COM 
123*8503SPengcheng.Chen@Sun.COM 	/* Always clean structures before use */
124*8503SPengcheng.Chen@Sun.COM 	bzero(&setup, sizeof (setup));
125*8503SPengcheng.Chen@Sun.COM 	setup.bmRequestType =
126*8503SPengcheng.Chen@Sun.COM 	    USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_HOST_TO_DEV;
127*8503SPengcheng.Chen@Sun.COM 	setup.bRequest = request;
128*8503SPengcheng.Chen@Sun.COM 	setup.wValue = value;
129*8503SPengcheng.Chen@Sun.COM 	setup.wIndex = 0;
130*8503SPengcheng.Chen@Sun.COM 	setup.wLength = len;
131*8503SPengcheng.Chen@Sun.COM 	setup.attrs = USB_ATTRS_NONE;
132*8503SPengcheng.Chen@Sun.COM 
133*8503SPengcheng.Chen@Sun.COM 	if ((msg = allocb(len, BPRI_HI)) == NULL)
134*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
135*8503SPengcheng.Chen@Sun.COM 
136*8503SPengcheng.Chen@Sun.COM 	bcopy(data, msg->b_wptr, len);
137*8503SPengcheng.Chen@Sun.COM 	msg->b_wptr += len;
138*8503SPengcheng.Chen@Sun.COM 
139*8503SPengcheng.Chen@Sun.COM 	while ((err = usb_pipe_ctrl_xfer_wait(uc->cdata->dev_default_ph,
140*8503SPengcheng.Chen@Sun.COM 	    &setup, &msg, NULL, NULL, 0)) != USB_SUCCESS) {
141*8503SPengcheng.Chen@Sun.COM 		if (retry++ > 3)
142*8503SPengcheng.Chen@Sun.COM 			break;
143*8503SPengcheng.Chen@Sun.COM 	}
144*8503SPengcheng.Chen@Sun.COM 
145*8503SPengcheng.Chen@Sun.COM 	freemsg(msg);
146*8503SPengcheng.Chen@Sun.COM 
147*8503SPengcheng.Chen@Sun.COM 	if (err != USB_SUCCESS) {
148*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_USB,
149*8503SPengcheng.Chen@Sun.COM 		    "control pipe send failure (%d)\n", err));
150*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
151*8503SPengcheng.Chen@Sun.COM 	}
152*8503SPengcheng.Chen@Sun.COM 
153*8503SPengcheng.Chen@Sun.COM 	return (ZYD_SUCCESS);
154*8503SPengcheng.Chen@Sun.COM }
155*8503SPengcheng.Chen@Sun.COM 
156*8503SPengcheng.Chen@Sun.COM /*
157*8503SPengcheng.Chen@Sun.COM  * Vendor-specific read from the default control pipe.
158*8503SPengcheng.Chen@Sun.COM  */
159*8503SPengcheng.Chen@Sun.COM static zyd_res
zyd_usb_ctrl_recv(struct zyd_usb * uc,uint8_t request,uint16_t value,uint8_t * data,uint16_t len)160*8503SPengcheng.Chen@Sun.COM zyd_usb_ctrl_recv(struct zyd_usb *uc, uint8_t request, uint16_t value,
161*8503SPengcheng.Chen@Sun.COM     uint8_t *data, uint16_t len)
162*8503SPengcheng.Chen@Sun.COM {
163*8503SPengcheng.Chen@Sun.COM 	int err;
164*8503SPengcheng.Chen@Sun.COM 	mblk_t *msg, *tmp_msg;
165*8503SPengcheng.Chen@Sun.COM 	usb_ctrl_setup_t setup;
166*8503SPengcheng.Chen@Sun.COM 	size_t msg_len;
167*8503SPengcheng.Chen@Sun.COM 
168*8503SPengcheng.Chen@Sun.COM 	ASSERT(data != NULL);
169*8503SPengcheng.Chen@Sun.COM 
170*8503SPengcheng.Chen@Sun.COM 	bzero(&setup, sizeof (setup));
171*8503SPengcheng.Chen@Sun.COM 	setup.bmRequestType =
172*8503SPengcheng.Chen@Sun.COM 	    USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_DEV_TO_HOST;
173*8503SPengcheng.Chen@Sun.COM 	setup.bRequest = request;
174*8503SPengcheng.Chen@Sun.COM 	setup.wValue = value;
175*8503SPengcheng.Chen@Sun.COM 	setup.wIndex = 0;
176*8503SPengcheng.Chen@Sun.COM 	setup.wLength = len;
177*8503SPengcheng.Chen@Sun.COM 	setup.attrs = USB_ATTRS_NONE;
178*8503SPengcheng.Chen@Sun.COM 
179*8503SPengcheng.Chen@Sun.COM 	/* Pointer msg must be either set to NULL or point to a valid mblk! */
180*8503SPengcheng.Chen@Sun.COM 	msg = NULL;
181*8503SPengcheng.Chen@Sun.COM 	err = usb_pipe_ctrl_xfer_wait(uc->cdata->dev_default_ph,
182*8503SPengcheng.Chen@Sun.COM 	    &setup, &msg, NULL, NULL, 0);
183*8503SPengcheng.Chen@Sun.COM 
184*8503SPengcheng.Chen@Sun.COM 	if (err != USB_SUCCESS) {
185*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("control pipe receive failure (%d)\n", err);
186*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
187*8503SPengcheng.Chen@Sun.COM 	}
188*8503SPengcheng.Chen@Sun.COM 
189*8503SPengcheng.Chen@Sun.COM 	msg_len = msgsize(msg);
190*8503SPengcheng.Chen@Sun.COM 
191*8503SPengcheng.Chen@Sun.COM 	if (msg_len != len) {
192*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("control pipe failure: "
193*8503SPengcheng.Chen@Sun.COM 		    "received %d bytes, %d expected\n", (int)msg_len, len);
194*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
195*8503SPengcheng.Chen@Sun.COM 	}
196*8503SPengcheng.Chen@Sun.COM 
197*8503SPengcheng.Chen@Sun.COM 	if (msg->b_cont != NULL) {
198*8503SPengcheng.Chen@Sun.COM 		/* Fragmented message, concatenate */
199*8503SPengcheng.Chen@Sun.COM 		tmp_msg = msgpullup(msg, -1);
200*8503SPengcheng.Chen@Sun.COM 		freemsg(msg);
201*8503SPengcheng.Chen@Sun.COM 		msg = tmp_msg;
202*8503SPengcheng.Chen@Sun.COM 	}
203*8503SPengcheng.Chen@Sun.COM 
204*8503SPengcheng.Chen@Sun.COM 	/*
205*8503SPengcheng.Chen@Sun.COM 	 * Now we can be sure the message is in a single block
206*8503SPengcheng.Chen@Sun.COM 	 * so we can copy it.
207*8503SPengcheng.Chen@Sun.COM 	 */
208*8503SPengcheng.Chen@Sun.COM 	bcopy(msg->b_rptr, data, len);
209*8503SPengcheng.Chen@Sun.COM 	freemsg(msg);
210*8503SPengcheng.Chen@Sun.COM 
211*8503SPengcheng.Chen@Sun.COM 	return (ZYD_SUCCESS);
212*8503SPengcheng.Chen@Sun.COM }
213*8503SPengcheng.Chen@Sun.COM 
214*8503SPengcheng.Chen@Sun.COM /*
215*8503SPengcheng.Chen@Sun.COM  * Load firmware into the chip.
216*8503SPengcheng.Chen@Sun.COM  */
217*8503SPengcheng.Chen@Sun.COM zyd_res
zyd_usb_loadfirmware(struct zyd_usb * uc,uint8_t * fw,size_t size)218*8503SPengcheng.Chen@Sun.COM zyd_usb_loadfirmware(struct zyd_usb *uc, uint8_t *fw, size_t size)
219*8503SPengcheng.Chen@Sun.COM {
220*8503SPengcheng.Chen@Sun.COM 	uint16_t addr;
221*8503SPengcheng.Chen@Sun.COM 	uint8_t stat;
222*8503SPengcheng.Chen@Sun.COM 
223*8503SPengcheng.Chen@Sun.COM 	ZYD_DEBUG((ZYD_DBG_FW, "firmware size: %lu\n", size));
224*8503SPengcheng.Chen@Sun.COM 
225*8503SPengcheng.Chen@Sun.COM 	addr = ZYD_FIRMWARE_START_ADDR;
226*8503SPengcheng.Chen@Sun.COM 	while (size > 0) {
227*8503SPengcheng.Chen@Sun.COM 		const uint16_t mlen = (uint16_t)min(size, 4096);
228*8503SPengcheng.Chen@Sun.COM 
229*8503SPengcheng.Chen@Sun.COM 		if (zyd_usb_ctrl_send(uc, ZYD_DOWNLOADREQ, addr, fw, mlen)
230*8503SPengcheng.Chen@Sun.COM 		    != USB_SUCCESS)
231*8503SPengcheng.Chen@Sun.COM 			return (ZYD_FAILURE);
232*8503SPengcheng.Chen@Sun.COM 
233*8503SPengcheng.Chen@Sun.COM 		addr += mlen / 2;
234*8503SPengcheng.Chen@Sun.COM 		fw += mlen;
235*8503SPengcheng.Chen@Sun.COM 		size -= mlen;
236*8503SPengcheng.Chen@Sun.COM 	}
237*8503SPengcheng.Chen@Sun.COM 
238*8503SPengcheng.Chen@Sun.COM 	/* check whether the upload succeeded */
239*8503SPengcheng.Chen@Sun.COM 	if (zyd_usb_ctrl_recv(uc, ZYD_DOWNLOADSTS, 0, &stat, sizeof (stat))
240*8503SPengcheng.Chen@Sun.COM 	    != ZYD_SUCCESS)
241*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
242*8503SPengcheng.Chen@Sun.COM 
243*8503SPengcheng.Chen@Sun.COM 	return ((stat & 0x80) ? ZYD_FAILURE : ZYD_SUCCESS);
244*8503SPengcheng.Chen@Sun.COM }
245*8503SPengcheng.Chen@Sun.COM 
246*8503SPengcheng.Chen@Sun.COM /*
247*8503SPengcheng.Chen@Sun.COM  * Return a specific alt_if from the device descriptor tree.
248*8503SPengcheng.Chen@Sun.COM  */
249*8503SPengcheng.Chen@Sun.COM static usb_alt_if_data_t *
usb_lookup_alt_if(usb_client_dev_data_t * cdd,uint_t config,uint_t interface,uint_t alt)250*8503SPengcheng.Chen@Sun.COM usb_lookup_alt_if(usb_client_dev_data_t *cdd, uint_t config,
251*8503SPengcheng.Chen@Sun.COM     uint_t interface, uint_t alt)
252*8503SPengcheng.Chen@Sun.COM {
253*8503SPengcheng.Chen@Sun.COM 	usb_cfg_data_t *dcfg;
254*8503SPengcheng.Chen@Sun.COM 	usb_if_data_t *cfgif;
255*8503SPengcheng.Chen@Sun.COM 	usb_alt_if_data_t *ifalt;
256*8503SPengcheng.Chen@Sun.COM 
257*8503SPengcheng.Chen@Sun.COM 	/*
258*8503SPengcheng.Chen@Sun.COM 	 * Assume everything is in the tree for now,
259*8503SPengcheng.Chen@Sun.COM 	 * (USB_PARSE_LVL_ALL)
260*8503SPengcheng.Chen@Sun.COM 	 * so we can directly index the array.
261*8503SPengcheng.Chen@Sun.COM 	 */
262*8503SPengcheng.Chen@Sun.COM 
263*8503SPengcheng.Chen@Sun.COM 	/* Descend to configuration, configs are 1-based */
264*8503SPengcheng.Chen@Sun.COM 	if (config < 1 || config > cdd->dev_n_cfg)
265*8503SPengcheng.Chen@Sun.COM 		return (NULL);
266*8503SPengcheng.Chen@Sun.COM 	dcfg = &cdd->dev_cfg[config - 1];
267*8503SPengcheng.Chen@Sun.COM 
268*8503SPengcheng.Chen@Sun.COM 	/* Descend to interface */
269*8503SPengcheng.Chen@Sun.COM 	if (interface > dcfg->cfg_n_if - 1)
270*8503SPengcheng.Chen@Sun.COM 		return (NULL);
271*8503SPengcheng.Chen@Sun.COM 	cfgif = &dcfg->cfg_if[interface];
272*8503SPengcheng.Chen@Sun.COM 
273*8503SPengcheng.Chen@Sun.COM 	/* Descend to alt */
274*8503SPengcheng.Chen@Sun.COM 	if (alt > cfgif->if_n_alt - 1)
275*8503SPengcheng.Chen@Sun.COM 		return (NULL);
276*8503SPengcheng.Chen@Sun.COM 	ifalt = &cfgif->if_alt[alt];
277*8503SPengcheng.Chen@Sun.COM 
278*8503SPengcheng.Chen@Sun.COM 	return (ifalt);
279*8503SPengcheng.Chen@Sun.COM }
280*8503SPengcheng.Chen@Sun.COM 
281*8503SPengcheng.Chen@Sun.COM /*
282*8503SPengcheng.Chen@Sun.COM  * Print all endpoints of an alt_if.
283*8503SPengcheng.Chen@Sun.COM  */
284*8503SPengcheng.Chen@Sun.COM static void
usb_list_all_endpoints(usb_alt_if_data_t * ifalt)285*8503SPengcheng.Chen@Sun.COM usb_list_all_endpoints(usb_alt_if_data_t *ifalt)
286*8503SPengcheng.Chen@Sun.COM {
287*8503SPengcheng.Chen@Sun.COM 	usb_ep_data_t *ep_data;
288*8503SPengcheng.Chen@Sun.COM 	usb_ep_descr_t *ep_descr;
289*8503SPengcheng.Chen@Sun.COM 	int i;
290*8503SPengcheng.Chen@Sun.COM 
291*8503SPengcheng.Chen@Sun.COM 	for (i = 0; i < ifalt->altif_n_ep; i++) {
292*8503SPengcheng.Chen@Sun.COM 		ep_data = &ifalt->altif_ep[i];
293*8503SPengcheng.Chen@Sun.COM 		ep_descr = &ep_data->ep_descr;
294*8503SPengcheng.Chen@Sun.COM 		cmn_err(CE_NOTE, "EP: %u\n", ep_descr->bEndpointAddress);
295*8503SPengcheng.Chen@Sun.COM 	}
296*8503SPengcheng.Chen@Sun.COM }
297*8503SPengcheng.Chen@Sun.COM 
298*8503SPengcheng.Chen@Sun.COM /*
299*8503SPengcheng.Chen@Sun.COM  * For the given alt_if, find an endpoint with the given
300*8503SPengcheng.Chen@Sun.COM  * address and direction.
301*8503SPengcheng.Chen@Sun.COM  *
302*8503SPengcheng.Chen@Sun.COM  *      ep_direction    USB_EP_DIR_IN or USB_EP_DIR_OUT
303*8503SPengcheng.Chen@Sun.COM  */
304*8503SPengcheng.Chen@Sun.COM static usb_ep_data_t *
usb_find_endpoint(usb_alt_if_data_t * alt_if,uint_t ep_address,uint_t ep_direction)305*8503SPengcheng.Chen@Sun.COM usb_find_endpoint(usb_alt_if_data_t *alt_if,
306*8503SPengcheng.Chen@Sun.COM     uint_t ep_address, uint_t ep_direction)
307*8503SPengcheng.Chen@Sun.COM {
308*8503SPengcheng.Chen@Sun.COM 	usb_ep_data_t *ep_data;
309*8503SPengcheng.Chen@Sun.COM 	usb_ep_descr_t *ep_descr;
310*8503SPengcheng.Chen@Sun.COM 	uint_t ep_addr, ep_dir;
311*8503SPengcheng.Chen@Sun.COM 	int i;
312*8503SPengcheng.Chen@Sun.COM 
313*8503SPengcheng.Chen@Sun.COM 	for (i = 0; i < alt_if->altif_n_ep; i++) {
314*8503SPengcheng.Chen@Sun.COM 		ep_data = &alt_if->altif_ep[i];
315*8503SPengcheng.Chen@Sun.COM 		ep_descr = &ep_data->ep_descr;
316*8503SPengcheng.Chen@Sun.COM 		ep_addr = ep_descr->bEndpointAddress & USB_EP_NUM_MASK;
317*8503SPengcheng.Chen@Sun.COM 		ep_dir = ep_descr->bEndpointAddress & USB_EP_DIR_MASK;
318*8503SPengcheng.Chen@Sun.COM 
319*8503SPengcheng.Chen@Sun.COM 		if (ep_addr == ep_address && ep_dir == ep_direction) {
320*8503SPengcheng.Chen@Sun.COM 			return (ep_data);
321*8503SPengcheng.Chen@Sun.COM 		}
322*8503SPengcheng.Chen@Sun.COM 	}
323*8503SPengcheng.Chen@Sun.COM 
324*8503SPengcheng.Chen@Sun.COM 	ZYD_WARN("no endpoint with addr %u, dir %u\n", ep_address,
325*8503SPengcheng.Chen@Sun.COM 	    ep_direction);
326*8503SPengcheng.Chen@Sun.COM 	return (NULL);
327*8503SPengcheng.Chen@Sun.COM }
328*8503SPengcheng.Chen@Sun.COM 
329*8503SPengcheng.Chen@Sun.COM enum zyd_usb_use_attr
330*8503SPengcheng.Chen@Sun.COM {
331*8503SPengcheng.Chen@Sun.COM 	ZYD_USB_USE_ATTR = 1,
332*8503SPengcheng.Chen@Sun.COM 	ZYD_USB_NO_ATTR = 0
333*8503SPengcheng.Chen@Sun.COM };
334*8503SPengcheng.Chen@Sun.COM 
335*8503SPengcheng.Chen@Sun.COM /*
336*8503SPengcheng.Chen@Sun.COM  * Open a pipe to a given endpoint address/direction in the given
337*8503SPengcheng.Chen@Sun.COM  * alt_if. Furthemore, if use_attr == ZYD_USB_USE_ATTR,
338*8503SPengcheng.Chen@Sun.COM  * check whether the endpoint's transfer type is attr.
339*8503SPengcheng.Chen@Sun.COM  */
340*8503SPengcheng.Chen@Sun.COM static zyd_res
zyd_usb_open_pipe(struct zyd_usb * uc,usb_alt_if_data_t * alt_if,uint_t ep_address,uint_t ep_direction,uint_t attr,enum zyd_usb_use_attr use_attr,usb_pipe_handle_t * pipe,usb_ep_data_t * endpoint)341*8503SPengcheng.Chen@Sun.COM zyd_usb_open_pipe(struct zyd_usb *uc,
342*8503SPengcheng.Chen@Sun.COM     usb_alt_if_data_t *alt_if,
343*8503SPengcheng.Chen@Sun.COM     uint_t ep_address,
344*8503SPengcheng.Chen@Sun.COM     uint_t ep_direction,
345*8503SPengcheng.Chen@Sun.COM     uint_t attr,
346*8503SPengcheng.Chen@Sun.COM     enum zyd_usb_use_attr use_attr,
347*8503SPengcheng.Chen@Sun.COM     usb_pipe_handle_t *pipe, usb_ep_data_t *endpoint)
348*8503SPengcheng.Chen@Sun.COM {
349*8503SPengcheng.Chen@Sun.COM 	usb_pipe_policy_t pipe_policy;
350*8503SPengcheng.Chen@Sun.COM 
351*8503SPengcheng.Chen@Sun.COM 	*endpoint = *usb_find_endpoint(alt_if, ep_address, ep_direction);
352*8503SPengcheng.Chen@Sun.COM 
353*8503SPengcheng.Chen@Sun.COM 	if ((use_attr == ZYD_USB_USE_ATTR) &&
354*8503SPengcheng.Chen@Sun.COM 	    (endpoint->ep_descr.bmAttributes & USB_EP_ATTR_MASK) != attr) {
355*8503SPengcheng.Chen@Sun.COM 
356*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("endpoint %u/%s is not of type %s\n", ep_address,
357*8503SPengcheng.Chen@Sun.COM 		    (ep_direction == USB_EP_DIR_IN) ? "IN" : "OUT",
358*8503SPengcheng.Chen@Sun.COM 		    (attr == USB_EP_ATTR_BULK) ? "bulk" : "intr");
359*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
360*8503SPengcheng.Chen@Sun.COM 	}
361*8503SPengcheng.Chen@Sun.COM 
362*8503SPengcheng.Chen@Sun.COM 	bzero(&pipe_policy, sizeof (usb_pipe_policy_t));
363*8503SPengcheng.Chen@Sun.COM 	pipe_policy.pp_max_async_reqs = ZYD_USB_REQ_COUNT;
364*8503SPengcheng.Chen@Sun.COM 
365*8503SPengcheng.Chen@Sun.COM 	if (usb_pipe_open(uc->dip, &endpoint->ep_descr,
366*8503SPengcheng.Chen@Sun.COM 	    &pipe_policy, USB_FLAGS_SLEEP, pipe) != USB_SUCCESS) {
367*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to open pipe %u\n", ep_address);
368*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
369*8503SPengcheng.Chen@Sun.COM 	}
370*8503SPengcheng.Chen@Sun.COM 
371*8503SPengcheng.Chen@Sun.COM 	return (ZYD_SUCCESS);
372*8503SPengcheng.Chen@Sun.COM }
373*8503SPengcheng.Chen@Sun.COM 
374*8503SPengcheng.Chen@Sun.COM /*
375*8503SPengcheng.Chen@Sun.COM  * Open communication pipes.
376*8503SPengcheng.Chen@Sun.COM  *
377*8503SPengcheng.Chen@Sun.COM  * The following pipes are used by the ZD1211:
378*8503SPengcheng.Chen@Sun.COM  *
379*8503SPengcheng.Chen@Sun.COM  *      1/OUT BULK
380*8503SPengcheng.Chen@Sun.COM  *      2/IN  BULK
381*8503SPengcheng.Chen@Sun.COM  *      3/IN  INTR
382*8503SPengcheng.Chen@Sun.COM  *      4/OUT BULK or INTR
383*8503SPengcheng.Chen@Sun.COM  */
384*8503SPengcheng.Chen@Sun.COM zyd_res
zyd_usb_open_pipes(struct zyd_usb * uc)385*8503SPengcheng.Chen@Sun.COM zyd_usb_open_pipes(struct zyd_usb *uc)
386*8503SPengcheng.Chen@Sun.COM {
387*8503SPengcheng.Chen@Sun.COM 	usb_alt_if_data_t *alt_if;
388*8503SPengcheng.Chen@Sun.COM 
389*8503SPengcheng.Chen@Sun.COM 	ZYD_DEBUG((ZYD_DBG_USB, "opening pipes\n"));
390*8503SPengcheng.Chen@Sun.COM 
391*8503SPengcheng.Chen@Sun.COM 	alt_if = usb_lookup_alt_if(uc->cdata, ZYD_USB_CONFIG_NUMBER,
392*8503SPengcheng.Chen@Sun.COM 	    ZYD_USB_IFACE_INDEX, ZYD_USB_ALT_IF_INDEX);
393*8503SPengcheng.Chen@Sun.COM 
394*8503SPengcheng.Chen@Sun.COM 	if (alt_if == NULL) {
395*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("alt_if not found\n");
396*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
397*8503SPengcheng.Chen@Sun.COM 	}
398*8503SPengcheng.Chen@Sun.COM 
399*8503SPengcheng.Chen@Sun.COM #ifdef DEBUG
400*8503SPengcheng.Chen@Sun.COM 	if (zyd_dbg_flags & ZYD_DBG_USB)
401*8503SPengcheng.Chen@Sun.COM 		usb_list_all_endpoints(alt_if);
402*8503SPengcheng.Chen@Sun.COM #endif
403*8503SPengcheng.Chen@Sun.COM 
404*8503SPengcheng.Chen@Sun.COM 	if (zyd_usb_open_pipe(uc, alt_if, 1, USB_EP_DIR_OUT, USB_EP_ATTR_BULK,
405*8503SPengcheng.Chen@Sun.COM 	    ZYD_USB_USE_ATTR, &uc->pipe_data_out, &uc->ep_data_out) !=
406*8503SPengcheng.Chen@Sun.COM 	    ZYD_SUCCESS) {
407*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to open data OUT pipe\n");
408*8503SPengcheng.Chen@Sun.COM 		goto fail;
409*8503SPengcheng.Chen@Sun.COM 	}
410*8503SPengcheng.Chen@Sun.COM 
411*8503SPengcheng.Chen@Sun.COM 	if (zyd_usb_open_pipe(uc, alt_if, 2, USB_EP_DIR_IN, USB_EP_ATTR_BULK,
412*8503SPengcheng.Chen@Sun.COM 	    ZYD_USB_USE_ATTR, &uc->pipe_data_in, &uc->ep_data_in) !=
413*8503SPengcheng.Chen@Sun.COM 	    ZYD_SUCCESS) {
414*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to open data IN pipe\n");
415*8503SPengcheng.Chen@Sun.COM 		goto fail;
416*8503SPengcheng.Chen@Sun.COM 	}
417*8503SPengcheng.Chen@Sun.COM 
418*8503SPengcheng.Chen@Sun.COM 	if (zyd_usb_open_pipe(uc, alt_if, 3, USB_EP_DIR_IN, USB_EP_ATTR_INTR,
419*8503SPengcheng.Chen@Sun.COM 	    ZYD_USB_USE_ATTR, &uc->pipe_cmd_in, &uc->ep_cmd_in) !=
420*8503SPengcheng.Chen@Sun.COM 	    ZYD_SUCCESS) {
421*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to open command IN pipe\n");
422*8503SPengcheng.Chen@Sun.COM 		goto fail;
423*8503SPengcheng.Chen@Sun.COM 	}
424*8503SPengcheng.Chen@Sun.COM 
425*8503SPengcheng.Chen@Sun.COM 	/*
426*8503SPengcheng.Chen@Sun.COM 	 * Pipe 4/OUT is either a bulk or interrupt pipe.
427*8503SPengcheng.Chen@Sun.COM 	 */
428*8503SPengcheng.Chen@Sun.COM 	if (zyd_usb_open_pipe(uc, alt_if, 4, USB_EP_DIR_OUT, 0,
429*8503SPengcheng.Chen@Sun.COM 	    ZYD_USB_NO_ATTR, &uc->pipe_cmd_out, &uc->ep_cmd_out) !=
430*8503SPengcheng.Chen@Sun.COM 	    ZYD_SUCCESS) {
431*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to open command OUT pipe\n");
432*8503SPengcheng.Chen@Sun.COM 		goto fail;
433*8503SPengcheng.Chen@Sun.COM 	}
434*8503SPengcheng.Chen@Sun.COM 
435*8503SPengcheng.Chen@Sun.COM 	return (ZYD_SUCCESS);
436*8503SPengcheng.Chen@Sun.COM 
437*8503SPengcheng.Chen@Sun.COM fail:
438*8503SPengcheng.Chen@Sun.COM 	zyd_usb_close_pipes(uc);
439*8503SPengcheng.Chen@Sun.COM 	return (ZYD_FAILURE);
440*8503SPengcheng.Chen@Sun.COM }
441*8503SPengcheng.Chen@Sun.COM 
442*8503SPengcheng.Chen@Sun.COM /*
443*8503SPengcheng.Chen@Sun.COM  * Close communication pipes.
444*8503SPengcheng.Chen@Sun.COM  */
445*8503SPengcheng.Chen@Sun.COM void
zyd_usb_close_pipes(struct zyd_usb * uc)446*8503SPengcheng.Chen@Sun.COM zyd_usb_close_pipes(struct zyd_usb *uc)
447*8503SPengcheng.Chen@Sun.COM {
448*8503SPengcheng.Chen@Sun.COM 	ZYD_DEBUG((ZYD_DBG_USB, "closing pipes\n"));
449*8503SPengcheng.Chen@Sun.COM 
450*8503SPengcheng.Chen@Sun.COM 	if (uc->pipe_data_out != NULL) {
451*8503SPengcheng.Chen@Sun.COM 		usb_pipe_close(uc->dip, uc->pipe_data_out, USB_FLAGS_SLEEP,
452*8503SPengcheng.Chen@Sun.COM 		    NULL, NULL);
453*8503SPengcheng.Chen@Sun.COM 		uc->pipe_data_out = NULL;
454*8503SPengcheng.Chen@Sun.COM 	}
455*8503SPengcheng.Chen@Sun.COM 
456*8503SPengcheng.Chen@Sun.COM 	if (uc->pipe_data_in != NULL) {
457*8503SPengcheng.Chen@Sun.COM 		usb_pipe_close(uc->dip, uc->pipe_data_in, USB_FLAGS_SLEEP,
458*8503SPengcheng.Chen@Sun.COM 		    NULL, NULL);
459*8503SPengcheng.Chen@Sun.COM 		uc->pipe_data_in = NULL;
460*8503SPengcheng.Chen@Sun.COM 	}
461*8503SPengcheng.Chen@Sun.COM 
462*8503SPengcheng.Chen@Sun.COM 	if (uc->pipe_cmd_in != NULL) {
463*8503SPengcheng.Chen@Sun.COM 		usb_pipe_close(uc->dip, uc->pipe_cmd_in, USB_FLAGS_SLEEP,
464*8503SPengcheng.Chen@Sun.COM 		    NULL, NULL);
465*8503SPengcheng.Chen@Sun.COM 		uc->pipe_cmd_in = NULL;
466*8503SPengcheng.Chen@Sun.COM 	}
467*8503SPengcheng.Chen@Sun.COM 
468*8503SPengcheng.Chen@Sun.COM 	if (uc->pipe_cmd_out != NULL) {
469*8503SPengcheng.Chen@Sun.COM 		usb_pipe_close(uc->dip, uc->pipe_cmd_out, USB_FLAGS_SLEEP,
470*8503SPengcheng.Chen@Sun.COM 		    NULL, NULL);
471*8503SPengcheng.Chen@Sun.COM 		uc->pipe_cmd_out = NULL;
472*8503SPengcheng.Chen@Sun.COM 	}
473*8503SPengcheng.Chen@Sun.COM }
474*8503SPengcheng.Chen@Sun.COM 
475*8503SPengcheng.Chen@Sun.COM /*
476*8503SPengcheng.Chen@Sun.COM  * Send a sequence of bytes to a bulk pipe.
477*8503SPengcheng.Chen@Sun.COM  *
478*8503SPengcheng.Chen@Sun.COM  *      uc      pointer to usb module state
479*8503SPengcheng.Chen@Sun.COM  *      data    pointer to a buffer of bytes
480*8503SPengcheng.Chen@Sun.COM  *      len     size of the buffer (bytes)
481*8503SPengcheng.Chen@Sun.COM  */
482*8503SPengcheng.Chen@Sun.COM /*ARGSUSED*/
483*8503SPengcheng.Chen@Sun.COM static void
zyd_data_out_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)484*8503SPengcheng.Chen@Sun.COM zyd_data_out_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
485*8503SPengcheng.Chen@Sun.COM {
486*8503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc = (struct zyd_softc *)req->bulk_client_private;
487*8503SPengcheng.Chen@Sun.COM 	struct ieee80211com *ic = &sc->ic;
488*8503SPengcheng.Chen@Sun.COM 	boolean_t resched;
489*8503SPengcheng.Chen@Sun.COM 
490*8503SPengcheng.Chen@Sun.COM 	if (req->bulk_completion_reason != USB_CR_OK)
491*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_USB, "data OUT exception\n"));
492*8503SPengcheng.Chen@Sun.COM 
493*8503SPengcheng.Chen@Sun.COM 	(void) zyd_serial_enter(sc, ZYD_NO_SIG);
494*8503SPengcheng.Chen@Sun.COM 	if (sc->tx_queued > 0)
495*8503SPengcheng.Chen@Sun.COM 		sc->tx_queued--;
496*8503SPengcheng.Chen@Sun.COM 	else
497*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_TX, "tx queue underrun\n"));
498*8503SPengcheng.Chen@Sun.COM 
499*8503SPengcheng.Chen@Sun.COM 	if (sc->resched && (sc->tx_queued < ZYD_TX_LIST_COUNT)) {
500*8503SPengcheng.Chen@Sun.COM 		resched = sc->resched;
501*8503SPengcheng.Chen@Sun.COM 		sc->resched = B_FALSE;
502*8503SPengcheng.Chen@Sun.COM 	}
503*8503SPengcheng.Chen@Sun.COM 	zyd_serial_exit(sc);
504*8503SPengcheng.Chen@Sun.COM 
505*8503SPengcheng.Chen@Sun.COM 	if (resched)
506*8503SPengcheng.Chen@Sun.COM 		mac_tx_update(ic->ic_mach);
507*8503SPengcheng.Chen@Sun.COM 
508*8503SPengcheng.Chen@Sun.COM 	usb_free_bulk_req(req);
509*8503SPengcheng.Chen@Sun.COM }
510*8503SPengcheng.Chen@Sun.COM 
511*8503SPengcheng.Chen@Sun.COM /*
512*8503SPengcheng.Chen@Sun.COM  * Called when the transfer from zyd_usb_bulk_pipe_send() terminates
513*8503SPengcheng.Chen@Sun.COM  * or an exception occurs on the pipe.
514*8503SPengcheng.Chen@Sun.COM  */
515*8503SPengcheng.Chen@Sun.COM /*ARGSUSED*/
516*8503SPengcheng.Chen@Sun.COM static void
zyd_bulk_pipe_cb(usb_pipe_handle_t pipe,struct usb_bulk_req * req)517*8503SPengcheng.Chen@Sun.COM zyd_bulk_pipe_cb(usb_pipe_handle_t pipe, struct usb_bulk_req *req)
518*8503SPengcheng.Chen@Sun.COM {
519*8503SPengcheng.Chen@Sun.COM 	struct zyd_cb_lock *lock;
520*8503SPengcheng.Chen@Sun.COM 	lock = (struct zyd_cb_lock *)req->bulk_client_private;
521*8503SPengcheng.Chen@Sun.COM 
522*8503SPengcheng.Chen@Sun.COM 	/* Just signal that something happened */
523*8503SPengcheng.Chen@Sun.COM 	zyd_cb_lock_signal(lock);
524*8503SPengcheng.Chen@Sun.COM }
525*8503SPengcheng.Chen@Sun.COM 
526*8503SPengcheng.Chen@Sun.COM static zyd_res
zyd_usb_bulk_pipe_send(struct zyd_usb * uc,usb_pipe_handle_t pipe,const void * data,size_t len)527*8503SPengcheng.Chen@Sun.COM zyd_usb_bulk_pipe_send(struct zyd_usb *uc,
528*8503SPengcheng.Chen@Sun.COM     usb_pipe_handle_t pipe, const void *data, size_t len)
529*8503SPengcheng.Chen@Sun.COM {
530*8503SPengcheng.Chen@Sun.COM 	usb_bulk_req_t *send_req;
531*8503SPengcheng.Chen@Sun.COM 	mblk_t *mblk;
532*8503SPengcheng.Chen@Sun.COM 	int res;
533*8503SPengcheng.Chen@Sun.COM 	struct zyd_cb_lock lock;
534*8503SPengcheng.Chen@Sun.COM 
535*8503SPengcheng.Chen@Sun.COM 	send_req = usb_alloc_bulk_req(uc->dip, len, USB_FLAGS_SLEEP);
536*8503SPengcheng.Chen@Sun.COM 	if (send_req == NULL) {
537*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to allocate bulk request\n");
538*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
539*8503SPengcheng.Chen@Sun.COM 	}
540*8503SPengcheng.Chen@Sun.COM 	send_req->bulk_len = (uint_t)len;
541*8503SPengcheng.Chen@Sun.COM 	send_req->bulk_client_private = (usb_opaque_t)&lock;
542*8503SPengcheng.Chen@Sun.COM 	send_req->bulk_attributes = USB_ATTRS_AUTOCLEARING;
543*8503SPengcheng.Chen@Sun.COM 	send_req->bulk_timeout = 5;
544*8503SPengcheng.Chen@Sun.COM 	send_req->bulk_cb = zyd_bulk_pipe_cb;
545*8503SPengcheng.Chen@Sun.COM 	send_req->bulk_exc_cb = zyd_bulk_pipe_cb;
546*8503SPengcheng.Chen@Sun.COM 
547*8503SPengcheng.Chen@Sun.COM 	mblk = send_req->bulk_data;
548*8503SPengcheng.Chen@Sun.COM 	bcopy(data, mblk->b_wptr, len);
549*8503SPengcheng.Chen@Sun.COM 	mblk->b_wptr += len;
550*8503SPengcheng.Chen@Sun.COM 
551*8503SPengcheng.Chen@Sun.COM 	zyd_cb_lock_init(&lock);
552*8503SPengcheng.Chen@Sun.COM 
553*8503SPengcheng.Chen@Sun.COM 	res = usb_pipe_bulk_xfer(pipe, send_req, USB_FLAGS_NOSLEEP);
554*8503SPengcheng.Chen@Sun.COM 	if (res != USB_SUCCESS) {
555*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_USB,
556*8503SPengcheng.Chen@Sun.COM 		    "failed writing to bulk OUT pipe (%d)\n", res));
557*8503SPengcheng.Chen@Sun.COM 		usb_free_bulk_req(send_req);
558*8503SPengcheng.Chen@Sun.COM 		zyd_cb_lock_destroy(&lock);
559*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
560*8503SPengcheng.Chen@Sun.COM 	}
561*8503SPengcheng.Chen@Sun.COM 
562*8503SPengcheng.Chen@Sun.COM 	if (zyd_cb_lock_wait(&lock, 1000000) != ZYD_SUCCESS) {
563*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("timeout - pipe reset\n");
564*8503SPengcheng.Chen@Sun.COM 		usb_pipe_reset(uc->dip, pipe, USB_FLAGS_SLEEP, NULL, 0);
565*8503SPengcheng.Chen@Sun.COM 		(void) zyd_cb_lock_wait(&lock, -1);
566*8503SPengcheng.Chen@Sun.COM 		res = ZYD_FAILURE;
567*8503SPengcheng.Chen@Sun.COM 	} else {
568*8503SPengcheng.Chen@Sun.COM 		res = (send_req->bulk_completion_reason == USB_CR_OK) ?
569*8503SPengcheng.Chen@Sun.COM 		    ZYD_SUCCESS : ZYD_FAILURE;
570*8503SPengcheng.Chen@Sun.COM 	}
571*8503SPengcheng.Chen@Sun.COM 
572*8503SPengcheng.Chen@Sun.COM 	usb_free_bulk_req(send_req);
573*8503SPengcheng.Chen@Sun.COM 	zyd_cb_lock_destroy(&lock);
574*8503SPengcheng.Chen@Sun.COM 	return (res);
575*8503SPengcheng.Chen@Sun.COM }
576*8503SPengcheng.Chen@Sun.COM 
577*8503SPengcheng.Chen@Sun.COM /*
578*8503SPengcheng.Chen@Sun.COM  * Called when the transfer from zyd_usb_intr_pipe_send() terminates
579*8503SPengcheng.Chen@Sun.COM  * or an exception occurs on the pipe.
580*8503SPengcheng.Chen@Sun.COM  */
581*8503SPengcheng.Chen@Sun.COM /*ARGSUSED*/
582*8503SPengcheng.Chen@Sun.COM static void
zyd_intr_pipe_cb(usb_pipe_handle_t pipe,struct usb_intr_req * req)583*8503SPengcheng.Chen@Sun.COM zyd_intr_pipe_cb(usb_pipe_handle_t pipe, struct usb_intr_req *req)
584*8503SPengcheng.Chen@Sun.COM {
585*8503SPengcheng.Chen@Sun.COM 	struct zyd_cb_lock *lock;
586*8503SPengcheng.Chen@Sun.COM 	lock = (struct zyd_cb_lock *)req->intr_client_private;
587*8503SPengcheng.Chen@Sun.COM 
588*8503SPengcheng.Chen@Sun.COM 	/* Just signal that something happened */
589*8503SPengcheng.Chen@Sun.COM 	zyd_cb_lock_signal(lock);
590*8503SPengcheng.Chen@Sun.COM }
591*8503SPengcheng.Chen@Sun.COM 
592*8503SPengcheng.Chen@Sun.COM /*
593*8503SPengcheng.Chen@Sun.COM  * Send a sequence of bytes to an interrupt pipe.
594*8503SPengcheng.Chen@Sun.COM  *
595*8503SPengcheng.Chen@Sun.COM  *      uc      pointer to usb module state
596*8503SPengcheng.Chen@Sun.COM  *      data    pointer to a buffer of bytes
597*8503SPengcheng.Chen@Sun.COM  *      len     size of the buffer (bytes)
598*8503SPengcheng.Chen@Sun.COM  */
599*8503SPengcheng.Chen@Sun.COM static zyd_res
zyd_usb_intr_pipe_send(struct zyd_usb * uc,usb_pipe_handle_t pipe,const void * data,size_t len)600*8503SPengcheng.Chen@Sun.COM zyd_usb_intr_pipe_send(struct zyd_usb *uc,
601*8503SPengcheng.Chen@Sun.COM     usb_pipe_handle_t pipe, const void *data, size_t len)
602*8503SPengcheng.Chen@Sun.COM {
603*8503SPengcheng.Chen@Sun.COM 	usb_intr_req_t *send_req;
604*8503SPengcheng.Chen@Sun.COM 	mblk_t *mblk;
605*8503SPengcheng.Chen@Sun.COM 	int res;
606*8503SPengcheng.Chen@Sun.COM 	struct zyd_cb_lock lock;
607*8503SPengcheng.Chen@Sun.COM 
608*8503SPengcheng.Chen@Sun.COM 	send_req = usb_alloc_intr_req(uc->dip, len, USB_FLAGS_SLEEP);
609*8503SPengcheng.Chen@Sun.COM 	if (send_req == NULL) {
610*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to allocate interupt request\n");
611*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
612*8503SPengcheng.Chen@Sun.COM 	}
613*8503SPengcheng.Chen@Sun.COM 	send_req->intr_len = (uint_t)len;
614*8503SPengcheng.Chen@Sun.COM 	send_req->intr_client_private = (usb_opaque_t)&lock;
615*8503SPengcheng.Chen@Sun.COM 	send_req->intr_attributes = USB_ATTRS_AUTOCLEARING;
616*8503SPengcheng.Chen@Sun.COM 	send_req->intr_timeout = 5;
617*8503SPengcheng.Chen@Sun.COM 	send_req->intr_cb = zyd_intr_pipe_cb;
618*8503SPengcheng.Chen@Sun.COM 	send_req->intr_exc_cb = zyd_intr_pipe_cb;
619*8503SPengcheng.Chen@Sun.COM 
620*8503SPengcheng.Chen@Sun.COM 	mblk = send_req->intr_data;
621*8503SPengcheng.Chen@Sun.COM 	bcopy(data, mblk->b_wptr, len);
622*8503SPengcheng.Chen@Sun.COM 	mblk->b_wptr += len;
623*8503SPengcheng.Chen@Sun.COM 
624*8503SPengcheng.Chen@Sun.COM 	zyd_cb_lock_init(&lock);
625*8503SPengcheng.Chen@Sun.COM 
626*8503SPengcheng.Chen@Sun.COM 	res = usb_pipe_intr_xfer(pipe, send_req, 0);
627*8503SPengcheng.Chen@Sun.COM 	if (res != USB_SUCCESS) {
628*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_USB,
629*8503SPengcheng.Chen@Sun.COM 		    "failed writing to intr/out pipe (%d)\n", res));
630*8503SPengcheng.Chen@Sun.COM 		usb_free_intr_req(send_req);
631*8503SPengcheng.Chen@Sun.COM 		zyd_cb_lock_destroy(&lock);
632*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
633*8503SPengcheng.Chen@Sun.COM 	}
634*8503SPengcheng.Chen@Sun.COM 
635*8503SPengcheng.Chen@Sun.COM 	if (zyd_cb_lock_wait(&lock, 1000000) != ZYD_SUCCESS) {
636*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("timeout - pipe reset\n");
637*8503SPengcheng.Chen@Sun.COM 		usb_pipe_reset(uc->dip, pipe, USB_FLAGS_SLEEP, NULL, 0);
638*8503SPengcheng.Chen@Sun.COM 		(void) zyd_cb_lock_wait(&lock, -1);
639*8503SPengcheng.Chen@Sun.COM 		res = ZYD_FAILURE;
640*8503SPengcheng.Chen@Sun.COM 	} else {
641*8503SPengcheng.Chen@Sun.COM 		res = (send_req->intr_completion_reason == USB_CR_OK) ?
642*8503SPengcheng.Chen@Sun.COM 		    ZYD_SUCCESS : ZYD_FAILURE;
643*8503SPengcheng.Chen@Sun.COM 	}
644*8503SPengcheng.Chen@Sun.COM 
645*8503SPengcheng.Chen@Sun.COM 	usb_free_intr_req(send_req);
646*8503SPengcheng.Chen@Sun.COM 	zyd_cb_lock_destroy(&lock);
647*8503SPengcheng.Chen@Sun.COM 	return (res);
648*8503SPengcheng.Chen@Sun.COM }
649*8503SPengcheng.Chen@Sun.COM 
650*8503SPengcheng.Chen@Sun.COM /*
651*8503SPengcheng.Chen@Sun.COM  * Send a sequence of bytes to the cmd_out pipe. (in a single USB transfer)
652*8503SPengcheng.Chen@Sun.COM  *
653*8503SPengcheng.Chen@Sun.COM  *      uc      pointer to usb module state
654*8503SPengcheng.Chen@Sun.COM  *      data    pointer to a buffer of bytes
655*8503SPengcheng.Chen@Sun.COM  *      len     size of the buffer (bytes)
656*8503SPengcheng.Chen@Sun.COM  */
657*8503SPengcheng.Chen@Sun.COM static zyd_res
zyd_usb_cmd_pipe_send(struct zyd_usb * uc,const void * data,size_t len)658*8503SPengcheng.Chen@Sun.COM zyd_usb_cmd_pipe_send(struct zyd_usb *uc, const void *data, size_t len)
659*8503SPengcheng.Chen@Sun.COM {
660*8503SPengcheng.Chen@Sun.COM 	zyd_res res;
661*8503SPengcheng.Chen@Sun.COM 	uint8_t type;
662*8503SPengcheng.Chen@Sun.COM 
663*8503SPengcheng.Chen@Sun.COM 	/* Determine the type of cmd_out */
664*8503SPengcheng.Chen@Sun.COM 	type = uc->ep_cmd_out.ep_descr.bmAttributes & USB_EP_ATTR_MASK;
665*8503SPengcheng.Chen@Sun.COM 	if (type == USB_EP_ATTR_BULK)
666*8503SPengcheng.Chen@Sun.COM 		res = zyd_usb_bulk_pipe_send(uc, uc->pipe_cmd_out, data, len);
667*8503SPengcheng.Chen@Sun.COM 	else
668*8503SPengcheng.Chen@Sun.COM 		res = zyd_usb_intr_pipe_send(uc, uc->pipe_cmd_out, data, len);
669*8503SPengcheng.Chen@Sun.COM 
670*8503SPengcheng.Chen@Sun.COM 	return (res);
671*8503SPengcheng.Chen@Sun.COM }
672*8503SPengcheng.Chen@Sun.COM 
673*8503SPengcheng.Chen@Sun.COM 
674*8503SPengcheng.Chen@Sun.COM /*
675*8503SPengcheng.Chen@Sun.COM  * Format and send a command to the cmd_out pipe.
676*8503SPengcheng.Chen@Sun.COM  *
677*8503SPengcheng.Chen@Sun.COM  *      uc      pointer to usb module state
678*8503SPengcheng.Chen@Sun.COM  *      code    ZD command code (16-bit)
679*8503SPengcheng.Chen@Sun.COM  *      data    raw buffer containing command data
680*8503SPengcheng.Chen@Sun.COM  *      len     size of the data buffer (bytes)
681*8503SPengcheng.Chen@Sun.COM  */
682*8503SPengcheng.Chen@Sun.COM zyd_res
zyd_usb_cmd_send(struct zyd_usb * uc,uint16_t code,const void * data,size_t len)683*8503SPengcheng.Chen@Sun.COM zyd_usb_cmd_send(struct zyd_usb *uc,
684*8503SPengcheng.Chen@Sun.COM     uint16_t code, const void *data, size_t len)
685*8503SPengcheng.Chen@Sun.COM {
686*8503SPengcheng.Chen@Sun.COM 	zyd_res res;
687*8503SPengcheng.Chen@Sun.COM 	struct zyd_cmd cmd;
688*8503SPengcheng.Chen@Sun.COM 
689*8503SPengcheng.Chen@Sun.COM 	cmd.cmd_code = LE_16(code);
690*8503SPengcheng.Chen@Sun.COM 	bcopy(data, cmd.data, len);
691*8503SPengcheng.Chen@Sun.COM 
692*8503SPengcheng.Chen@Sun.COM 	res = zyd_usb_cmd_pipe_send(uc, &cmd, sizeof (uint16_t) + len);
693*8503SPengcheng.Chen@Sun.COM 	if (res != ZYD_SUCCESS) {
694*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_USB, "failed writing command (%d)\n", res));
695*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
696*8503SPengcheng.Chen@Sun.COM 	}
697*8503SPengcheng.Chen@Sun.COM 
698*8503SPengcheng.Chen@Sun.COM 	return (ZYD_SUCCESS);
699*8503SPengcheng.Chen@Sun.COM }
700*8503SPengcheng.Chen@Sun.COM 
701*8503SPengcheng.Chen@Sun.COM /*
702*8503SPengcheng.Chen@Sun.COM  * Issue an ioread request.
703*8503SPengcheng.Chen@Sun.COM  *
704*8503SPengcheng.Chen@Sun.COM  * Issues a ZD ioread command (with a vector of addresses passed in raw
705*8503SPengcheng.Chen@Sun.COM  * form as in_data) and blocks until the response is received
706*8503SPengcheng.Chen@Sun.COM  * and filled into the response buffer.
707*8503SPengcheng.Chen@Sun.COM  *
708*8503SPengcheng.Chen@Sun.COM  *      uc              pointer to usb module state
709*8503SPengcheng.Chen@Sun.COM  *      in_data         pointer to request data
710*8503SPengcheng.Chen@Sun.COM  *      in_len          request data size (bytes)
711*8503SPengcheng.Chen@Sun.COM  *      out_data        pointer to response buffer
712*8503SPengcheng.Chen@Sun.COM  *      out_len         response buffer size (bytes)
713*8503SPengcheng.Chen@Sun.COM  */
714*8503SPengcheng.Chen@Sun.COM zyd_res
zyd_usb_ioread_req(struct zyd_usb * uc,const void * in_data,size_t in_len,void * out_data,size_t out_len)715*8503SPengcheng.Chen@Sun.COM zyd_usb_ioread_req(struct zyd_usb *uc,
716*8503SPengcheng.Chen@Sun.COM     const void *in_data, size_t in_len, void *out_data, size_t out_len)
717*8503SPengcheng.Chen@Sun.COM {
718*8503SPengcheng.Chen@Sun.COM 	zyd_res res;
719*8503SPengcheng.Chen@Sun.COM 	int cnt;
720*8503SPengcheng.Chen@Sun.COM 
721*8503SPengcheng.Chen@Sun.COM 	/* Initialise io_read structure */
722*8503SPengcheng.Chen@Sun.COM 	uc->io_read.done = B_FALSE;
723*8503SPengcheng.Chen@Sun.COM 	uc->io_read.buffer = out_data;
724*8503SPengcheng.Chen@Sun.COM 	uc->io_read.buf_len = (int)out_len;
725*8503SPengcheng.Chen@Sun.COM 
726*8503SPengcheng.Chen@Sun.COM 	uc->io_read.pending = B_TRUE;
727*8503SPengcheng.Chen@Sun.COM 
728*8503SPengcheng.Chen@Sun.COM 	res = zyd_usb_cmd_send(uc, ZYD_CMD_IORD, in_data, in_len);
729*8503SPengcheng.Chen@Sun.COM 	if (res != ZYD_SUCCESS) {
730*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_USB, "IO read request: pipe failure(%d)\n"));
731*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
732*8503SPengcheng.Chen@Sun.COM 	}
733*8503SPengcheng.Chen@Sun.COM 
734*8503SPengcheng.Chen@Sun.COM 	cnt = 0;
735*8503SPengcheng.Chen@Sun.COM 	while (uc->io_read.done != B_TRUE && cnt < 500) {
736*8503SPengcheng.Chen@Sun.COM 		delay(drv_usectohz(10 * 1000));
737*8503SPengcheng.Chen@Sun.COM 		++cnt;
738*8503SPengcheng.Chen@Sun.COM 	}
739*8503SPengcheng.Chen@Sun.COM 
740*8503SPengcheng.Chen@Sun.COM 	if (uc->io_read.done != B_TRUE) {
741*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("I/O read request: timeout\n");
742*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
743*8503SPengcheng.Chen@Sun.COM 	}
744*8503SPengcheng.Chen@Sun.COM 
745*8503SPengcheng.Chen@Sun.COM 	if (uc->io_read.exc != B_FALSE) {
746*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_USB, "I/O read request: exception\n"));
747*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
748*8503SPengcheng.Chen@Sun.COM 	}
749*8503SPengcheng.Chen@Sun.COM 
750*8503SPengcheng.Chen@Sun.COM 	return (ZYD_SUCCESS);
751*8503SPengcheng.Chen@Sun.COM }
752*8503SPengcheng.Chen@Sun.COM 
753*8503SPengcheng.Chen@Sun.COM 
754*8503SPengcheng.Chen@Sun.COM /*
755*8503SPengcheng.Chen@Sun.COM  * Called when data arrives from the cmd_in pipe.
756*8503SPengcheng.Chen@Sun.COM  */
757*8503SPengcheng.Chen@Sun.COM /*ARGSUSED*/
758*8503SPengcheng.Chen@Sun.COM static void
zyd_cmd_in_cb(usb_pipe_handle_t pipe,usb_intr_req_t * req)759*8503SPengcheng.Chen@Sun.COM zyd_cmd_in_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req)
760*8503SPengcheng.Chen@Sun.COM {
761*8503SPengcheng.Chen@Sun.COM 	struct zyd_usb *uc;
762*8503SPengcheng.Chen@Sun.COM 	struct zyd_ioread *rdp;
763*8503SPengcheng.Chen@Sun.COM 	mblk_t *mblk, *tmp_blk;
764*8503SPengcheng.Chen@Sun.COM 	unsigned char *data;
765*8503SPengcheng.Chen@Sun.COM 	size_t len;
766*8503SPengcheng.Chen@Sun.COM 	uint16_t code;
767*8503SPengcheng.Chen@Sun.COM 
768*8503SPengcheng.Chen@Sun.COM 	uc = (struct zyd_usb *)req->intr_client_private;
769*8503SPengcheng.Chen@Sun.COM 	ASSERT(uc != NULL);
770*8503SPengcheng.Chen@Sun.COM 	rdp = &uc->io_read;
771*8503SPengcheng.Chen@Sun.COM 	mblk = req->intr_data;
772*8503SPengcheng.Chen@Sun.COM 
773*8503SPengcheng.Chen@Sun.COM 	if (mblk->b_cont != NULL) {
774*8503SPengcheng.Chen@Sun.COM 		/* Fragmented message, concatenate */
775*8503SPengcheng.Chen@Sun.COM 		tmp_blk = msgpullup(mblk, -1);
776*8503SPengcheng.Chen@Sun.COM 		data = tmp_blk->b_rptr;
777*8503SPengcheng.Chen@Sun.COM 		len = MBLKL(tmp_blk);
778*8503SPengcheng.Chen@Sun.COM 	} else {
779*8503SPengcheng.Chen@Sun.COM 		/* Non-fragmented message, use directly */
780*8503SPengcheng.Chen@Sun.COM 		tmp_blk = NULL;
781*8503SPengcheng.Chen@Sun.COM 		data = mblk->b_rptr;
782*8503SPengcheng.Chen@Sun.COM 		len = MBLKL(mblk);
783*8503SPengcheng.Chen@Sun.COM 	}
784*8503SPengcheng.Chen@Sun.COM 
785*8503SPengcheng.Chen@Sun.COM 	code = LE_16(*(uint16_t *)(uintptr_t)data);
786*8503SPengcheng.Chen@Sun.COM 	if (code != ZYD_RESPONSE_IOREAD) {
787*8503SPengcheng.Chen@Sun.COM 		/* Other response types not handled yet */
788*8503SPengcheng.Chen@Sun.COM 		usb_free_intr_req(req);
789*8503SPengcheng.Chen@Sun.COM 		return;
790*8503SPengcheng.Chen@Sun.COM 	}
791*8503SPengcheng.Chen@Sun.COM 
792*8503SPengcheng.Chen@Sun.COM 	if (rdp->pending != B_TRUE) {
793*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("no ioread pending\n");
794*8503SPengcheng.Chen@Sun.COM 		usb_free_intr_req(req);
795*8503SPengcheng.Chen@Sun.COM 		return;
796*8503SPengcheng.Chen@Sun.COM 	}
797*8503SPengcheng.Chen@Sun.COM 	rdp->pending = B_FALSE;
798*8503SPengcheng.Chen@Sun.COM 
799*8503SPengcheng.Chen@Sun.COM 	/* Now move on to the data part */
800*8503SPengcheng.Chen@Sun.COM 	data += sizeof (uint16_t);
801*8503SPengcheng.Chen@Sun.COM 	len -= sizeof (uint16_t);
802*8503SPengcheng.Chen@Sun.COM 	if (rdp->buf_len > len) {
803*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("too few bytes received\n");
804*8503SPengcheng.Chen@Sun.COM 	}
805*8503SPengcheng.Chen@Sun.COM 
806*8503SPengcheng.Chen@Sun.COM 	bcopy(data, rdp->buffer, rdp->buf_len);
807*8503SPengcheng.Chen@Sun.COM 
808*8503SPengcheng.Chen@Sun.COM 	if (tmp_blk != NULL)
809*8503SPengcheng.Chen@Sun.COM 		freemsg(tmp_blk);
810*8503SPengcheng.Chen@Sun.COM 
811*8503SPengcheng.Chen@Sun.COM 	rdp->exc = B_FALSE;
812*8503SPengcheng.Chen@Sun.COM 	rdp->done = B_TRUE;
813*8503SPengcheng.Chen@Sun.COM 	usb_free_intr_req(req);
814*8503SPengcheng.Chen@Sun.COM }
815*8503SPengcheng.Chen@Sun.COM 
816*8503SPengcheng.Chen@Sun.COM /*
817*8503SPengcheng.Chen@Sun.COM  * Called when an exception occurs on the cmd_in pipe.
818*8503SPengcheng.Chen@Sun.COM  */
819*8503SPengcheng.Chen@Sun.COM /*ARGSUSED*/
820*8503SPengcheng.Chen@Sun.COM static void
zyd_cmd_in_exc_cb(usb_pipe_handle_t pipe,usb_intr_req_t * req)821*8503SPengcheng.Chen@Sun.COM zyd_cmd_in_exc_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req)
822*8503SPengcheng.Chen@Sun.COM {
823*8503SPengcheng.Chen@Sun.COM 	struct zyd_usb *uc;
824*8503SPengcheng.Chen@Sun.COM 	struct zyd_ioread *rdp;
825*8503SPengcheng.Chen@Sun.COM 
826*8503SPengcheng.Chen@Sun.COM 	ZYD_DEBUG((ZYD_DBG_USB, "command IN exception\n"));
827*8503SPengcheng.Chen@Sun.COM 
828*8503SPengcheng.Chen@Sun.COM 	uc = (struct zyd_usb *)req->intr_client_private;
829*8503SPengcheng.Chen@Sun.COM 	ASSERT(uc != NULL);
830*8503SPengcheng.Chen@Sun.COM 	rdp = &uc->io_read;
831*8503SPengcheng.Chen@Sun.COM 
832*8503SPengcheng.Chen@Sun.COM 	if (rdp->pending == B_TRUE) {
833*8503SPengcheng.Chen@Sun.COM 		rdp->exc = B_TRUE;
834*8503SPengcheng.Chen@Sun.COM 		rdp->done = B_TRUE;
835*8503SPengcheng.Chen@Sun.COM 	}
836*8503SPengcheng.Chen@Sun.COM 	usb_free_intr_req(req);
837*8503SPengcheng.Chen@Sun.COM }
838*8503SPengcheng.Chen@Sun.COM 
839*8503SPengcheng.Chen@Sun.COM /*
840*8503SPengcheng.Chen@Sun.COM  * Start interrupt polling on the cmd_in pipe.
841*8503SPengcheng.Chen@Sun.COM  */
842*8503SPengcheng.Chen@Sun.COM zyd_res
zyd_usb_cmd_in_start_polling(struct zyd_usb * uc)843*8503SPengcheng.Chen@Sun.COM zyd_usb_cmd_in_start_polling(struct zyd_usb *uc)
844*8503SPengcheng.Chen@Sun.COM {
845*8503SPengcheng.Chen@Sun.COM 	usb_intr_req_t *intr_req;
846*8503SPengcheng.Chen@Sun.COM 	int res;
847*8503SPengcheng.Chen@Sun.COM 
848*8503SPengcheng.Chen@Sun.COM 	intr_req = usb_alloc_intr_req(uc->dip, 0, USB_FLAGS_SLEEP);
849*8503SPengcheng.Chen@Sun.COM 	if (intr_req == NULL) {
850*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to allocate interrupt request\n");
851*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
852*8503SPengcheng.Chen@Sun.COM 	}
853*8503SPengcheng.Chen@Sun.COM 
854*8503SPengcheng.Chen@Sun.COM 	intr_req->intr_attributes = USB_ATTRS_SHORT_XFER_OK;
855*8503SPengcheng.Chen@Sun.COM 	intr_req->intr_len = uc->ep_cmd_in.ep_descr.wMaxPacketSize;
856*8503SPengcheng.Chen@Sun.COM 	intr_req->intr_cb = zyd_cmd_in_cb;
857*8503SPengcheng.Chen@Sun.COM 	intr_req->intr_exc_cb = zyd_cmd_in_exc_cb;
858*8503SPengcheng.Chen@Sun.COM 	intr_req->intr_client_private = (usb_opaque_t)uc;
859*8503SPengcheng.Chen@Sun.COM 
860*8503SPengcheng.Chen@Sun.COM 	res = usb_pipe_intr_xfer(uc->pipe_cmd_in, intr_req, USB_FLAGS_NOSLEEP);
861*8503SPengcheng.Chen@Sun.COM 	if (res != USB_SUCCESS) {
862*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed starting command IN polling: pipe failure\n");
863*8503SPengcheng.Chen@Sun.COM 		usb_free_intr_req(intr_req);
864*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
865*8503SPengcheng.Chen@Sun.COM 	}
866*8503SPengcheng.Chen@Sun.COM 
867*8503SPengcheng.Chen@Sun.COM 	return (ZYD_SUCCESS);
868*8503SPengcheng.Chen@Sun.COM }
869*8503SPengcheng.Chen@Sun.COM 
870*8503SPengcheng.Chen@Sun.COM /*
871*8503SPengcheng.Chen@Sun.COM  * Stop interrupt polling on the cmd_in pipe.
872*8503SPengcheng.Chen@Sun.COM  */
873*8503SPengcheng.Chen@Sun.COM void
zyd_usb_cmd_in_stop_polling(struct zyd_usb * uc)874*8503SPengcheng.Chen@Sun.COM zyd_usb_cmd_in_stop_polling(struct zyd_usb *uc)
875*8503SPengcheng.Chen@Sun.COM {
876*8503SPengcheng.Chen@Sun.COM 	ZYD_DEBUG((ZYD_DBG_USB, "stopping command IN polling\n"));
877*8503SPengcheng.Chen@Sun.COM 
878*8503SPengcheng.Chen@Sun.COM 	usb_pipe_stop_intr_polling(uc->pipe_cmd_in, USB_FLAGS_SLEEP);
879*8503SPengcheng.Chen@Sun.COM }
880*8503SPengcheng.Chen@Sun.COM 
881*8503SPengcheng.Chen@Sun.COM /*
882*8503SPengcheng.Chen@Sun.COM  * Called when data arrives on the data_in pipe.
883*8503SPengcheng.Chen@Sun.COM  */
884*8503SPengcheng.Chen@Sun.COM /*ARGSUSED*/
885*8503SPengcheng.Chen@Sun.COM static void
zyd_data_in_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)886*8503SPengcheng.Chen@Sun.COM zyd_data_in_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
887*8503SPengcheng.Chen@Sun.COM {
888*8503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc;
889*8503SPengcheng.Chen@Sun.COM 	struct zyd_usb *uc;
890*8503SPengcheng.Chen@Sun.COM 	mblk_t *mblk, *tmp_blk;
891*8503SPengcheng.Chen@Sun.COM 	struct zyd_rx_desc *desc;
892*8503SPengcheng.Chen@Sun.COM 	unsigned char *data;
893*8503SPengcheng.Chen@Sun.COM 	size_t len;
894*8503SPengcheng.Chen@Sun.COM 
895*8503SPengcheng.Chen@Sun.COM 	uc = (struct zyd_usb *)req->bulk_client_private;
896*8503SPengcheng.Chen@Sun.COM 	ASSERT(uc != NULL);
897*8503SPengcheng.Chen@Sun.COM 	sc = ZYD_USB_TO_SOFTC(uc);
898*8503SPengcheng.Chen@Sun.COM 	ASSERT(sc != NULL);
899*8503SPengcheng.Chen@Sun.COM 	mblk = req->bulk_data;
900*8503SPengcheng.Chen@Sun.COM 
901*8503SPengcheng.Chen@Sun.COM 	/* Fragmented STREAMS message? */
902*8503SPengcheng.Chen@Sun.COM 	if (mblk->b_cont != NULL) {
903*8503SPengcheng.Chen@Sun.COM 		/* Fragmented, concatenate it into a single block */
904*8503SPengcheng.Chen@Sun.COM 		tmp_blk = msgpullup(mblk, -1);
905*8503SPengcheng.Chen@Sun.COM 		if (tmp_blk == NULL) {
906*8503SPengcheng.Chen@Sun.COM 			ZYD_WARN("failed to concatenate fragments\n");
907*8503SPengcheng.Chen@Sun.COM 			goto error;
908*8503SPengcheng.Chen@Sun.COM 		}
909*8503SPengcheng.Chen@Sun.COM 		data = tmp_blk->b_rptr;
910*8503SPengcheng.Chen@Sun.COM 		len = MBLKL(tmp_blk);
911*8503SPengcheng.Chen@Sun.COM 	} else {
912*8503SPengcheng.Chen@Sun.COM 		/* Not fragmented, use directly */
913*8503SPengcheng.Chen@Sun.COM 		tmp_blk = NULL;
914*8503SPengcheng.Chen@Sun.COM 		data = mblk->b_rptr;
915*8503SPengcheng.Chen@Sun.COM 		len = MBLKL(mblk);
916*8503SPengcheng.Chen@Sun.COM 	}
917*8503SPengcheng.Chen@Sun.COM 
918*8503SPengcheng.Chen@Sun.COM 	if (len < 2) {
919*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("received usb transfer too short\n");
920*8503SPengcheng.Chen@Sun.COM 		goto error;
921*8503SPengcheng.Chen@Sun.COM 	}
922*8503SPengcheng.Chen@Sun.COM 
923*8503SPengcheng.Chen@Sun.COM 	/*
924*8503SPengcheng.Chen@Sun.COM 	 * If this is a composite packet, the last two bytes contain
925*8503SPengcheng.Chen@Sun.COM 	 * two special signature bytes.
926*8503SPengcheng.Chen@Sun.COM 	 */
927*8503SPengcheng.Chen@Sun.COM 	desc = (struct zyd_rx_desc *)(data + len) - 1;
928*8503SPengcheng.Chen@Sun.COM 	/* multi-frame transfer */
929*8503SPengcheng.Chen@Sun.COM 	if (LE_16(desc->tag) == ZYD_TAG_MULTIFRAME) {
930*8503SPengcheng.Chen@Sun.COM 		const uint8_t *p = data, *end = data + len;
931*8503SPengcheng.Chen@Sun.COM 		int i;
932*8503SPengcheng.Chen@Sun.COM 
933*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_RX, "composite packet\n"));
934*8503SPengcheng.Chen@Sun.COM 
935*8503SPengcheng.Chen@Sun.COM 		for (i = 0; i < ZYD_MAX_RXFRAMECNT; i++) {
936*8503SPengcheng.Chen@Sun.COM 			const uint16_t len16 = LE_16(desc->len[i]);
937*8503SPengcheng.Chen@Sun.COM 			if (len16 == 0 || p + len16 > end)
938*8503SPengcheng.Chen@Sun.COM 				break;
939*8503SPengcheng.Chen@Sun.COM 			zyd_receive(ZYD_USB_TO_SOFTC(uc), p, len16);
940*8503SPengcheng.Chen@Sun.COM 			/* next frame is aligned on a 32-bit boundary */
941*8503SPengcheng.Chen@Sun.COM 			p += (len16 + 3) & ~3;
942*8503SPengcheng.Chen@Sun.COM 		}
943*8503SPengcheng.Chen@Sun.COM 	} else {
944*8503SPengcheng.Chen@Sun.COM 		/* single-frame transfer */
945*8503SPengcheng.Chen@Sun.COM 		zyd_receive(ZYD_USB_TO_SOFTC(uc), data, MBLKL(mblk));
946*8503SPengcheng.Chen@Sun.COM 	}
947*8503SPengcheng.Chen@Sun.COM 
948*8503SPengcheng.Chen@Sun.COM error:
949*8503SPengcheng.Chen@Sun.COM 	if (tmp_blk != NULL)
950*8503SPengcheng.Chen@Sun.COM 		freemsg(tmp_blk);
951*8503SPengcheng.Chen@Sun.COM 
952*8503SPengcheng.Chen@Sun.COM 	usb_free_bulk_req(req);
953*8503SPengcheng.Chen@Sun.COM 
954*8503SPengcheng.Chen@Sun.COM 	if (!sc->running)
955*8503SPengcheng.Chen@Sun.COM 		return;
956*8503SPengcheng.Chen@Sun.COM 
957*8503SPengcheng.Chen@Sun.COM 	if (zyd_usb_data_in_start_request(uc) != ZYD_SUCCESS) {
958*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("error restarting data_in transfer\n");
959*8503SPengcheng.Chen@Sun.COM 	}
960*8503SPengcheng.Chen@Sun.COM }
961*8503SPengcheng.Chen@Sun.COM 
962*8503SPengcheng.Chen@Sun.COM /*
963*8503SPengcheng.Chen@Sun.COM  * Called when an exception occurs on the data_in pipe.
964*8503SPengcheng.Chen@Sun.COM  */
965*8503SPengcheng.Chen@Sun.COM /*ARGSUSED*/
966*8503SPengcheng.Chen@Sun.COM static void
zyd_data_in_exc_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)967*8503SPengcheng.Chen@Sun.COM zyd_data_in_exc_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
968*8503SPengcheng.Chen@Sun.COM {
969*8503SPengcheng.Chen@Sun.COM 	struct zyd_usb *uc;
970*8503SPengcheng.Chen@Sun.COM 
971*8503SPengcheng.Chen@Sun.COM 	ZYD_DEBUG((ZYD_DBG_USB, "data IN exception\n"));
972*8503SPengcheng.Chen@Sun.COM 
973*8503SPengcheng.Chen@Sun.COM 	uc = (struct zyd_usb *)req->bulk_client_private;
974*8503SPengcheng.Chen@Sun.COM 	ASSERT(uc != NULL);
975*8503SPengcheng.Chen@Sun.COM 
976*8503SPengcheng.Chen@Sun.COM 	usb_free_bulk_req(req);
977*8503SPengcheng.Chen@Sun.COM }
978*8503SPengcheng.Chen@Sun.COM 
979*8503SPengcheng.Chen@Sun.COM /*
980*8503SPengcheng.Chen@Sun.COM  * Start a receive request on the data_in pipe.
981*8503SPengcheng.Chen@Sun.COM  */
982*8503SPengcheng.Chen@Sun.COM static zyd_res
zyd_usb_data_in_start_request(struct zyd_usb * uc)983*8503SPengcheng.Chen@Sun.COM zyd_usb_data_in_start_request(struct zyd_usb *uc)
984*8503SPengcheng.Chen@Sun.COM {
985*8503SPengcheng.Chen@Sun.COM 	usb_bulk_req_t *req;
986*8503SPengcheng.Chen@Sun.COM 	int res;
987*8503SPengcheng.Chen@Sun.COM 
988*8503SPengcheng.Chen@Sun.COM 	req = usb_alloc_bulk_req(uc->dip, ZYD_RX_BUF_SIZE, USB_FLAGS_SLEEP);
989*8503SPengcheng.Chen@Sun.COM 	if (req == NULL) {
990*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to allocate bulk IN request\n");
991*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
992*8503SPengcheng.Chen@Sun.COM 	}
993*8503SPengcheng.Chen@Sun.COM 
994*8503SPengcheng.Chen@Sun.COM 	req->bulk_len = (uint_t)ZYD_RX_BUF_SIZE;
995*8503SPengcheng.Chen@Sun.COM 	req->bulk_timeout = 0;
996*8503SPengcheng.Chen@Sun.COM 	req->bulk_client_private = (usb_opaque_t)uc;
997*8503SPengcheng.Chen@Sun.COM 	req->bulk_attributes =
998*8503SPengcheng.Chen@Sun.COM 	    USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING;
999*8503SPengcheng.Chen@Sun.COM 	req->bulk_cb = zyd_data_in_cb;
1000*8503SPengcheng.Chen@Sun.COM 	req->bulk_exc_cb = zyd_data_in_exc_cb;
1001*8503SPengcheng.Chen@Sun.COM 
1002*8503SPengcheng.Chen@Sun.COM 	res = usb_pipe_bulk_xfer(uc->pipe_data_in, req, USB_FLAGS_NOSLEEP);
1003*8503SPengcheng.Chen@Sun.COM 	if (res != USB_SUCCESS) {
1004*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("error starting receive request on data_in pipe\n");
1005*8503SPengcheng.Chen@Sun.COM 		usb_free_bulk_req(req);
1006*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
1007*8503SPengcheng.Chen@Sun.COM 	}
1008*8503SPengcheng.Chen@Sun.COM 
1009*8503SPengcheng.Chen@Sun.COM 	return (ZYD_SUCCESS);
1010*8503SPengcheng.Chen@Sun.COM }
1011*8503SPengcheng.Chen@Sun.COM 
1012*8503SPengcheng.Chen@Sun.COM 
1013*8503SPengcheng.Chen@Sun.COM /*
1014*8503SPengcheng.Chen@Sun.COM  * Start receiving packets on the data_in pipe.
1015*8503SPengcheng.Chen@Sun.COM  */
1016*8503SPengcheng.Chen@Sun.COM zyd_res
zyd_usb_data_in_enable(struct zyd_usb * uc)1017*8503SPengcheng.Chen@Sun.COM zyd_usb_data_in_enable(struct zyd_usb *uc)
1018*8503SPengcheng.Chen@Sun.COM {
1019*8503SPengcheng.Chen@Sun.COM 	for (int i = 0; i < ZYD_RX_LIST_COUNT; i++) {
1020*8503SPengcheng.Chen@Sun.COM 		if (zyd_usb_data_in_start_request(uc) != ZYD_SUCCESS) {
1021*8503SPengcheng.Chen@Sun.COM 			ZYD_WARN("failed to start data IN requests\n");
1022*8503SPengcheng.Chen@Sun.COM 			return (ZYD_FAILURE);
1023*8503SPengcheng.Chen@Sun.COM 		}
1024*8503SPengcheng.Chen@Sun.COM 	}
1025*8503SPengcheng.Chen@Sun.COM 	return (ZYD_SUCCESS);
1026*8503SPengcheng.Chen@Sun.COM }
1027*8503SPengcheng.Chen@Sun.COM 
1028*8503SPengcheng.Chen@Sun.COM /*
1029*8503SPengcheng.Chen@Sun.COM  * Stop receiving packets on the data_in pipe.
1030*8503SPengcheng.Chen@Sun.COM  */
1031*8503SPengcheng.Chen@Sun.COM void
zyd_usb_data_in_disable(struct zyd_usb * uc)1032*8503SPengcheng.Chen@Sun.COM zyd_usb_data_in_disable(struct zyd_usb *uc)
1033*8503SPengcheng.Chen@Sun.COM {
1034*8503SPengcheng.Chen@Sun.COM 	usb_pipe_reset(uc->dip, uc->pipe_data_in, USB_FLAGS_SLEEP,
1035*8503SPengcheng.Chen@Sun.COM 	    NULL, NULL);
1036*8503SPengcheng.Chen@Sun.COM }
1037*8503SPengcheng.Chen@Sun.COM 
1038*8503SPengcheng.Chen@Sun.COM /*
1039*8503SPengcheng.Chen@Sun.COM  * Send a packet to data_out.
1040*8503SPengcheng.Chen@Sun.COM  *
1041*8503SPengcheng.Chen@Sun.COM  * A packet consists of a zyd_tx_header + the IEEE802.11 frame.
1042*8503SPengcheng.Chen@Sun.COM  */
1043*8503SPengcheng.Chen@Sun.COM zyd_res
zyd_usb_send_packet(struct zyd_usb * uc,mblk_t * mp)1044*8503SPengcheng.Chen@Sun.COM zyd_usb_send_packet(struct zyd_usb *uc, mblk_t *mp)
1045*8503SPengcheng.Chen@Sun.COM {
1046*8503SPengcheng.Chen@Sun.COM 	usb_bulk_req_t *send_req;
1047*8503SPengcheng.Chen@Sun.COM 	int res;
1048*8503SPengcheng.Chen@Sun.COM 
1049*8503SPengcheng.Chen@Sun.COM 	send_req = usb_alloc_bulk_req(uc->dip, 0, USB_FLAGS_SLEEP);
1050*8503SPengcheng.Chen@Sun.COM 	if (send_req == NULL) {
1051*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to allocate bulk request\n");
1052*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
1053*8503SPengcheng.Chen@Sun.COM 	}
1054*8503SPengcheng.Chen@Sun.COM 
1055*8503SPengcheng.Chen@Sun.COM 	send_req->bulk_len = msgdsize(mp);
1056*8503SPengcheng.Chen@Sun.COM 	send_req->bulk_data = mp;
1057*8503SPengcheng.Chen@Sun.COM 	send_req->bulk_timeout = 5;
1058*8503SPengcheng.Chen@Sun.COM 	send_req->bulk_attributes = USB_ATTRS_AUTOCLEARING;
1059*8503SPengcheng.Chen@Sun.COM 	send_req->bulk_client_private = (usb_opaque_t)ZYD_USB_TO_SOFTC(uc);
1060*8503SPengcheng.Chen@Sun.COM 	send_req->bulk_cb = zyd_data_out_cb;
1061*8503SPengcheng.Chen@Sun.COM 	send_req->bulk_exc_cb = zyd_data_out_cb;
1062*8503SPengcheng.Chen@Sun.COM 	send_req->bulk_completion_reason = 0;
1063*8503SPengcheng.Chen@Sun.COM 	send_req->bulk_cb_flags = 0;
1064*8503SPengcheng.Chen@Sun.COM 
1065*8503SPengcheng.Chen@Sun.COM 	res = usb_pipe_bulk_xfer(uc->pipe_data_out, send_req, 0);
1066*8503SPengcheng.Chen@Sun.COM 	if (res != USB_SUCCESS) {
1067*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_USB,
1068*8503SPengcheng.Chen@Sun.COM 		    "failed writing to bulk/out pipe (%d)\n", res));
1069*8503SPengcheng.Chen@Sun.COM 		usb_free_bulk_req(send_req);
1070*8503SPengcheng.Chen@Sun.COM 		return (USB_FAILURE);
1071*8503SPengcheng.Chen@Sun.COM 	}
1072*8503SPengcheng.Chen@Sun.COM 
1073*8503SPengcheng.Chen@Sun.COM 	return (USB_SUCCESS);
1074*8503SPengcheng.Chen@Sun.COM }
1075*8503SPengcheng.Chen@Sun.COM 
1076*8503SPengcheng.Chen@Sun.COM /*
1077*8503SPengcheng.Chen@Sun.COM  * Initialize USB device communication and USB module state.
1078*8503SPengcheng.Chen@Sun.COM  *
1079*8503SPengcheng.Chen@Sun.COM  *      uc      pointer to usb module state
1080*8503SPengcheng.Chen@Sun.COM  *      dip     pointer to device info structure
1081*8503SPengcheng.Chen@Sun.COM  */
1082*8503SPengcheng.Chen@Sun.COM zyd_res
zyd_usb_init(struct zyd_softc * sc)1083*8503SPengcheng.Chen@Sun.COM zyd_usb_init(struct zyd_softc *sc)
1084*8503SPengcheng.Chen@Sun.COM {
1085*8503SPengcheng.Chen@Sun.COM 	struct zyd_usb *uc = &sc->usb;
1086*8503SPengcheng.Chen@Sun.COM 	dev_info_t *dip = sc->dip;
1087*8503SPengcheng.Chen@Sun.COM 	int ures;
1088*8503SPengcheng.Chen@Sun.COM 
1089*8503SPengcheng.Chen@Sun.COM 	uc->dip = dip;
1090*8503SPengcheng.Chen@Sun.COM 
1091*8503SPengcheng.Chen@Sun.COM 	ures = usb_client_attach(uc->dip, USBDRV_VERSION, 0);
1092*8503SPengcheng.Chen@Sun.COM 	if (ures != USB_SUCCESS) {
1093*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("usb_client_attach failed, error code: %d\n", ures);
1094*8503SPengcheng.Chen@Sun.COM 		return (ZYD_FAILURE);
1095*8503SPengcheng.Chen@Sun.COM 	}
1096*8503SPengcheng.Chen@Sun.COM 
1097*8503SPengcheng.Chen@Sun.COM 	/*
1098*8503SPengcheng.Chen@Sun.COM 	 * LVL_ALL is needed for later endpoint scanning,
1099*8503SPengcheng.Chen@Sun.COM 	 * and the tree must not be freed before that.
1100*8503SPengcheng.Chen@Sun.COM 	 */
1101*8503SPengcheng.Chen@Sun.COM 	ures = usb_get_dev_data(uc->dip, &uc->cdata, USB_PARSE_LVL_ALL, 0);
1102*8503SPengcheng.Chen@Sun.COM 	if (ures != USB_SUCCESS) {
1103*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("usb_get_dev_data failed, error code: %d\n", ures);
1104*8503SPengcheng.Chen@Sun.COM 		ASSERT(uc->cdata == NULL);
1105*8503SPengcheng.Chen@Sun.COM 		goto fail;
1106*8503SPengcheng.Chen@Sun.COM 	}
1107*8503SPengcheng.Chen@Sun.COM 
1108*8503SPengcheng.Chen@Sun.COM 	ures = usb_reset_device(uc->dip, USB_RESET_LVL_DEFAULT);
1109*8503SPengcheng.Chen@Sun.COM 	if (ures != USB_SUCCESS) {
1110*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("usb_reset_device failed, error code: %d\n", ures);
1111*8503SPengcheng.Chen@Sun.COM 		goto fail;
1112*8503SPengcheng.Chen@Sun.COM 	}
1113*8503SPengcheng.Chen@Sun.COM 
1114*8503SPengcheng.Chen@Sun.COM 	uc->connected = B_TRUE;
1115*8503SPengcheng.Chen@Sun.COM 
1116*8503SPengcheng.Chen@Sun.COM 	ures = usb_register_hotplug_cbs(dip, zyd_usb_disconnect,
1117*8503SPengcheng.Chen@Sun.COM 	    zyd_usb_reconnect);
1118*8503SPengcheng.Chen@Sun.COM 	if (ures != USB_SUCCESS) {
1119*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("usb_register_hotplug_cbs failed, error code: %d\n",
1120*8503SPengcheng.Chen@Sun.COM 		    ures);
1121*8503SPengcheng.Chen@Sun.COM 		goto fail;
1122*8503SPengcheng.Chen@Sun.COM 	}
1123*8503SPengcheng.Chen@Sun.COM 
1124*8503SPengcheng.Chen@Sun.COM 	return (ZYD_SUCCESS);
1125*8503SPengcheng.Chen@Sun.COM fail:
1126*8503SPengcheng.Chen@Sun.COM 	usb_client_detach(uc->dip, uc->cdata);
1127*8503SPengcheng.Chen@Sun.COM 	uc->cdata = NULL;
1128*8503SPengcheng.Chen@Sun.COM 	return (ZYD_FAILURE);
1129*8503SPengcheng.Chen@Sun.COM }
1130*8503SPengcheng.Chen@Sun.COM 
1131*8503SPengcheng.Chen@Sun.COM /*
1132*8503SPengcheng.Chen@Sun.COM  * Deinitialize USB device communication.
1133*8503SPengcheng.Chen@Sun.COM  */
1134*8503SPengcheng.Chen@Sun.COM void
zyd_usb_deinit(struct zyd_softc * sc)1135*8503SPengcheng.Chen@Sun.COM zyd_usb_deinit(struct zyd_softc *sc)
1136*8503SPengcheng.Chen@Sun.COM {
1137*8503SPengcheng.Chen@Sun.COM 	struct zyd_usb *uc = &sc->usb;
1138*8503SPengcheng.Chen@Sun.COM 
1139*8503SPengcheng.Chen@Sun.COM 	usb_unregister_hotplug_cbs(sc->dip);
1140*8503SPengcheng.Chen@Sun.COM 
1141*8503SPengcheng.Chen@Sun.COM 	usb_client_detach(uc->dip, uc->cdata);
1142*8503SPengcheng.Chen@Sun.COM 	uc->cdata = NULL;
1143*8503SPengcheng.Chen@Sun.COM 	uc->connected = B_FALSE;
1144*8503SPengcheng.Chen@Sun.COM }
1145*8503SPengcheng.Chen@Sun.COM 
1146*8503SPengcheng.Chen@Sun.COM /*
1147*8503SPengcheng.Chen@Sun.COM  * Device connected
1148*8503SPengcheng.Chen@Sun.COM  */
1149*8503SPengcheng.Chen@Sun.COM static int
zyd_usb_reconnect(dev_info_t * dip)1150*8503SPengcheng.Chen@Sun.COM zyd_usb_reconnect(dev_info_t *dip)
1151*8503SPengcheng.Chen@Sun.COM {
1152*8503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc;
1153*8503SPengcheng.Chen@Sun.COM 	struct zyd_usb *uc;
1154*8503SPengcheng.Chen@Sun.COM 
1155*8503SPengcheng.Chen@Sun.COM 	sc = ddi_get_soft_state(zyd_ssp, ddi_get_instance(dip));
1156*8503SPengcheng.Chen@Sun.COM 	ASSERT(sc != NULL);
1157*8503SPengcheng.Chen@Sun.COM 	uc = &sc->usb;
1158*8503SPengcheng.Chen@Sun.COM 	ASSERT(!uc->connected);
1159*8503SPengcheng.Chen@Sun.COM 
1160*8503SPengcheng.Chen@Sun.COM 	if (sc->suspended)
1161*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_RESUME | ZYD_DBG_USB,
1162*8503SPengcheng.Chen@Sun.COM 		    "reconnect before resume\n"));
1163*8503SPengcheng.Chen@Sun.COM 
1164*8503SPengcheng.Chen@Sun.COM 	/* check device changes after disconnect */
1165*8503SPengcheng.Chen@Sun.COM 	if (usb_check_same_device(sc->dip, NULL, USB_LOG_L2, -1,
1166*8503SPengcheng.Chen@Sun.COM 	    USB_CHK_BASIC | USB_CHK_CFG, NULL) != USB_SUCCESS) {
1167*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_USB, "different device connected\n"));
1168*8503SPengcheng.Chen@Sun.COM 		return (DDI_FAILURE);
1169*8503SPengcheng.Chen@Sun.COM 	}
1170*8503SPengcheng.Chen@Sun.COM 
1171*8503SPengcheng.Chen@Sun.COM 	(void) zyd_serial_enter(sc, ZYD_NO_SIG);
1172*8503SPengcheng.Chen@Sun.COM 	if (zyd_hw_init(sc) != ZYD_SUCCESS) {
1173*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to reinit hardware\n");
1174*8503SPengcheng.Chen@Sun.COM 		zyd_serial_exit(sc);
1175*8503SPengcheng.Chen@Sun.COM 		return (DDI_FAILURE);
1176*8503SPengcheng.Chen@Sun.COM 	}
1177*8503SPengcheng.Chen@Sun.COM 	if (sc->running) {
1178*8503SPengcheng.Chen@Sun.COM 		if (zyd_hw_start(sc) != ZYD_SUCCESS) {
1179*8503SPengcheng.Chen@Sun.COM 			ZYD_WARN("failed to restart hardware\n");
1180*8503SPengcheng.Chen@Sun.COM 			zyd_serial_exit(sc);
1181*8503SPengcheng.Chen@Sun.COM 			goto fail;
1182*8503SPengcheng.Chen@Sun.COM 		}
1183*8503SPengcheng.Chen@Sun.COM 	}
1184*8503SPengcheng.Chen@Sun.COM 	zyd_serial_exit(sc);
1185*8503SPengcheng.Chen@Sun.COM 
1186*8503SPengcheng.Chen@Sun.COM 	uc->connected = B_TRUE;
1187*8503SPengcheng.Chen@Sun.COM 
1188*8503SPengcheng.Chen@Sun.COM 	return (DDI_SUCCESS);
1189*8503SPengcheng.Chen@Sun.COM fail:
1190*8503SPengcheng.Chen@Sun.COM 	usb_client_detach(uc->dip, uc->cdata);
1191*8503SPengcheng.Chen@Sun.COM 	uc->cdata = NULL;
1192*8503SPengcheng.Chen@Sun.COM 	return (DDI_FAILURE);
1193*8503SPengcheng.Chen@Sun.COM }
1194*8503SPengcheng.Chen@Sun.COM 
1195*8503SPengcheng.Chen@Sun.COM static int
zyd_usb_disconnect(dev_info_t * dip)1196*8503SPengcheng.Chen@Sun.COM zyd_usb_disconnect(dev_info_t *dip)
1197*8503SPengcheng.Chen@Sun.COM {
1198*8503SPengcheng.Chen@Sun.COM 	struct zyd_softc *sc;
1199*8503SPengcheng.Chen@Sun.COM 	struct zyd_usb *uc;
1200*8503SPengcheng.Chen@Sun.COM 
1201*8503SPengcheng.Chen@Sun.COM 	sc = ddi_get_soft_state(zyd_ssp, ddi_get_instance(dip));
1202*8503SPengcheng.Chen@Sun.COM 	ASSERT(sc != NULL);
1203*8503SPengcheng.Chen@Sun.COM 	uc = &sc->usb;
1204*8503SPengcheng.Chen@Sun.COM 
1205*8503SPengcheng.Chen@Sun.COM 	if (!uc->connected) {
1206*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_USB, "different device disconnected\n"));
1207*8503SPengcheng.Chen@Sun.COM 		return (DDI_FAILURE);
1208*8503SPengcheng.Chen@Sun.COM 	}
1209*8503SPengcheng.Chen@Sun.COM 	uc->connected = B_FALSE;
1210*8503SPengcheng.Chen@Sun.COM 
1211*8503SPengcheng.Chen@Sun.COM 	if (sc->suspended) {
1212*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_USB, "disconnect after suspend\n"));
1213*8503SPengcheng.Chen@Sun.COM 		return (DDI_SUCCESS);
1214*8503SPengcheng.Chen@Sun.COM 	}
1215*8503SPengcheng.Chen@Sun.COM 	ieee80211_new_state(&sc->ic, IEEE80211_S_INIT, -1);
1216*8503SPengcheng.Chen@Sun.COM 
1217*8503SPengcheng.Chen@Sun.COM 	(void) zyd_serial_enter(sc, ZYD_NO_SIG);
1218*8503SPengcheng.Chen@Sun.COM 	zyd_hw_stop(sc);
1219*8503SPengcheng.Chen@Sun.COM 	zyd_hw_deinit(sc);
1220*8503SPengcheng.Chen@Sun.COM 	zyd_serial_exit(sc);
1221*8503SPengcheng.Chen@Sun.COM 
1222*8503SPengcheng.Chen@Sun.COM 	return (DDI_SUCCESS);
1223*8503SPengcheng.Chen@Sun.COM }
1224*8503SPengcheng.Chen@Sun.COM 
1225*8503SPengcheng.Chen@Sun.COM int
zyd_suspend(struct zyd_softc * sc)1226*8503SPengcheng.Chen@Sun.COM zyd_suspend(struct zyd_softc *sc)
1227*8503SPengcheng.Chen@Sun.COM {
1228*8503SPengcheng.Chen@Sun.COM 	struct zyd_usb *uc = &sc->usb;
1229*8503SPengcheng.Chen@Sun.COM 
1230*8503SPengcheng.Chen@Sun.COM 	if (!uc->connected) {
1231*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_USB | ZYD_DBG_RESUME,
1232*8503SPengcheng.Chen@Sun.COM 		    "suspend after disconnect\n"));
1233*8503SPengcheng.Chen@Sun.COM 		sc->suspended = B_TRUE;
1234*8503SPengcheng.Chen@Sun.COM 		return (DDI_SUCCESS);
1235*8503SPengcheng.Chen@Sun.COM 	}
1236*8503SPengcheng.Chen@Sun.COM 	ZYD_DEBUG((ZYD_DBG_RESUME, "suspend\n"));
1237*8503SPengcheng.Chen@Sun.COM 
1238*8503SPengcheng.Chen@Sun.COM 	sc->suspended = B_TRUE;
1239*8503SPengcheng.Chen@Sun.COM 	ieee80211_new_state(&sc->ic, IEEE80211_S_INIT, -1);
1240*8503SPengcheng.Chen@Sun.COM 
1241*8503SPengcheng.Chen@Sun.COM 	(void) zyd_serial_enter(sc, ZYD_NO_SIG);
1242*8503SPengcheng.Chen@Sun.COM 	zyd_hw_stop(sc);
1243*8503SPengcheng.Chen@Sun.COM 	zyd_hw_deinit(sc);
1244*8503SPengcheng.Chen@Sun.COM 	zyd_serial_exit(sc);
1245*8503SPengcheng.Chen@Sun.COM 
1246*8503SPengcheng.Chen@Sun.COM 	ZYD_DEBUG((ZYD_DBG_RESUME, "suspend complete\n"));
1247*8503SPengcheng.Chen@Sun.COM 	return (DDI_SUCCESS);
1248*8503SPengcheng.Chen@Sun.COM }
1249*8503SPengcheng.Chen@Sun.COM 
1250*8503SPengcheng.Chen@Sun.COM int
zyd_resume(struct zyd_softc * sc)1251*8503SPengcheng.Chen@Sun.COM zyd_resume(struct zyd_softc *sc)
1252*8503SPengcheng.Chen@Sun.COM {
1253*8503SPengcheng.Chen@Sun.COM 	struct zyd_usb *uc = &sc->usb;
1254*8503SPengcheng.Chen@Sun.COM 
1255*8503SPengcheng.Chen@Sun.COM 	if (!uc->connected) {
1256*8503SPengcheng.Chen@Sun.COM 		ZYD_DEBUG((ZYD_DBG_USB | ZYD_DBG_RESUME,
1257*8503SPengcheng.Chen@Sun.COM 		    "resume after disconnect\n"));
1258*8503SPengcheng.Chen@Sun.COM 		sc->suspended = B_FALSE;
1259*8503SPengcheng.Chen@Sun.COM 		return (DDI_SUCCESS);
1260*8503SPengcheng.Chen@Sun.COM 	}
1261*8503SPengcheng.Chen@Sun.COM 	ZYD_DEBUG((ZYD_DBG_RESUME, "resume\n"));
1262*8503SPengcheng.Chen@Sun.COM 
1263*8503SPengcheng.Chen@Sun.COM 	/* check device changes after disconnect */
1264*8503SPengcheng.Chen@Sun.COM 	if (usb_check_same_device(sc->dip, NULL, USB_LOG_L2, -1,
1265*8503SPengcheng.Chen@Sun.COM 	    USB_CHK_BASIC | USB_CHK_CFG, NULL) != USB_SUCCESS) {
1266*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("different device connected to same port\n");
1267*8503SPengcheng.Chen@Sun.COM 		sc->suspended = B_FALSE;
1268*8503SPengcheng.Chen@Sun.COM 		uc->connected = B_FALSE;
1269*8503SPengcheng.Chen@Sun.COM 		return (DDI_SUCCESS);
1270*8503SPengcheng.Chen@Sun.COM 	}
1271*8503SPengcheng.Chen@Sun.COM 
1272*8503SPengcheng.Chen@Sun.COM 	(void) zyd_serial_enter(sc, ZYD_NO_SIG);
1273*8503SPengcheng.Chen@Sun.COM 	if (zyd_hw_init(sc) != ZYD_SUCCESS) {
1274*8503SPengcheng.Chen@Sun.COM 		ZYD_WARN("failed to reinit hardware\n");
1275*8503SPengcheng.Chen@Sun.COM 		zyd_serial_exit(sc);
1276*8503SPengcheng.Chen@Sun.COM 		return (DDI_FAILURE);
1277*8503SPengcheng.Chen@Sun.COM 	}
1278*8503SPengcheng.Chen@Sun.COM 	if (sc->running) {
1279*8503SPengcheng.Chen@Sun.COM 		if (zyd_hw_start(sc) != ZYD_SUCCESS) {
1280*8503SPengcheng.Chen@Sun.COM 			ZYD_WARN("failed to restart hardware\n");
1281*8503SPengcheng.Chen@Sun.COM 			zyd_serial_exit(sc);
1282*8503SPengcheng.Chen@Sun.COM 			return (DDI_FAILURE);
1283*8503SPengcheng.Chen@Sun.COM 		}
1284*8503SPengcheng.Chen@Sun.COM 	}
1285*8503SPengcheng.Chen@Sun.COM 	zyd_serial_exit(sc);
1286*8503SPengcheng.Chen@Sun.COM 
1287*8503SPengcheng.Chen@Sun.COM 	sc->suspended = B_FALSE;
1288*8503SPengcheng.Chen@Sun.COM 
1289*8503SPengcheng.Chen@Sun.COM 	ZYD_DEBUG((ZYD_DBG_RESUME, "resume complete\n"));
1290*8503SPengcheng.Chen@Sun.COM 	return (DDI_SUCCESS);
1291*8503SPengcheng.Chen@Sun.COM }
1292