usb_msctest.c revision 194677
1254721Semaste/* $FreeBSD: head/sys/dev/usb/usb_msctest.c 194677 2009-06-23 02:19:59Z thompsa $ */
2254721Semaste/*-
3254721Semaste * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4254721Semaste *
5254721Semaste * Redistribution and use in source and binary forms, with or without
6254721Semaste * modification, are permitted provided that the following conditions
7254721Semaste * are met:
8254721Semaste * 1. Redistributions of source code must retain the above copyright
9254721Semaste *    notice, this list of conditions and the following disclaimer.
10254721Semaste * 2. Redistributions in binary form must reproduce the above copyright
11254721Semaste *    notice, this list of conditions and the following disclaimer in the
12254721Semaste *    documentation and/or other materials provided with the distribution.
13254721Semaste *
14254721Semaste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15254721Semaste * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16254721Semaste * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17254721Semaste * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18254721Semaste * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19254721Semaste * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20254721Semaste * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21254721Semaste * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22254721Semaste * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23254721Semaste * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24254721Semaste * SUCH DAMAGE.
25254721Semaste */
26254721Semaste
27254721Semaste/*
28254721Semaste * The following file contains code that will detect USB autoinstall
29254721Semaste * disks.
30254721Semaste *
31254721Semaste * TODO: Potentially we could add code to automatically detect USB
32254721Semaste * mass storage quirks for not supported SCSI commands!
33254721Semaste */
34254721Semaste
35254721Semaste#include <sys/stdint.h>
36254721Semaste#include <sys/stddef.h>
37254721Semaste#include <sys/param.h>
38254721Semaste#include <sys/queue.h>
39254721Semaste#include <sys/types.h>
40254721Semaste#include <sys/systm.h>
41254721Semaste#include <sys/kernel.h>
42254721Semaste#include <sys/bus.h>
43254721Semaste#include <sys/linker_set.h>
44254721Semaste#include <sys/module.h>
45254721Semaste#include <sys/lock.h>
46254721Semaste#include <sys/mutex.h>
47254721Semaste#include <sys/condvar.h>
48254721Semaste#include <sys/sysctl.h>
49254721Semaste#include <sys/sx.h>
50254721Semaste#include <sys/unistd.h>
51254721Semaste#include <sys/callout.h>
52254721Semaste#include <sys/malloc.h>
53254721Semaste#include <sys/priv.h>
54254721Semaste
55254721Semaste#include <dev/usb/usb.h>
56254721Semaste#include <dev/usb/usbdi.h>
57254721Semaste#include <dev/usb/usbdi_util.h>
58254721Semaste
59254721Semaste#define	USB_DEBUG_VAR usb_debug
60254721Semaste
61254721Semaste#include <dev/usb/usb_busdma.h>
62254721Semaste#include <dev/usb/usb_process.h>
63254721Semaste#include <dev/usb/usb_transfer.h>
64254721Semaste#include <dev/usb/usb_msctest.h>
65254721Semaste#include <dev/usb/usb_debug.h>
66254721Semaste#include <dev/usb/usb_busdma.h>
67254721Semaste#include <dev/usb/usb_device.h>
68254721Semaste#include <dev/usb/usb_request.h>
69254721Semaste#include <dev/usb/usb_util.h>
70254721Semaste
71254721Semaste#include <dev/usb/usb.h>
72254721Semaste
73254721Semasteenum {
74254721Semaste	ST_COMMAND,
75254721Semaste	ST_DATA_RD,
76254721Semaste	ST_DATA_RD_CS,
77254721Semaste	ST_DATA_WR,
78254721Semaste	ST_DATA_WR_CS,
79254721Semaste	ST_STATUS,
80254721Semaste	ST_MAX,
81254721Semaste};
82254721Semaste
83254721Semasteenum {
84254721Semaste	DIR_IN,
85254721Semaste	DIR_OUT,
86254721Semaste	DIR_NONE,
87254721Semaste};
88254721Semaste
89254721Semaste#define	BULK_SIZE		64	/* dummy */
90254721Semaste
91254721Semaste/* Command Block Wrapper */
92254721Semastestruct bbb_cbw {
93254721Semaste	uDWord	dCBWSignature;
94254721Semaste#define	CBWSIGNATURE	0x43425355
95254721Semaste	uDWord	dCBWTag;
96254721Semaste	uDWord	dCBWDataTransferLength;
97254721Semaste	uByte	bCBWFlags;
98254721Semaste#define	CBWFLAGS_OUT	0x00
99254721Semaste#define	CBWFLAGS_IN	0x80
100254721Semaste	uByte	bCBWLUN;
101254721Semaste	uByte	bCDBLength;
102254721Semaste#define	CBWCDBLENGTH	16
103254721Semaste	uByte	CBWCDB[CBWCDBLENGTH];
104254721Semaste} __packed;
105254721Semaste
106254721Semaste/* Command Status Wrapper */
107254721Semastestruct bbb_csw {
108254721Semaste	uDWord	dCSWSignature;
109254721Semaste#define	CSWSIGNATURE	0x53425355
110254721Semaste	uDWord	dCSWTag;
111254721Semaste	uDWord	dCSWDataResidue;
112254721Semaste	uByte	bCSWStatus;
113254721Semaste#define	CSWSTATUS_GOOD	0x0
114254721Semaste#define	CSWSTATUS_FAILED	0x1
115254721Semaste#define	CSWSTATUS_PHASE	0x2
116254721Semaste} __packed;
117254721Semaste
118254721Semastestruct bbb_transfer {
119254721Semaste	struct mtx mtx;
120254721Semaste	struct cv cv;
121254721Semaste	struct bbb_cbw cbw;
122254721Semaste	struct bbb_csw csw;
123254721Semaste
124254721Semaste	struct usb_xfer *xfer[ST_MAX];
125254721Semaste
126254721Semaste	uint8_t *data_ptr;
127254721Semaste
128254721Semaste	usb_size_t data_len;		/* bytes */
129254721Semaste	usb_size_t data_rem;		/* bytes */
130254721Semaste	usb_timeout_t data_timeout;	/* ms */
131254721Semaste	usb_frlength_t actlen;		/* bytes */
132254721Semaste
133254721Semaste	uint8_t	cmd_len;		/* bytes */
134254721Semaste	uint8_t	dir;
135254721Semaste	uint8_t	lun;
136254721Semaste	uint8_t	state;
137254721Semaste	uint8_t	error;
138254721Semaste	uint8_t	status_try;
139254721Semaste
140254721Semaste	uint8_t	buffer[256];
141254721Semaste};
142254721Semaste
143254721Semastestatic usb_callback_t bbb_command_callback;
144254721Semastestatic usb_callback_t bbb_data_read_callback;
145254721Semastestatic usb_callback_t bbb_data_rd_cs_callback;
146254721Semastestatic usb_callback_t bbb_data_write_callback;
147254721Semastestatic usb_callback_t bbb_data_wr_cs_callback;
148254721Semastestatic usb_callback_t bbb_status_callback;
149254721Semaste
150254721Semastestatic const struct usb_config bbb_config[ST_MAX] = {
151254721Semaste
152254721Semaste	[ST_COMMAND] = {
153254721Semaste		.type = UE_BULK,
154254721Semaste		.endpoint = UE_ADDR_ANY,
155254721Semaste		.direction = UE_DIR_OUT,
156254721Semaste		.bufsize = sizeof(struct bbb_cbw),
157254721Semaste		.callback = &bbb_command_callback,
158254721Semaste		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
159254721Semaste	},
160254721Semaste
161254721Semaste	[ST_DATA_RD] = {
162254721Semaste		.type = UE_BULK,
163254721Semaste		.endpoint = UE_ADDR_ANY,
164254721Semaste		.direction = UE_DIR_IN,
165263363Semaste		.bufsize = BULK_SIZE,
166254721Semaste		.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
167254721Semaste		.callback = &bbb_data_read_callback,
168254721Semaste		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
169254721Semaste	},
170254721Semaste
171254721Semaste	[ST_DATA_RD_CS] = {
172254721Semaste		.type = UE_CONTROL,
173254721Semaste		.endpoint = 0x00,	/* Control pipe */
174254721Semaste		.direction = UE_DIR_ANY,
175254721Semaste		.bufsize = sizeof(struct usb_device_request),
176254721Semaste		.callback = &bbb_data_rd_cs_callback,
177254721Semaste		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
178254721Semaste	},
179254721Semaste
180254721Semaste	[ST_DATA_WR] = {
181254721Semaste		.type = UE_BULK,
182254721Semaste		.endpoint = UE_ADDR_ANY,
183254721Semaste		.direction = UE_DIR_OUT,
184254721Semaste		.bufsize = BULK_SIZE,
185254721Semaste		.flags = {.proxy_buffer = 1,},
186263363Semaste		.callback = &bbb_data_write_callback,
187254721Semaste		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
188263363Semaste	},
189254721Semaste
190254721Semaste	[ST_DATA_WR_CS] = {
191254721Semaste		.type = UE_CONTROL,
192254721Semaste		.endpoint = 0x00,	/* Control pipe */
193254721Semaste		.direction = UE_DIR_ANY,
194254721Semaste		.bufsize = sizeof(struct usb_device_request),
195254721Semaste		.callback = &bbb_data_wr_cs_callback,
196254721Semaste		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
197254721Semaste	},
198254721Semaste
199254721Semaste	[ST_STATUS] = {
200254721Semaste		.type = UE_BULK,
201254721Semaste		.endpoint = UE_ADDR_ANY,
202254721Semaste		.direction = UE_DIR_IN,
203254721Semaste		.bufsize = sizeof(struct bbb_csw),
204254721Semaste		.flags = {.short_xfer_ok = 1,},
205254721Semaste		.callback = &bbb_status_callback,
206254721Semaste		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
207254721Semaste	},
208254721Semaste};
209254721Semaste
210254721Semastestatic void
211254721Semastebbb_done(struct bbb_transfer *sc, uint8_t error)
212254721Semaste{
213254721Semaste	struct usb_xfer *xfer;
214254721Semaste
215254721Semaste	xfer = sc->xfer[sc->state];
216254721Semaste
217254721Semaste	/* verify the error code */
218254721Semaste
219254721Semaste	if (error) {
220254721Semaste		switch (USB_GET_STATE(xfer)) {
221254721Semaste		case USB_ST_SETUP:
222254721Semaste		case USB_ST_TRANSFERRED:
223254721Semaste			error = 1;
224254721Semaste			break;
225254721Semaste		default:
226254721Semaste			error = 2;
227254721Semaste			break;
228254721Semaste		}
229254721Semaste	}
230254721Semaste	sc->error = error;
231254721Semaste	sc->state = ST_COMMAND;
232254721Semaste	sc->status_try = 1;
233254721Semaste	cv_signal(&sc->cv);
234254721Semaste}
235254721Semaste
236254721Semastestatic void
237254721Semastebbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index)
238254721Semaste{
239254721Semaste	sc->state = xfer_index;
240254721Semaste	usbd_transfer_start(sc->xfer[xfer_index]);
241254721Semaste}
242254721Semaste
243254721Semastestatic void
244254721Semastebbb_data_clear_stall_callback(struct usb_xfer *xfer,
245254721Semaste    uint8_t next_xfer, uint8_t stall_xfer)
246254721Semaste{
247254721Semaste	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
248254721Semaste
249254721Semaste	if (usbd_clear_stall_callback(xfer, sc->xfer[stall_xfer])) {
250254721Semaste		switch (USB_GET_STATE(xfer)) {
251254721Semaste		case USB_ST_SETUP:
252254721Semaste		case USB_ST_TRANSFERRED:
253254721Semaste			bbb_transfer_start(sc, next_xfer);
254254721Semaste			break;
255254721Semaste		default:
256254721Semaste			bbb_done(sc, 1);
257254721Semaste			break;
258254721Semaste		}
259254721Semaste	}
260254721Semaste}
261254721Semaste
262254721Semastestatic void
263254721Semastebbb_command_callback(struct usb_xfer *xfer, usb_error_t error)
264254721Semaste{
265254721Semaste	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
266254721Semaste	uint32_t tag;
267254721Semaste
268254721Semaste	switch (USB_GET_STATE(xfer)) {
269254721Semaste	case USB_ST_TRANSFERRED:
270254721Semaste		bbb_transfer_start
271254721Semaste		    (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD :
272254721Semaste		    (sc->dir == DIR_OUT) ? ST_DATA_WR :
273254721Semaste		    ST_STATUS));
274254721Semaste		break;
275
276	case USB_ST_SETUP:
277		sc->status_try = 0;
278		tag = UGETDW(sc->cbw.dCBWTag) + 1;
279		USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE);
280		USETDW(sc->cbw.dCBWTag, tag);
281		USETDW(sc->cbw.dCBWDataTransferLength, (uint32_t)sc->data_len);
282		sc->cbw.bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT);
283		sc->cbw.bCBWLUN = sc->lun;
284		sc->cbw.bCDBLength = sc->cmd_len;
285		if (sc->cbw.bCDBLength > sizeof(sc->cbw.CBWCDB)) {
286			sc->cbw.bCDBLength = sizeof(sc->cbw.CBWCDB);
287			DPRINTFN(0, "Truncating long command!\n");
288		}
289		usbd_xfer_set_frame_data(xfer, 0, &sc->cbw, sizeof(sc->cbw));
290		usbd_transfer_submit(xfer);
291		break;
292
293	default:			/* Error */
294		bbb_done(sc, 1);
295		break;
296	}
297}
298
299static void
300bbb_data_read_callback(struct usb_xfer *xfer, usb_error_t error)
301{
302	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
303	usb_frlength_t max_bulk = usbd_xfer_max_len(xfer);
304	int actlen, sumlen;
305
306	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
307
308	switch (USB_GET_STATE(xfer)) {
309	case USB_ST_TRANSFERRED:
310		sc->data_rem -= actlen;
311		sc->data_ptr += actlen;
312		sc->actlen += actlen;
313
314		if (actlen < sumlen) {
315			/* short transfer */
316			sc->data_rem = 0;
317		}
318	case USB_ST_SETUP:
319		DPRINTF("max_bulk=%d, data_rem=%d\n",
320		    max_bulk, sc->data_rem);
321
322		if (sc->data_rem == 0) {
323			bbb_transfer_start(sc, ST_STATUS);
324			break;
325		}
326		if (max_bulk > sc->data_rem) {
327			max_bulk = sc->data_rem;
328		}
329		usbd_xfer_set_timeout(xfer, sc->data_timeout);
330		usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk);
331		usbd_transfer_submit(xfer);
332		break;
333
334	default:			/* Error */
335		if (error == USB_ERR_CANCELLED) {
336			bbb_done(sc, 1);
337		} else {
338			bbb_transfer_start(sc, ST_DATA_RD_CS);
339		}
340		break;
341	}
342}
343
344static void
345bbb_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error)
346{
347	bbb_data_clear_stall_callback(xfer, ST_STATUS,
348	    ST_DATA_RD);
349}
350
351static void
352bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error)
353{
354	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
355	usb_frlength_t max_bulk = usbd_xfer_max_len(xfer);
356	int actlen, sumlen;
357
358	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
359
360	switch (USB_GET_STATE(xfer)) {
361	case USB_ST_TRANSFERRED:
362		sc->data_rem -= actlen;
363		sc->data_ptr += actlen;
364		sc->actlen += actlen;
365
366		if (actlen < sumlen) {
367			/* short transfer */
368			sc->data_rem = 0;
369		}
370	case USB_ST_SETUP:
371		DPRINTF("max_bulk=%d, data_rem=%d\n",
372		    max_bulk, sc->data_rem);
373
374		if (sc->data_rem == 0) {
375			bbb_transfer_start(sc, ST_STATUS);
376			return;
377		}
378		if (max_bulk > sc->data_rem) {
379			max_bulk = sc->data_rem;
380		}
381		usbd_xfer_set_timeout(xfer, sc->data_timeout);
382		usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk);
383		usbd_transfer_submit(xfer);
384		return;
385
386	default:			/* Error */
387		if (error == USB_ERR_CANCELLED) {
388			bbb_done(sc, 1);
389		} else {
390			bbb_transfer_start(sc, ST_DATA_WR_CS);
391		}
392		return;
393
394	}
395}
396
397static void
398bbb_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error)
399{
400	bbb_data_clear_stall_callback(xfer, ST_STATUS,
401	    ST_DATA_WR);
402}
403
404static void
405bbb_status_callback(struct usb_xfer *xfer, usb_error_t error)
406{
407	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
408	int actlen, sumlen;
409
410	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
411
412	switch (USB_GET_STATE(xfer)) {
413	case USB_ST_TRANSFERRED:
414
415		/* very simple status check */
416
417		if (actlen < sizeof(sc->csw)) {
418			bbb_done(sc, 1);/* error */
419		} else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) {
420			bbb_done(sc, 0);/* success */
421		} else {
422			bbb_done(sc, 1);/* error */
423		}
424		break;
425
426	case USB_ST_SETUP:
427		usbd_xfer_set_frame_data(xfer, 0, &sc->csw, sizeof(sc->csw));
428		usbd_transfer_submit(xfer);
429		break;
430
431	default:
432		DPRINTFN(0, "Failed to read CSW: %s, try %d\n",
433		    usbd_errstr(error), sc->status_try);
434
435		if (error == USB_ERR_CANCELLED || sc->status_try) {
436			bbb_done(sc, 1);
437		} else {
438			sc->status_try = 1;
439			bbb_transfer_start(sc, ST_DATA_RD_CS);
440		}
441		break;
442	}
443}
444
445/*------------------------------------------------------------------------*
446 *	bbb_command_start - execute a SCSI command synchronously
447 *
448 * Return values
449 * 0: Success
450 * Else: Failure
451 *------------------------------------------------------------------------*/
452static uint8_t
453bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun,
454    void *data_ptr, usb_size_t data_len, uint8_t cmd_len,
455    usb_timeout_t data_timeout)
456{
457	sc->lun = lun;
458	sc->dir = data_len ? dir : DIR_NONE;
459	sc->data_ptr = data_ptr;
460	sc->data_len = data_len;
461	sc->data_rem = data_len;
462	sc->data_timeout = (data_timeout + USB_MS_HZ);
463	sc->actlen = 0;
464	sc->cmd_len = cmd_len;
465
466	usbd_transfer_start(sc->xfer[sc->state]);
467
468	while (usbd_transfer_pending(sc->xfer[sc->state])) {
469		cv_wait(&sc->cv, &sc->mtx);
470	}
471	return (sc->error);
472}
473
474/*------------------------------------------------------------------------*
475 *	usb_test_autoinstall
476 *
477 * Return values:
478 * 0: This interface is an auto install disk (CD-ROM)
479 * Else: Not an auto install disk.
480 *------------------------------------------------------------------------*/
481usb_error_t
482usb_test_autoinstall(struct usb_device *udev, uint8_t iface_index,
483    uint8_t do_eject)
484{
485	struct usb_interface *iface;
486	struct usb_interface_descriptor *id;
487	usb_error_t err;
488	uint8_t timeout;
489	uint8_t sid_type;
490	struct bbb_transfer *sc;
491
492	if (udev == NULL) {
493		return (USB_ERR_INVAL);
494	}
495	iface = usbd_get_iface(udev, iface_index);
496	if (iface == NULL) {
497		return (USB_ERR_INVAL);
498	}
499	id = iface->idesc;
500	if (id == NULL) {
501		return (USB_ERR_INVAL);
502	}
503	if (id->bInterfaceClass != UICLASS_MASS) {
504		return (USB_ERR_INVAL);
505	}
506	switch (id->bInterfaceSubClass) {
507	case UISUBCLASS_SCSI:
508	case UISUBCLASS_UFI:
509		break;
510	default:
511		return (USB_ERR_INVAL);
512	}
513
514	switch (id->bInterfaceProtocol) {
515	case UIPROTO_MASS_BBB_OLD:
516	case UIPROTO_MASS_BBB:
517		break;
518	default:
519		return (USB_ERR_INVAL);
520	}
521
522	sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO);
523	if (sc == NULL) {
524		return (USB_ERR_NOMEM);
525	}
526	mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF);
527	cv_init(&sc->cv, "WBBB");
528
529	err = usbd_transfer_setup(udev,
530	    &iface_index, sc->xfer, bbb_config,
531	    ST_MAX, sc, &sc->mtx);
532
533	if (err) {
534		goto done;
535	}
536	mtx_lock(&sc->mtx);
537
538	timeout = 4;			/* tries */
539
540repeat_inquiry:
541
542	sc->cbw.CBWCDB[0] = 0x12;	/* INQUIRY */
543	sc->cbw.CBWCDB[1] = 0;
544	sc->cbw.CBWCDB[2] = 0;
545	sc->cbw.CBWCDB[3] = 0;
546	sc->cbw.CBWCDB[4] = 0x24;	/* length */
547	sc->cbw.CBWCDB[5] = 0;
548	err = bbb_command_start(sc, DIR_IN, 0,
549	    sc->buffer, 0x24, 6, USB_MS_HZ);
550
551	if ((sc->actlen != 0) && (err == 0)) {
552		sid_type = sc->buffer[0] & 0x1F;
553		if (sid_type == 0x05) {
554			/* CD-ROM */
555			if (do_eject) {
556				/* 0: opcode: SCSI START/STOP */
557				sc->cbw.CBWCDB[0] = 0x1b;
558				/* 1: byte2: Not immediate */
559				sc->cbw.CBWCDB[1] = 0x00;
560				/* 2..3: reserved */
561				sc->cbw.CBWCDB[2] = 0x00;
562				sc->cbw.CBWCDB[3] = 0x00;
563				/* 4: Load/Eject command */
564				sc->cbw.CBWCDB[4] = 0x02;
565				/* 5: control */
566				sc->cbw.CBWCDB[5] = 0x00;
567				err = bbb_command_start(sc, DIR_OUT, 0,
568				    NULL, 0, 6, USB_MS_HZ);
569
570				DPRINTFN(0, "Eject CD command "
571				    "status: %s\n", usbd_errstr(err));
572			}
573			err = 0;
574			goto done;
575		}
576	} else if ((err != 2) && --timeout) {
577		usb_pause_mtx(&sc->mtx, hz);
578		goto repeat_inquiry;
579	}
580	err = USB_ERR_INVAL;
581	goto done;
582
583done:
584	mtx_unlock(&sc->mtx);
585	usbd_transfer_unsetup(sc->xfer, ST_MAX);
586	mtx_destroy(&sc->mtx);
587	cv_destroy(&sc->cv);
588	free(sc, M_USB);
589	return (err);
590}
591