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