xref: /freebsd/sys/dev/usb/usb_msctest.c (revision 830940567b49bb0c08dfaed40418999e76616909)
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 
71 #include <dev/usb/usb.h>
72 
73 enum {
74 	ST_COMMAND,
75 	ST_DATA_RD,
76 	ST_DATA_RD_CS,
77 	ST_DATA_WR,
78 	ST_DATA_WR_CS,
79 	ST_STATUS,
80 	ST_MAX,
81 };
82 
83 enum {
84 	DIR_IN,
85 	DIR_OUT,
86 	DIR_NONE,
87 };
88 
89 #define	BULK_SIZE		64	/* dummy */
90 
91 /* Command Block Wrapper */
92 struct bbb_cbw {
93 	uDWord	dCBWSignature;
94 #define	CBWSIGNATURE	0x43425355
95 	uDWord	dCBWTag;
96 	uDWord	dCBWDataTransferLength;
97 	uByte	bCBWFlags;
98 #define	CBWFLAGS_OUT	0x00
99 #define	CBWFLAGS_IN	0x80
100 	uByte	bCBWLUN;
101 	uByte	bCDBLength;
102 #define	CBWCDBLENGTH	16
103 	uByte	CBWCDB[CBWCDBLENGTH];
104 } __packed;
105 
106 /* Command Status Wrapper */
107 struct bbb_csw {
108 	uDWord	dCSWSignature;
109 #define	CSWSIGNATURE	0x53425355
110 	uDWord	dCSWTag;
111 	uDWord	dCSWDataResidue;
112 	uByte	bCSWStatus;
113 #define	CSWSTATUS_GOOD	0x0
114 #define	CSWSTATUS_FAILED	0x1
115 #define	CSWSTATUS_PHASE	0x2
116 } __packed;
117 
118 struct bbb_transfer {
119 	struct mtx mtx;
120 	struct cv cv;
121 	struct bbb_cbw cbw;
122 	struct bbb_csw csw;
123 
124 	struct usb_xfer *xfer[ST_MAX];
125 
126 	uint8_t *data_ptr;
127 
128 	usb_size_t data_len;		/* bytes */
129 	usb_size_t data_rem;		/* bytes */
130 	usb_timeout_t data_timeout;	/* ms */
131 	usb_frlength_t actlen;		/* bytes */
132 
133 	uint8_t	cmd_len;		/* bytes */
134 	uint8_t	dir;
135 	uint8_t	lun;
136 	uint8_t	state;
137 	uint8_t	error;
138 	uint8_t	status_try;
139 
140 	uint8_t	buffer[256];
141 };
142 
143 static usb_callback_t bbb_command_callback;
144 static usb_callback_t bbb_data_read_callback;
145 static usb_callback_t bbb_data_rd_cs_callback;
146 static usb_callback_t bbb_data_write_callback;
147 static usb_callback_t bbb_data_wr_cs_callback;
148 static usb_callback_t bbb_status_callback;
149 
150 static const struct usb_config bbb_config[ST_MAX] = {
151 
152 	[ST_COMMAND] = {
153 		.type = UE_BULK,
154 		.endpoint = UE_ADDR_ANY,
155 		.direction = UE_DIR_OUT,
156 		.bufsize = sizeof(struct bbb_cbw),
157 		.callback = &bbb_command_callback,
158 		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
159 	},
160 
161 	[ST_DATA_RD] = {
162 		.type = UE_BULK,
163 		.endpoint = UE_ADDR_ANY,
164 		.direction = UE_DIR_IN,
165 		.bufsize = BULK_SIZE,
166 		.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
167 		.callback = &bbb_data_read_callback,
168 		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
169 	},
170 
171 	[ST_DATA_RD_CS] = {
172 		.type = UE_CONTROL,
173 		.endpoint = 0x00,	/* Control pipe */
174 		.direction = UE_DIR_ANY,
175 		.bufsize = sizeof(struct usb_device_request),
176 		.callback = &bbb_data_rd_cs_callback,
177 		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
178 	},
179 
180 	[ST_DATA_WR] = {
181 		.type = UE_BULK,
182 		.endpoint = UE_ADDR_ANY,
183 		.direction = UE_DIR_OUT,
184 		.bufsize = BULK_SIZE,
185 		.flags = {.proxy_buffer = 1,},
186 		.callback = &bbb_data_write_callback,
187 		.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
188 	},
189 
190 	[ST_DATA_WR_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_wr_cs_callback,
196 		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
197 	},
198 
199 	[ST_STATUS] = {
200 		.type = UE_BULK,
201 		.endpoint = UE_ADDR_ANY,
202 		.direction = UE_DIR_IN,
203 		.bufsize = sizeof(struct bbb_csw),
204 		.flags = {.short_xfer_ok = 1,},
205 		.callback = &bbb_status_callback,
206 		.timeout = 1 * USB_MS_HZ,	/* 1 second  */
207 	},
208 };
209 
210 static void
211 bbb_done(struct bbb_transfer *sc, uint8_t error)
212 {
213 	struct usb_xfer *xfer;
214 
215 	xfer = sc->xfer[sc->state];
216 
217 	/* verify the error code */
218 
219 	if (error) {
220 		switch (USB_GET_STATE(xfer)) {
221 		case USB_ST_SETUP:
222 		case USB_ST_TRANSFERRED:
223 			error = 1;
224 			break;
225 		default:
226 			error = 2;
227 			break;
228 		}
229 	}
230 	sc->error = error;
231 	sc->state = ST_COMMAND;
232 	sc->status_try = 1;
233 	cv_signal(&sc->cv);
234 }
235 
236 static void
237 bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index)
238 {
239 	sc->state = xfer_index;
240 	usbd_transfer_start(sc->xfer[xfer_index]);
241 }
242 
243 static void
244 bbb_data_clear_stall_callback(struct usb_xfer *xfer,
245     uint8_t next_xfer, uint8_t stall_xfer)
246 {
247 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
248 
249 	if (usbd_clear_stall_callback(xfer, sc->xfer[stall_xfer])) {
250 		switch (USB_GET_STATE(xfer)) {
251 		case USB_ST_SETUP:
252 		case USB_ST_TRANSFERRED:
253 			bbb_transfer_start(sc, next_xfer);
254 			break;
255 		default:
256 			bbb_done(sc, 1);
257 			break;
258 		}
259 	}
260 }
261 
262 static void
263 bbb_command_callback(struct usb_xfer *xfer, usb_error_t error)
264 {
265 	struct bbb_transfer *sc = usbd_xfer_softc(xfer);
266 	uint32_t tag;
267 
268 	switch (USB_GET_STATE(xfer)) {
269 	case USB_ST_TRANSFERRED:
270 		bbb_transfer_start
271 		    (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD :
272 		    (sc->dir == DIR_OUT) ? ST_DATA_WR :
273 		    ST_STATUS));
274 		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 
299 static void
300 bbb_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 
344 static void
345 bbb_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 
351 static void
352 bbb_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 
397 static void
398 bbb_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 
404 static void
405 bbb_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  *------------------------------------------------------------------------*/
452 static uint8_t
453 bbb_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  *------------------------------------------------------------------------*/
481 usb_error_t
482 usb_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 
540 repeat_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 
583 done:
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