xref: /freebsd/sys/dev/usb/usb_msctest.c (revision 409a390c3341fb4f162cd7de1fd595a323ebbfd8)
1 /* $FreeBSD$ */
2 /*-
3  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 /*
28  * The following file contains code that will detect USB autoinstall
29  * disks.
30  *
31  * TODO: Potentially we could add code to automatically detect USB
32  * mass storage quirks for not supported SCSI commands!
33  */
34 
35 #include <sys/stdint.h>
36 #include <sys/stddef.h>
37 #include <sys/param.h>
38 #include <sys/queue.h>
39 #include <sys/types.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/bus.h>
43 #include <sys/linker_set.h>
44 #include <sys/module.h>
45 #include <sys/lock.h>
46 #include <sys/mutex.h>
47 #include <sys/condvar.h>
48 #include <sys/sysctl.h>
49 #include <sys/sx.h>
50 #include <sys/unistd.h>
51 #include <sys/callout.h>
52 #include <sys/malloc.h>
53 #include <sys/priv.h>
54 
55 #include <dev/usb/usb.h>
56 #include <dev/usb/usbdi.h>
57 #include <dev/usb/usbdi_util.h>
58 
59 #define	USB_DEBUG_VAR usb_debug
60 
61 #include <dev/usb/usb_busdma.h>
62 #include <dev/usb/usb_process.h>
63 #include <dev/usb/usb_transfer.h>
64 #include <dev/usb/usb_msctest.h>
65 #include <dev/usb/usb_debug.h>
66 #include <dev/usb/usb_busdma.h>
67 #include <dev/usb/usb_device.h>
68 #include <dev/usb/usb_request.h>
69 #include <dev/usb/usb_util.h>
70 #include <dev/usb/quirk/usb_quirk.h>
71 
72 enum {
73 	ST_COMMAND,
74 	ST_DATA_RD,
75 	ST_DATA_RD_CS,
76 	ST_DATA_WR,
77 	ST_DATA_WR_CS,
78 	ST_STATUS,
79 	ST_MAX,
80 };
81 
82 enum {
83 	DIR_IN,
84 	DIR_OUT,
85 	DIR_NONE,
86 };
87 
88 #define	SCSI_INQ_LEN	0x24
89 static uint8_t scsi_test_unit_ready[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
90 static uint8_t scsi_inquiry[] = { 0x12, 0x00, 0x00, 0x00, SCSI_INQ_LEN, 0x00 };
91 static uint8_t scsi_rezero_init[] =     { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
92 static uint8_t scsi_start_stop_unit[] = { 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00 };
93 static uint8_t scsi_ztestor_eject[] =   { 0x85, 0x01, 0x01, 0x01, 0x18, 0x01,
94 					  0x01, 0x01, 0x01, 0x01, 0x00, 0x00 };
95 static uint8_t scsi_cmotech_eject[] =   { 0xff, 0x52, 0x44, 0x45, 0x56, 0x43,
96 					  0x48, 0x47 };
97 
98 #define	BULK_SIZE		64	/* dummy */
99 #define	ERR_CSW_FAILED		-1
100 
101 /* Command Block Wrapper */
102 struct bbb_cbw {
103 	uDWord	dCBWSignature;
104 #define	CBWSIGNATURE	0x43425355
105 	uDWord	dCBWTag;
106 	uDWord	dCBWDataTransferLength;
107 	uByte	bCBWFlags;
108 #define	CBWFLAGS_OUT	0x00
109 #define	CBWFLAGS_IN	0x80
110 	uByte	bCBWLUN;
111 	uByte	bCDBLength;
112 #define	CBWCDBLENGTH	16
113 	uByte	CBWCDB[CBWCDBLENGTH];
114 } __packed;
115 
116 /* Command Status Wrapper */
117 struct bbb_csw {
118 	uDWord	dCSWSignature;
119 #define	CSWSIGNATURE	0x53425355
120 	uDWord	dCSWTag;
121 	uDWord	dCSWDataResidue;
122 	uByte	bCSWStatus;
123 #define	CSWSTATUS_GOOD	0x0
124 #define	CSWSTATUS_FAILED	0x1
125 #define	CSWSTATUS_PHASE	0x2
126 } __packed;
127 
128 struct bbb_transfer {
129 	struct mtx mtx;
130 	struct cv cv;
131 	struct bbb_cbw cbw;
132 	struct bbb_csw csw;
133 
134 	struct usb_xfer *xfer[ST_MAX];
135 
136 	uint8_t *data_ptr;
137 
138 	usb_size_t data_len;		/* bytes */
139 	usb_size_t data_rem;		/* bytes */
140 	usb_timeout_t data_timeout;	/* ms */
141 	usb_frlength_t actlen;		/* bytes */
142 
143 	uint8_t	cmd_len;		/* bytes */
144 	uint8_t	dir;
145 	uint8_t	lun;
146 	uint8_t	state;
147 	uint8_t	status_try;
148 	int	error;
149 
150 	uint8_t	buffer[256];
151 };
152 
153 static usb_callback_t bbb_command_callback;
154 static usb_callback_t bbb_data_read_callback;
155 static usb_callback_t bbb_data_rd_cs_callback;
156 static usb_callback_t bbb_data_write_callback;
157 static usb_callback_t bbb_data_wr_cs_callback;
158 static usb_callback_t bbb_status_callback;
159 
160 static void	bbb_done(struct bbb_transfer *, int);
161 static void	bbb_transfer_start(struct bbb_transfer *, uint8_t);
162 static void	bbb_data_clear_stall_callback(struct usb_xfer *, uint8_t,
163 		    uint8_t);
164 static uint8_t bbb_command_start(struct bbb_transfer *, uint8_t, uint8_t,
165 		    void *, size_t, void *, size_t, usb_timeout_t);
166 static struct bbb_transfer *bbb_attach(struct usb_device *, uint8_t);
167 static void	bbb_detach(struct bbb_transfer *);
168 
169 static const struct usb_config bbb_config[ST_MAX] = {
170 
171 	[ST_COMMAND] = {
172 		.type = UE_BULK,
173 		.endpoint = UE_ADDR_ANY,
174 		.direction = UE_DIR_OUT,
175 		.bufsize = sizeof(struct bbb_cbw),
176 		.callback = &bbb_command_callback,
177 		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
178 	},
179 
180 	[ST_DATA_RD] = {
181 		.type = UE_BULK,
182 		.endpoint = UE_ADDR_ANY,
183 		.direction = UE_DIR_IN,
184 		.bufsize = BULK_SIZE,
185 		.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
186 		.callback = &bbb_data_read_callback,
187 		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
188 	},
189 
190 	[ST_DATA_RD_CS] = {
191 		.type = UE_CONTROL,
192 		.endpoint = 0x00,	/* Control pipe */
193 		.direction = UE_DIR_ANY,
194 		.bufsize = sizeof(struct usb_device_request),
195 		.callback = &bbb_data_rd_cs_callback,
196 		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
197 	},
198 
199 	[ST_DATA_WR] = {
200 		.type = UE_BULK,
201 		.endpoint = UE_ADDR_ANY,
202 		.direction = UE_DIR_OUT,
203 		.bufsize = BULK_SIZE,
204 		.flags = {.proxy_buffer = 1,},
205 		.callback = &bbb_data_write_callback,
206 		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
207 	},
208 
209 	[ST_DATA_WR_CS] = {
210 		.type = UE_CONTROL,
211 		.endpoint = 0x00,	/* Control pipe */
212 		.direction = UE_DIR_ANY,
213 		.bufsize = sizeof(struct usb_device_request),
214 		.callback = &bbb_data_wr_cs_callback,
215 		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
216 	},
217 
218 	[ST_STATUS] = {
219 		.type = UE_BULK,
220 		.endpoint = UE_ADDR_ANY,
221 		.direction = UE_DIR_IN,
222 		.bufsize = sizeof(struct bbb_csw),
223 		.flags = {.short_xfer_ok = 1,},
224 		.callback = &bbb_status_callback,
225 		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
226 	},
227 };
228 
229 static void
230 bbb_done(struct bbb_transfer *sc, int error)
231 {
232 
233 	sc->error = error;
234 	sc->state = ST_COMMAND;
235 	sc->status_try = 1;
236 	cv_signal(&sc->cv);
237 }
238 
239 static void
240 bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index)
241 {
242 	sc->state = xfer_index;
243 	usbd_transfer_start(sc->xfer[xfer_index]);
244 }
245 
246 static void
247 bbb_data_clear_stall_callback(struct usb_xfer *xfer,
248     uint8_t next_xfer, uint8_t stall_xfer)
249 {
250 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
251 
252 	if (usbd_clear_stall_callback(xfer, sc->xfer[stall_xfer])) {
253 		switch (USB_GET_STATE(xfer)) {
254 		case USB_ST_SETUP:
255 		case USB_ST_TRANSFERRED:
256 			bbb_transfer_start(sc, next_xfer);
257 			break;
258 		default:
259 			bbb_done(sc, USB_ERR_STALLED);
260 			break;
261 		}
262 	}
263 }
264 
265 static void
266 bbb_command_callback(struct usb_xfer *xfer, usb_error_t error)
267 {
268 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
269 	uint32_t tag;
270 
271 	switch (USB_GET_STATE(xfer)) {
272 	case USB_ST_TRANSFERRED:
273 		bbb_transfer_start
274 		    (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD :
275 		    (sc->dir == DIR_OUT) ? ST_DATA_WR :
276 		    ST_STATUS));
277 		break;
278 
279 	case USB_ST_SETUP:
280 		sc->status_try = 0;
281 		tag = UGETDW(sc->cbw.dCBWTag) + 1;
282 		USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE);
283 		USETDW(sc->cbw.dCBWTag, tag);
284 		USETDW(sc->cbw.dCBWDataTransferLength, (uint32_t)sc->data_len);
285 		sc->cbw.bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT);
286 		sc->cbw.bCBWLUN = sc->lun;
287 		sc->cbw.bCDBLength = sc->cmd_len;
288 		if (sc->cbw.bCDBLength > sizeof(sc->cbw.CBWCDB)) {
289 			sc->cbw.bCDBLength = sizeof(sc->cbw.CBWCDB);
290 			DPRINTFN(0, "Truncating long command\n");
291 		}
292 		usbd_xfer_set_frame_data(xfer, 0, &sc->cbw, sizeof(sc->cbw));
293 		usbd_transfer_submit(xfer);
294 		break;
295 
296 	default:			/* Error */
297 		bbb_done(sc, error);
298 		break;
299 	}
300 }
301 
302 static void
303 bbb_data_read_callback(struct usb_xfer *xfer, usb_error_t error)
304 {
305 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
306 	usb_frlength_t max_bulk = usbd_xfer_max_len(xfer);
307 	int actlen, sumlen;
308 
309 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
310 
311 	switch (USB_GET_STATE(xfer)) {
312 	case USB_ST_TRANSFERRED:
313 		sc->data_rem -= actlen;
314 		sc->data_ptr += actlen;
315 		sc->actlen += actlen;
316 
317 		if (actlen < sumlen) {
318 			/* short transfer */
319 			sc->data_rem = 0;
320 		}
321 	case USB_ST_SETUP:
322 		DPRINTF("max_bulk=%d, data_rem=%d\n",
323 		    max_bulk, sc->data_rem);
324 
325 		if (sc->data_rem == 0) {
326 			bbb_transfer_start(sc, ST_STATUS);
327 			break;
328 		}
329 		if (max_bulk > sc->data_rem) {
330 			max_bulk = sc->data_rem;
331 		}
332 		usbd_xfer_set_timeout(xfer, sc->data_timeout);
333 		usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk);
334 		usbd_transfer_submit(xfer);
335 		break;
336 
337 	default:			/* Error */
338 		if (error == USB_ERR_CANCELLED) {
339 			bbb_done(sc, error);
340 		} else {
341 			bbb_transfer_start(sc, ST_DATA_RD_CS);
342 		}
343 		break;
344 	}
345 }
346 
347 static void
348 bbb_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error)
349 {
350 	bbb_data_clear_stall_callback(xfer, ST_STATUS,
351 	    ST_DATA_RD);
352 }
353 
354 static void
355 bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error)
356 {
357 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
358 	usb_frlength_t max_bulk = usbd_xfer_max_len(xfer);
359 	int actlen, sumlen;
360 
361 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
362 
363 	switch (USB_GET_STATE(xfer)) {
364 	case USB_ST_TRANSFERRED:
365 		sc->data_rem -= actlen;
366 		sc->data_ptr += actlen;
367 		sc->actlen += actlen;
368 
369 		if (actlen < sumlen) {
370 			/* short transfer */
371 			sc->data_rem = 0;
372 		}
373 	case USB_ST_SETUP:
374 		DPRINTF("max_bulk=%d, data_rem=%d\n",
375 		    max_bulk, sc->data_rem);
376 
377 		if (sc->data_rem == 0) {
378 			bbb_transfer_start(sc, ST_STATUS);
379 			return;
380 		}
381 		if (max_bulk > sc->data_rem) {
382 			max_bulk = sc->data_rem;
383 		}
384 		usbd_xfer_set_timeout(xfer, sc->data_timeout);
385 		usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk);
386 		usbd_transfer_submit(xfer);
387 		return;
388 
389 	default:			/* Error */
390 		if (error == USB_ERR_CANCELLED) {
391 			bbb_done(sc, error);
392 		} else {
393 			bbb_transfer_start(sc, ST_DATA_WR_CS);
394 		}
395 		return;
396 
397 	}
398 }
399 
400 static void
401 bbb_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error)
402 {
403 	bbb_data_clear_stall_callback(xfer, ST_STATUS,
404 	    ST_DATA_WR);
405 }
406 
407 static void
408 bbb_status_callback(struct usb_xfer *xfer, usb_error_t error)
409 {
410 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
411 	int actlen, sumlen;
412 
413 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
414 
415 	switch (USB_GET_STATE(xfer)) {
416 	case USB_ST_TRANSFERRED:
417 
418 		/* very simple status check */
419 
420 		if (actlen < sizeof(sc->csw)) {
421 			bbb_done(sc, USB_ERR_SHORT_XFER);
422 		} else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) {
423 			bbb_done(sc, 0);	/* success */
424 		} else {
425 			bbb_done(sc, ERR_CSW_FAILED);	/* error */
426 		}
427 		break;
428 
429 	case USB_ST_SETUP:
430 		usbd_xfer_set_frame_data(xfer, 0, &sc->csw, sizeof(sc->csw));
431 		usbd_transfer_submit(xfer);
432 		break;
433 
434 	default:
435 		DPRINTF("Failed to read CSW: %s, try %d\n",
436 		    usbd_errstr(error), sc->status_try);
437 
438 		if (error == USB_ERR_CANCELLED || sc->status_try) {
439 			bbb_done(sc, error);
440 		} else {
441 			sc->status_try = 1;
442 			bbb_transfer_start(sc, ST_DATA_RD_CS);
443 		}
444 		break;
445 	}
446 }
447 
448 /*------------------------------------------------------------------------*
449  *	bbb_command_start - execute a SCSI command synchronously
450  *
451  * Return values
452  * 0: Success
453  * Else: Failure
454  *------------------------------------------------------------------------*/
455 static uint8_t
456 bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun,
457     void *data_ptr, size_t data_len, void *cmd_ptr, size_t cmd_len,
458     usb_timeout_t data_timeout)
459 {
460 	sc->lun = lun;
461 	sc->dir = data_len ? dir : DIR_NONE;
462 	sc->data_ptr = data_ptr;
463 	sc->data_len = data_len;
464 	sc->data_rem = data_len;
465 	sc->data_timeout = (data_timeout + USB_MS_HZ);
466 	sc->actlen = 0;
467 	sc->data_ptr = data_ptr;
468 	sc->cmd_len = cmd_len;
469 	bzero(&sc->cbw.CBWCDB, sizeof(sc->cbw.CBWCDB));
470 	bcopy(cmd_ptr, &sc->cbw.CBWCDB, cmd_len);
471 	DPRINTFN(1, "SCSI cmd = %*D\n", (int)cmd_len, &sc->cbw.CBWCDB, ":");
472 
473 	mtx_lock(&sc->mtx);
474 	usbd_transfer_start(sc->xfer[sc->state]);
475 
476 	while (usbd_transfer_pending(sc->xfer[sc->state])) {
477 		cv_wait(&sc->cv, &sc->mtx);
478 	}
479 	mtx_unlock(&sc->mtx);
480 	return (sc->error);
481 }
482 
483 static struct bbb_transfer *
484 bbb_attach(struct usb_device *udev, uint8_t iface_index)
485 {
486 	struct usb_interface *iface;
487 	struct usb_interface_descriptor *id;
488 	struct bbb_transfer *sc;
489 	usb_error_t err;
490 
491 	iface = usbd_get_iface(udev, iface_index);
492 	if (iface == NULL)
493 		return (NULL);
494 
495 	id = iface->idesc;
496 	if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
497 		return (NULL);
498 
499 	switch (id->bInterfaceSubClass) {
500 	case UISUBCLASS_SCSI:
501 	case UISUBCLASS_UFI:
502 	case UISUBCLASS_SFF8020I:
503 	case UISUBCLASS_SFF8070I:
504 		break;
505 	default:
506 		return (NULL);
507 	}
508 
509 	switch (id->bInterfaceProtocol) {
510 	case UIPROTO_MASS_BBB_OLD:
511 	case UIPROTO_MASS_BBB:
512 		break;
513 	default:
514 		return (NULL);
515 	}
516 
517 	sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO);
518 	mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF);
519 	cv_init(&sc->cv, "WBBB");
520 
521 	err = usbd_transfer_setup(udev, &iface_index, sc->xfer, bbb_config,
522 	    ST_MAX, sc, &sc->mtx);
523 	if (err) {
524 		bbb_detach(sc);
525 		return (NULL);
526 	}
527 	return (sc);
528 }
529 
530 static void
531 bbb_detach(struct bbb_transfer *sc)
532 {
533 	usbd_transfer_unsetup(sc->xfer, ST_MAX);
534 	mtx_destroy(&sc->mtx);
535 	cv_destroy(&sc->cv);
536 	free(sc, M_USB);
537 }
538 
539 /*------------------------------------------------------------------------*
540  *	usb_iface_is_cdrom
541  *
542  * Return values:
543  * 1: This interface is an auto install disk (CD-ROM)
544  * 0: Not an auto install disk.
545  *------------------------------------------------------------------------*/
546 int
547 usb_iface_is_cdrom(struct usb_device *udev, uint8_t iface_index)
548 {
549 	struct bbb_transfer *sc;
550 	usb_error_t err;
551 	uint8_t timeout, is_cdrom;
552 	uint8_t sid_type;
553 
554 	sc = bbb_attach(udev, iface_index);
555 	if (sc == NULL)
556 		return (0);
557 
558 	is_cdrom = 0;
559 	timeout = 4;	/* tries */
560 	while (--timeout) {
561 		err = bbb_command_start(sc, DIR_IN, 0, sc->buffer,
562 		    SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry),
563 		    USB_MS_HZ);
564 
565 		if (err == 0 && sc->actlen > 0) {
566 			sid_type = sc->buffer[0] & 0x1F;
567 			if (sid_type == 0x05)
568 				is_cdrom = 1;
569 			break;
570 		} else if (err != ERR_CSW_FAILED)
571 			break;	/* non retryable error */
572 		usb_pause_mtx(NULL, hz);
573 	}
574 	bbb_detach(sc);
575 	return (is_cdrom);
576 }
577 
578 usb_error_t
579 usb_msc_eject(struct usb_device *udev, uint8_t iface_index, int method)
580 {
581 	struct bbb_transfer *sc;
582 	usb_error_t err;
583 
584 	sc = bbb_attach(udev, iface_index);
585 	if (sc == NULL)
586 		return (USB_ERR_INVAL);
587 
588 	err = 0;
589 	switch (method) {
590 	case MSC_EJECT_STOPUNIT:
591 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
592 		    &scsi_test_unit_ready, sizeof(scsi_test_unit_ready),
593 		    USB_MS_HZ);
594 		DPRINTF("Test unit ready status: %s\n", usbd_errstr(err));
595 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
596 		    &scsi_start_stop_unit, sizeof(scsi_start_stop_unit),
597 		    USB_MS_HZ);
598 		break;
599 	case MSC_EJECT_REZERO:
600 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
601 		    &scsi_rezero_init, sizeof(scsi_rezero_init),
602 		    USB_MS_HZ);
603 		break;
604 	case MSC_EJECT_ZTESTOR:
605 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
606 		    &scsi_ztestor_eject, sizeof(scsi_ztestor_eject),
607 		    USB_MS_HZ);
608 		break;
609 	case MSC_EJECT_CMOTECH:
610 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
611 		    &scsi_cmotech_eject, sizeof(scsi_cmotech_eject),
612 		    USB_MS_HZ);
613 		break;
614 	default:
615 		printf("usb_msc_eject: unknown eject method (%d)\n", method);
616 		break;
617 	}
618 	DPRINTF("Eject CD command status: %s\n", usbd_errstr(err));
619 
620 	bbb_detach(sc);
621 	return (0);
622 }
623