xref: /freebsd/sys/dev/usb/usb_msctest.c (revision ee6eac62f7525e948d3b693e4eef182f84625852)
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/module.h>
44 #include <sys/lock.h>
45 #include <sys/mutex.h>
46 #include <sys/condvar.h>
47 #include <sys/sysctl.h>
48 #include <sys/sx.h>
49 #include <sys/unistd.h>
50 #include <sys/callout.h>
51 #include <sys/malloc.h>
52 #include <sys/priv.h>
53 
54 #include <dev/usb/usb.h>
55 #include <dev/usb/usbdi.h>
56 #include <dev/usb/usbdi_util.h>
57 
58 #define	USB_DEBUG_VAR usb_debug
59 
60 #include <dev/usb/usb_busdma.h>
61 #include <dev/usb/usb_process.h>
62 #include <dev/usb/usb_transfer.h>
63 #include <dev/usb/usb_msctest.h>
64 #include <dev/usb/usb_debug.h>
65 #include <dev/usb/usb_device.h>
66 #include <dev/usb/usb_request.h>
67 #include <dev/usb/usb_util.h>
68 #include <dev/usb/quirk/usb_quirk.h>
69 
70 enum {
71 	ST_COMMAND,
72 	ST_DATA_RD,
73 	ST_DATA_RD_CS,
74 	ST_DATA_WR,
75 	ST_DATA_WR_CS,
76 	ST_STATUS,
77 	ST_MAX,
78 };
79 
80 enum {
81 	DIR_IN,
82 	DIR_OUT,
83 	DIR_NONE,
84 };
85 
86 #define	SCSI_INQ_LEN	0x24
87 static uint8_t scsi_test_unit_ready[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
88 static uint8_t scsi_inquiry[] = { 0x12, 0x00, 0x00, 0x00, SCSI_INQ_LEN, 0x00 };
89 static uint8_t scsi_rezero_init[] =     { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
90 static uint8_t scsi_start_stop_unit[] = { 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00 };
91 static uint8_t scsi_ztestor_eject[] =   { 0x85, 0x01, 0x01, 0x01, 0x18, 0x01,
92 					  0x01, 0x01, 0x01, 0x01, 0x00, 0x00 };
93 static uint8_t scsi_cmotech_eject[] =   { 0xff, 0x52, 0x44, 0x45, 0x56, 0x43,
94 					  0x48, 0x47 };
95 static uint8_t scsi_huawei_eject[] =	{ 0x11, 0x06, 0x00, 0x00, 0x00, 0x00,
96 					  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97 					  0x00, 0x00, 0x00, 0x00 };
98 static uint8_t scsi_tct_eject[] =	{ 0x06, 0xf5, 0x04, 0x02, 0x52, 0x70 };
99 
100 #define	BULK_SIZE		64	/* dummy */
101 #define	ERR_CSW_FAILED		-1
102 
103 /* Command Block Wrapper */
104 struct bbb_cbw {
105 	uDWord	dCBWSignature;
106 #define	CBWSIGNATURE	0x43425355
107 	uDWord	dCBWTag;
108 	uDWord	dCBWDataTransferLength;
109 	uByte	bCBWFlags;
110 #define	CBWFLAGS_OUT	0x00
111 #define	CBWFLAGS_IN	0x80
112 	uByte	bCBWLUN;
113 	uByte	bCDBLength;
114 #define	CBWCDBLENGTH	16
115 	uByte	CBWCDB[CBWCDBLENGTH];
116 } __packed;
117 
118 /* Command Status Wrapper */
119 struct bbb_csw {
120 	uDWord	dCSWSignature;
121 #define	CSWSIGNATURE	0x53425355
122 	uDWord	dCSWTag;
123 	uDWord	dCSWDataResidue;
124 	uByte	bCSWStatus;
125 #define	CSWSTATUS_GOOD	0x0
126 #define	CSWSTATUS_FAILED	0x1
127 #define	CSWSTATUS_PHASE	0x2
128 } __packed;
129 
130 struct bbb_transfer {
131 	struct mtx mtx;
132 	struct cv cv;
133 	struct bbb_cbw cbw;
134 	struct bbb_csw csw;
135 
136 	struct usb_xfer *xfer[ST_MAX];
137 
138 	uint8_t *data_ptr;
139 
140 	usb_size_t data_len;		/* bytes */
141 	usb_size_t data_rem;		/* bytes */
142 	usb_timeout_t data_timeout;	/* ms */
143 	usb_frlength_t actlen;		/* bytes */
144 
145 	uint8_t	cmd_len;		/* bytes */
146 	uint8_t	dir;
147 	uint8_t	lun;
148 	uint8_t	state;
149 	uint8_t	status_try;
150 	int	error;
151 
152 	uint8_t	buffer[256];
153 };
154 
155 static usb_callback_t bbb_command_callback;
156 static usb_callback_t bbb_data_read_callback;
157 static usb_callback_t bbb_data_rd_cs_callback;
158 static usb_callback_t bbb_data_write_callback;
159 static usb_callback_t bbb_data_wr_cs_callback;
160 static usb_callback_t bbb_status_callback;
161 
162 static void	bbb_done(struct bbb_transfer *, int);
163 static void	bbb_transfer_start(struct bbb_transfer *, uint8_t);
164 static void	bbb_data_clear_stall_callback(struct usb_xfer *, uint8_t,
165 		    uint8_t);
166 static uint8_t bbb_command_start(struct bbb_transfer *, uint8_t, uint8_t,
167 		    void *, size_t, void *, size_t, usb_timeout_t);
168 static struct bbb_transfer *bbb_attach(struct usb_device *, uint8_t);
169 static void	bbb_detach(struct bbb_transfer *);
170 
171 static const struct usb_config bbb_config[ST_MAX] = {
172 
173 	[ST_COMMAND] = {
174 		.type = UE_BULK,
175 		.endpoint = UE_ADDR_ANY,
176 		.direction = UE_DIR_OUT,
177 		.bufsize = sizeof(struct bbb_cbw),
178 		.callback = &bbb_command_callback,
179 		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
180 	},
181 
182 	[ST_DATA_RD] = {
183 		.type = UE_BULK,
184 		.endpoint = UE_ADDR_ANY,
185 		.direction = UE_DIR_IN,
186 		.bufsize = BULK_SIZE,
187 		.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
188 		.callback = &bbb_data_read_callback,
189 		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
190 	},
191 
192 	[ST_DATA_RD_CS] = {
193 		.type = UE_CONTROL,
194 		.endpoint = 0x00,	/* Control pipe */
195 		.direction = UE_DIR_ANY,
196 		.bufsize = sizeof(struct usb_device_request),
197 		.callback = &bbb_data_rd_cs_callback,
198 		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
199 	},
200 
201 	[ST_DATA_WR] = {
202 		.type = UE_BULK,
203 		.endpoint = UE_ADDR_ANY,
204 		.direction = UE_DIR_OUT,
205 		.bufsize = BULK_SIZE,
206 		.flags = {.proxy_buffer = 1,},
207 		.callback = &bbb_data_write_callback,
208 		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
209 	},
210 
211 	[ST_DATA_WR_CS] = {
212 		.type = UE_CONTROL,
213 		.endpoint = 0x00,	/* Control pipe */
214 		.direction = UE_DIR_ANY,
215 		.bufsize = sizeof(struct usb_device_request),
216 		.callback = &bbb_data_wr_cs_callback,
217 		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
218 	},
219 
220 	[ST_STATUS] = {
221 		.type = UE_BULK,
222 		.endpoint = UE_ADDR_ANY,
223 		.direction = UE_DIR_IN,
224 		.bufsize = sizeof(struct bbb_csw),
225 		.flags = {.short_xfer_ok = 1,},
226 		.callback = &bbb_status_callback,
227 		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
228 	},
229 };
230 
231 static void
232 bbb_done(struct bbb_transfer *sc, int error)
233 {
234 
235 	sc->error = error;
236 	sc->state = ST_COMMAND;
237 	sc->status_try = 1;
238 	cv_signal(&sc->cv);
239 }
240 
241 static void
242 bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index)
243 {
244 	sc->state = xfer_index;
245 	usbd_transfer_start(sc->xfer[xfer_index]);
246 }
247 
248 static void
249 bbb_data_clear_stall_callback(struct usb_xfer *xfer,
250     uint8_t next_xfer, uint8_t stall_xfer)
251 {
252 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
253 
254 	if (usbd_clear_stall_callback(xfer, sc->xfer[stall_xfer])) {
255 		switch (USB_GET_STATE(xfer)) {
256 		case USB_ST_SETUP:
257 		case USB_ST_TRANSFERRED:
258 			bbb_transfer_start(sc, next_xfer);
259 			break;
260 		default:
261 			bbb_done(sc, USB_ERR_STALLED);
262 			break;
263 		}
264 	}
265 }
266 
267 static void
268 bbb_command_callback(struct usb_xfer *xfer, usb_error_t error)
269 {
270 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
271 	uint32_t tag;
272 
273 	switch (USB_GET_STATE(xfer)) {
274 	case USB_ST_TRANSFERRED:
275 		bbb_transfer_start
276 		    (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD :
277 		    (sc->dir == DIR_OUT) ? ST_DATA_WR :
278 		    ST_STATUS));
279 		break;
280 
281 	case USB_ST_SETUP:
282 		sc->status_try = 0;
283 		tag = UGETDW(sc->cbw.dCBWTag) + 1;
284 		USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE);
285 		USETDW(sc->cbw.dCBWTag, tag);
286 		USETDW(sc->cbw.dCBWDataTransferLength, (uint32_t)sc->data_len);
287 		sc->cbw.bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT);
288 		sc->cbw.bCBWLUN = sc->lun;
289 		sc->cbw.bCDBLength = sc->cmd_len;
290 		if (sc->cbw.bCDBLength > sizeof(sc->cbw.CBWCDB)) {
291 			sc->cbw.bCDBLength = sizeof(sc->cbw.CBWCDB);
292 			DPRINTFN(0, "Truncating long command\n");
293 		}
294 		usbd_xfer_set_frame_data(xfer, 0, &sc->cbw, sizeof(sc->cbw));
295 		usbd_transfer_submit(xfer);
296 		break;
297 
298 	default:			/* Error */
299 		bbb_done(sc, error);
300 		break;
301 	}
302 }
303 
304 static void
305 bbb_data_read_callback(struct usb_xfer *xfer, usb_error_t error)
306 {
307 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
308 	usb_frlength_t max_bulk = usbd_xfer_max_len(xfer);
309 	int actlen, sumlen;
310 
311 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
312 
313 	switch (USB_GET_STATE(xfer)) {
314 	case USB_ST_TRANSFERRED:
315 		sc->data_rem -= actlen;
316 		sc->data_ptr += actlen;
317 		sc->actlen += actlen;
318 
319 		if (actlen < sumlen) {
320 			/* short transfer */
321 			sc->data_rem = 0;
322 		}
323 	case USB_ST_SETUP:
324 		DPRINTF("max_bulk=%d, data_rem=%d\n",
325 		    max_bulk, sc->data_rem);
326 
327 		if (sc->data_rem == 0) {
328 			bbb_transfer_start(sc, ST_STATUS);
329 			break;
330 		}
331 		if (max_bulk > sc->data_rem) {
332 			max_bulk = sc->data_rem;
333 		}
334 		usbd_xfer_set_timeout(xfer, sc->data_timeout);
335 		usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk);
336 		usbd_transfer_submit(xfer);
337 		break;
338 
339 	default:			/* Error */
340 		if (error == USB_ERR_CANCELLED) {
341 			bbb_done(sc, error);
342 		} else {
343 			bbb_transfer_start(sc, ST_DATA_RD_CS);
344 		}
345 		break;
346 	}
347 }
348 
349 static void
350 bbb_data_rd_cs_callback(struct usb_xfer *xfer, usb_error_t error)
351 {
352 	bbb_data_clear_stall_callback(xfer, ST_STATUS,
353 	    ST_DATA_RD);
354 }
355 
356 static void
357 bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error)
358 {
359 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
360 	usb_frlength_t max_bulk = usbd_xfer_max_len(xfer);
361 	int actlen, sumlen;
362 
363 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
364 
365 	switch (USB_GET_STATE(xfer)) {
366 	case USB_ST_TRANSFERRED:
367 		sc->data_rem -= actlen;
368 		sc->data_ptr += actlen;
369 		sc->actlen += actlen;
370 
371 		if (actlen < sumlen) {
372 			/* short transfer */
373 			sc->data_rem = 0;
374 		}
375 	case USB_ST_SETUP:
376 		DPRINTF("max_bulk=%d, data_rem=%d\n",
377 		    max_bulk, sc->data_rem);
378 
379 		if (sc->data_rem == 0) {
380 			bbb_transfer_start(sc, ST_STATUS);
381 			return;
382 		}
383 		if (max_bulk > sc->data_rem) {
384 			max_bulk = sc->data_rem;
385 		}
386 		usbd_xfer_set_timeout(xfer, sc->data_timeout);
387 		usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk);
388 		usbd_transfer_submit(xfer);
389 		return;
390 
391 	default:			/* Error */
392 		if (error == USB_ERR_CANCELLED) {
393 			bbb_done(sc, error);
394 		} else {
395 			bbb_transfer_start(sc, ST_DATA_WR_CS);
396 		}
397 		return;
398 
399 	}
400 }
401 
402 static void
403 bbb_data_wr_cs_callback(struct usb_xfer *xfer, usb_error_t error)
404 {
405 	bbb_data_clear_stall_callback(xfer, ST_STATUS,
406 	    ST_DATA_WR);
407 }
408 
409 static void
410 bbb_status_callback(struct usb_xfer *xfer, usb_error_t error)
411 {
412 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
413 	int actlen, sumlen;
414 
415 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
416 
417 	switch (USB_GET_STATE(xfer)) {
418 	case USB_ST_TRANSFERRED:
419 
420 		/* very simple status check */
421 
422 		if (actlen < sizeof(sc->csw)) {
423 			bbb_done(sc, USB_ERR_SHORT_XFER);
424 		} else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) {
425 			bbb_done(sc, 0);	/* success */
426 		} else {
427 			bbb_done(sc, ERR_CSW_FAILED);	/* error */
428 		}
429 		break;
430 
431 	case USB_ST_SETUP:
432 		usbd_xfer_set_frame_data(xfer, 0, &sc->csw, sizeof(sc->csw));
433 		usbd_transfer_submit(xfer);
434 		break;
435 
436 	default:
437 		DPRINTF("Failed to read CSW: %s, try %d\n",
438 		    usbd_errstr(error), sc->status_try);
439 
440 		if (error == USB_ERR_CANCELLED || sc->status_try) {
441 			bbb_done(sc, error);
442 		} else {
443 			sc->status_try = 1;
444 			bbb_transfer_start(sc, ST_DATA_RD_CS);
445 		}
446 		break;
447 	}
448 }
449 
450 /*------------------------------------------------------------------------*
451  *	bbb_command_start - execute a SCSI command synchronously
452  *
453  * Return values
454  * 0: Success
455  * Else: Failure
456  *------------------------------------------------------------------------*/
457 static uint8_t
458 bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun,
459     void *data_ptr, size_t data_len, void *cmd_ptr, size_t cmd_len,
460     usb_timeout_t data_timeout)
461 {
462 	sc->lun = lun;
463 	sc->dir = data_len ? dir : DIR_NONE;
464 	sc->data_ptr = data_ptr;
465 	sc->data_len = data_len;
466 	sc->data_rem = data_len;
467 	sc->data_timeout = (data_timeout + USB_MS_HZ);
468 	sc->actlen = 0;
469 	sc->cmd_len = cmd_len;
470 	bzero(&sc->cbw.CBWCDB, sizeof(sc->cbw.CBWCDB));
471 	bcopy(cmd_ptr, &sc->cbw.CBWCDB, cmd_len);
472 	DPRINTFN(1, "SCSI cmd = %*D\n", (int)cmd_len, &sc->cbw.CBWCDB, ":");
473 
474 	mtx_lock(&sc->mtx);
475 	usbd_transfer_start(sc->xfer[sc->state]);
476 
477 	while (usbd_transfer_pending(sc->xfer[sc->state])) {
478 		cv_wait(&sc->cv, &sc->mtx);
479 	}
480 	mtx_unlock(&sc->mtx);
481 	return (sc->error);
482 }
483 
484 static struct bbb_transfer *
485 bbb_attach(struct usb_device *udev, uint8_t iface_index)
486 {
487 	struct usb_interface *iface;
488 	struct usb_interface_descriptor *id;
489 	struct bbb_transfer *sc;
490 	usb_error_t err;
491 	uint8_t do_unlock;
492 
493 	/* automatic locking */
494 	if (usbd_enum_is_locked(udev)) {
495 		do_unlock = 0;
496 	} else {
497 		do_unlock = 1;
498 		usbd_enum_lock(udev);
499 	}
500 
501 	/*
502 	 * Make sure any driver which is hooked up to this interface,
503 	 * like umass is gone:
504 	 */
505 	usb_detach_device(udev, iface_index, 0);
506 
507 	if (do_unlock)
508 		usbd_enum_unlock(udev);
509 
510 	iface = usbd_get_iface(udev, iface_index);
511 	if (iface == NULL)
512 		return (NULL);
513 
514 	id = iface->idesc;
515 	if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
516 		return (NULL);
517 
518 	switch (id->bInterfaceSubClass) {
519 	case UISUBCLASS_SCSI:
520 	case UISUBCLASS_UFI:
521 	case UISUBCLASS_SFF8020I:
522 	case UISUBCLASS_SFF8070I:
523 		break;
524 	default:
525 		return (NULL);
526 	}
527 
528 	switch (id->bInterfaceProtocol) {
529 	case UIPROTO_MASS_BBB_OLD:
530 	case UIPROTO_MASS_BBB:
531 		break;
532 	default:
533 		return (NULL);
534 	}
535 
536 	sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO);
537 	mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF);
538 	cv_init(&sc->cv, "WBBB");
539 
540 	err = usbd_transfer_setup(udev, &iface_index, sc->xfer, bbb_config,
541 	    ST_MAX, sc, &sc->mtx);
542 	if (err) {
543 		bbb_detach(sc);
544 		return (NULL);
545 	}
546 	return (sc);
547 }
548 
549 static void
550 bbb_detach(struct bbb_transfer *sc)
551 {
552 	usbd_transfer_unsetup(sc->xfer, ST_MAX);
553 	mtx_destroy(&sc->mtx);
554 	cv_destroy(&sc->cv);
555 	free(sc, M_USB);
556 }
557 
558 /*------------------------------------------------------------------------*
559  *	usb_iface_is_cdrom
560  *
561  * Return values:
562  * 1: This interface is an auto install disk (CD-ROM)
563  * 0: Not an auto install disk.
564  *------------------------------------------------------------------------*/
565 int
566 usb_iface_is_cdrom(struct usb_device *udev, uint8_t iface_index)
567 {
568 	struct bbb_transfer *sc;
569 	usb_error_t err;
570 	uint8_t timeout, is_cdrom;
571 	uint8_t sid_type;
572 
573 	sc = bbb_attach(udev, iface_index);
574 	if (sc == NULL)
575 		return (0);
576 
577 	is_cdrom = 0;
578 	timeout = 4;	/* tries */
579 	while (--timeout) {
580 		err = bbb_command_start(sc, DIR_IN, 0, sc->buffer,
581 		    SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry),
582 		    USB_MS_HZ);
583 
584 		if (err == 0 && sc->actlen > 0) {
585 			sid_type = sc->buffer[0] & 0x1F;
586 			if (sid_type == 0x05)
587 				is_cdrom = 1;
588 			break;
589 		} else if (err != ERR_CSW_FAILED)
590 			break;	/* non retryable error */
591 		usb_pause_mtx(NULL, hz);
592 	}
593 	bbb_detach(sc);
594 	return (is_cdrom);
595 }
596 
597 usb_error_t
598 usb_msc_eject(struct usb_device *udev, uint8_t iface_index, int method)
599 {
600 	struct bbb_transfer *sc;
601 	usb_error_t err;
602 
603 	sc = bbb_attach(udev, iface_index);
604 	if (sc == NULL)
605 		return (USB_ERR_INVAL);
606 
607 	err = 0;
608 	switch (method) {
609 	case MSC_EJECT_STOPUNIT:
610 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
611 		    &scsi_test_unit_ready, sizeof(scsi_test_unit_ready),
612 		    USB_MS_HZ);
613 		DPRINTF("Test unit ready status: %s\n", usbd_errstr(err));
614 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
615 		    &scsi_start_stop_unit, sizeof(scsi_start_stop_unit),
616 		    USB_MS_HZ);
617 		break;
618 	case MSC_EJECT_REZERO:
619 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
620 		    &scsi_rezero_init, sizeof(scsi_rezero_init),
621 		    USB_MS_HZ);
622 		break;
623 	case MSC_EJECT_ZTESTOR:
624 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
625 		    &scsi_ztestor_eject, sizeof(scsi_ztestor_eject),
626 		    USB_MS_HZ);
627 		break;
628 	case MSC_EJECT_CMOTECH:
629 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
630 		    &scsi_cmotech_eject, sizeof(scsi_cmotech_eject),
631 		    USB_MS_HZ);
632 		break;
633 	case MSC_EJECT_HUAWEI:
634 		err = bbb_command_start(sc, DIR_IN, 0, NULL, 0,
635 		    &scsi_huawei_eject, sizeof(scsi_huawei_eject),
636 		    USB_MS_HZ);
637 		break;
638 	case MSC_EJECT_TCT:
639 		/*
640 		 * TCTMobile needs DIR_IN flag. To get it, we
641 		 * supply a dummy data with the command.
642 		 */
643 		err = bbb_command_start(sc, DIR_IN, 0, &sc->buffer,
644 		    sizeof(sc->buffer), &scsi_tct_eject,
645 		    sizeof(scsi_tct_eject), USB_MS_HZ);
646 		break;
647 	default:
648 		printf("usb_msc_eject: unknown eject method (%d)\n", method);
649 		break;
650 	}
651 	DPRINTF("Eject CD command status: %s\n", usbd_errstr(err));
652 
653 	bbb_detach(sc);
654 	return (0);
655 }
656