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