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