xref: /freebsd/sys/dev/usb/usb_msctest.c (revision b3aaa0cc21c63d388230c7ef2a80abd631ff20d5)
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 <dev/usb/usb_defs.h>
36 #include <dev/usb/usb_mfunc.h>
37 #include <dev/usb/usb_error.h>
38 #include <dev/usb/usb.h>
39 
40 #define	USB_DEBUG_VAR usb2_debug
41 
42 #include <dev/usb/usb_core.h>
43 #include <dev/usb/usb_busdma.h>
44 #include <dev/usb/usb_process.h>
45 #include <dev/usb/usb_transfer.h>
46 #include <dev/usb/usb_msctest.h>
47 #include <dev/usb/usb_debug.h>
48 #include <dev/usb/usb_busdma.h>
49 #include <dev/usb/usb_device.h>
50 #include <dev/usb/usb_request.h>
51 #include <dev/usb/usb_util.h>
52 #include <dev/usb/usb_lookup.h>
53 
54 #include <dev/usb/usb_mfunc.h>
55 #include <dev/usb/usb_error.h>
56 #include <dev/usb/usb.h>
57 
58 enum {
59 	ST_COMMAND,
60 	ST_DATA_RD,
61 	ST_DATA_RD_CS,
62 	ST_DATA_WR,
63 	ST_DATA_WR_CS,
64 	ST_STATUS,
65 	ST_MAX,
66 };
67 
68 enum {
69 	DIR_IN,
70 	DIR_OUT,
71 	DIR_NONE,
72 };
73 
74 #define	BULK_SIZE		64	/* dummy */
75 
76 /* Command Block Wrapper */
77 struct bbb_cbw {
78 	uDWord	dCBWSignature;
79 #define	CBWSIGNATURE	0x43425355
80 	uDWord	dCBWTag;
81 	uDWord	dCBWDataTransferLength;
82 	uByte	bCBWFlags;
83 #define	CBWFLAGS_OUT	0x00
84 #define	CBWFLAGS_IN	0x80
85 	uByte	bCBWLUN;
86 	uByte	bCDBLength;
87 #define	CBWCDBLENGTH	16
88 	uByte	CBWCDB[CBWCDBLENGTH];
89 } __packed;
90 
91 /* Command Status Wrapper */
92 struct bbb_csw {
93 	uDWord	dCSWSignature;
94 #define	CSWSIGNATURE	0x53425355
95 	uDWord	dCSWTag;
96 	uDWord	dCSWDataResidue;
97 	uByte	bCSWStatus;
98 #define	CSWSTATUS_GOOD	0x0
99 #define	CSWSTATUS_FAILED	0x1
100 #define	CSWSTATUS_PHASE	0x2
101 } __packed;
102 
103 struct bbb_transfer {
104 	struct mtx mtx;
105 	struct cv cv;
106 	struct bbb_cbw cbw;
107 	struct bbb_csw csw;
108 
109 	struct usb2_xfer *xfer[ST_MAX];
110 
111 	uint8_t *data_ptr;
112 
113 	uint32_t data_len;		/* bytes */
114 	uint32_t data_rem;		/* bytes */
115 	uint32_t data_timeout;		/* ms */
116 	uint32_t actlen;		/* bytes */
117 
118 	uint8_t	cmd_len;		/* bytes */
119 	uint8_t	dir;
120 	uint8_t	lun;
121 	uint8_t	state;
122 	uint8_t	error;
123 	uint8_t	status_try;
124 
125 	uint8_t	buffer[256];
126 };
127 
128 static usb2_callback_t bbb_command_callback;
129 static usb2_callback_t bbb_data_read_callback;
130 static usb2_callback_t bbb_data_rd_cs_callback;
131 static usb2_callback_t bbb_data_write_callback;
132 static usb2_callback_t bbb_data_wr_cs_callback;
133 static usb2_callback_t bbb_status_callback;
134 
135 static const struct usb2_config bbb_config[ST_MAX] = {
136 
137 	[ST_COMMAND] = {
138 		.type = UE_BULK,
139 		.endpoint = UE_ADDR_ANY,
140 		.direction = UE_DIR_OUT,
141 		.mh.bufsize = sizeof(struct bbb_cbw),
142 		.mh.flags = {},
143 		.mh.callback = &bbb_command_callback,
144 		.mh.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
145 	},
146 
147 	[ST_DATA_RD] = {
148 		.type = UE_BULK,
149 		.endpoint = UE_ADDR_ANY,
150 		.direction = UE_DIR_IN,
151 		.mh.bufsize = BULK_SIZE,
152 		.mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
153 		.mh.callback = &bbb_data_read_callback,
154 		.mh.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
155 	},
156 
157 	[ST_DATA_RD_CS] = {
158 		.type = UE_CONTROL,
159 		.endpoint = 0x00,	/* Control pipe */
160 		.direction = UE_DIR_ANY,
161 		.mh.bufsize = sizeof(struct usb2_device_request),
162 		.mh.flags = {},
163 		.mh.callback = &bbb_data_rd_cs_callback,
164 		.mh.timeout = 1 * USB_MS_HZ,	/* 1 second  */
165 	},
166 
167 	[ST_DATA_WR] = {
168 		.type = UE_BULK,
169 		.endpoint = UE_ADDR_ANY,
170 		.direction = UE_DIR_OUT,
171 		.mh.bufsize = BULK_SIZE,
172 		.mh.flags = {.proxy_buffer = 1,},
173 		.mh.callback = &bbb_data_write_callback,
174 		.mh.timeout = 4 * USB_MS_HZ,	/* 4 seconds */
175 	},
176 
177 	[ST_DATA_WR_CS] = {
178 		.type = UE_CONTROL,
179 		.endpoint = 0x00,	/* Control pipe */
180 		.direction = UE_DIR_ANY,
181 		.mh.bufsize = sizeof(struct usb2_device_request),
182 		.mh.flags = {},
183 		.mh.callback = &bbb_data_wr_cs_callback,
184 		.mh.timeout = 1 * USB_MS_HZ,	/* 1 second  */
185 	},
186 
187 	[ST_STATUS] = {
188 		.type = UE_BULK,
189 		.endpoint = UE_ADDR_ANY,
190 		.direction = UE_DIR_IN,
191 		.mh.bufsize = sizeof(struct bbb_csw),
192 		.mh.flags = {.short_xfer_ok = 1,},
193 		.mh.callback = &bbb_status_callback,
194 		.mh.timeout = 1 * USB_MS_HZ,	/* 1 second  */
195 	},
196 };
197 
198 static void
199 bbb_done(struct bbb_transfer *sc, uint8_t error)
200 {
201 	struct usb2_xfer *xfer;
202 
203 	xfer = sc->xfer[sc->state];
204 
205 	/* verify the error code */
206 
207 	if (error) {
208 		switch (USB_GET_STATE(xfer)) {
209 		case USB_ST_SETUP:
210 		case USB_ST_TRANSFERRED:
211 			error = 1;
212 			break;
213 		default:
214 			error = 2;
215 			break;
216 		}
217 	}
218 	sc->error = error;
219 	sc->state = ST_COMMAND;
220 	sc->status_try = 1;
221 	usb2_cv_signal(&sc->cv);
222 }
223 
224 static void
225 bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index)
226 {
227 	sc->state = xfer_index;
228 	usb2_transfer_start(sc->xfer[xfer_index]);
229 }
230 
231 static void
232 bbb_data_clear_stall_callback(struct usb2_xfer *xfer,
233     uint8_t next_xfer, uint8_t stall_xfer)
234 {
235 	struct bbb_transfer *sc = xfer->priv_sc;
236 
237 	if (usb2_clear_stall_callback(xfer, sc->xfer[stall_xfer])) {
238 		switch (USB_GET_STATE(xfer)) {
239 		case USB_ST_SETUP:
240 		case USB_ST_TRANSFERRED:
241 			bbb_transfer_start(sc, next_xfer);
242 			break;
243 		default:
244 			bbb_done(sc, 1);
245 			break;
246 		}
247 	}
248 }
249 
250 static void
251 bbb_command_callback(struct usb2_xfer *xfer)
252 {
253 	struct bbb_transfer *sc = xfer->priv_sc;
254 	uint32_t tag;
255 
256 	switch (USB_GET_STATE(xfer)) {
257 	case USB_ST_TRANSFERRED:
258 		bbb_transfer_start
259 		    (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD :
260 		    (sc->dir == DIR_OUT) ? ST_DATA_WR :
261 		    ST_STATUS));
262 		break;
263 
264 	case USB_ST_SETUP:
265 		sc->status_try = 0;
266 		tag = UGETDW(sc->cbw.dCBWTag) + 1;
267 		USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE);
268 		USETDW(sc->cbw.dCBWTag, tag);
269 		USETDW(sc->cbw.dCBWDataTransferLength, sc->data_len);
270 		sc->cbw.bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT);
271 		sc->cbw.bCBWLUN = sc->lun;
272 		sc->cbw.bCDBLength = sc->cmd_len;
273 		if (sc->cbw.bCDBLength > sizeof(sc->cbw.CBWCDB)) {
274 			sc->cbw.bCDBLength = sizeof(sc->cbw.CBWCDB);
275 			DPRINTFN(0, "Truncating long command!\n");
276 		}
277 		xfer->frlengths[0] = sizeof(sc->cbw);
278 
279 		usb2_set_frame_data(xfer, &sc->cbw, 0);
280 		usb2_start_hardware(xfer);
281 		break;
282 
283 	default:			/* Error */
284 		bbb_done(sc, 1);
285 		break;
286 	}
287 }
288 
289 static void
290 bbb_data_read_callback(struct usb2_xfer *xfer)
291 {
292 	struct bbb_transfer *sc = xfer->priv_sc;
293 	uint32_t max_bulk = xfer->max_data_length;
294 
295 	switch (USB_GET_STATE(xfer)) {
296 	case USB_ST_TRANSFERRED:
297 		sc->data_rem -= xfer->actlen;
298 		sc->data_ptr += xfer->actlen;
299 		sc->actlen += xfer->actlen;
300 
301 		if (xfer->actlen < xfer->sumlen) {
302 			/* short transfer */
303 			sc->data_rem = 0;
304 		}
305 	case USB_ST_SETUP:
306 		DPRINTF("max_bulk=%d, data_rem=%d\n",
307 		    max_bulk, sc->data_rem);
308 
309 		if (sc->data_rem == 0) {
310 			bbb_transfer_start(sc, ST_STATUS);
311 			break;
312 		}
313 		if (max_bulk > sc->data_rem) {
314 			max_bulk = sc->data_rem;
315 		}
316 		xfer->timeout = sc->data_timeout;
317 		xfer->frlengths[0] = max_bulk;
318 
319 		usb2_set_frame_data(xfer, sc->data_ptr, 0);
320 		usb2_start_hardware(xfer);
321 		break;
322 
323 	default:			/* Error */
324 		if (xfer->error == USB_ERR_CANCELLED) {
325 			bbb_done(sc, 1);
326 		} else {
327 			bbb_transfer_start(sc, ST_DATA_RD_CS);
328 		}
329 		break;
330 	}
331 }
332 
333 static void
334 bbb_data_rd_cs_callback(struct usb2_xfer *xfer)
335 {
336 	bbb_data_clear_stall_callback(xfer, ST_STATUS,
337 	    ST_DATA_RD);
338 }
339 
340 static void
341 bbb_data_write_callback(struct usb2_xfer *xfer)
342 {
343 	struct bbb_transfer *sc = xfer->priv_sc;
344 	uint32_t max_bulk = xfer->max_data_length;
345 
346 	switch (USB_GET_STATE(xfer)) {
347 	case USB_ST_TRANSFERRED:
348 		sc->data_rem -= xfer->actlen;
349 		sc->data_ptr += xfer->actlen;
350 		sc->actlen += xfer->actlen;
351 
352 		if (xfer->actlen < xfer->sumlen) {
353 			/* short transfer */
354 			sc->data_rem = 0;
355 		}
356 	case USB_ST_SETUP:
357 		DPRINTF("max_bulk=%d, data_rem=%d\n",
358 		    max_bulk, sc->data_rem);
359 
360 		if (sc->data_rem == 0) {
361 			bbb_transfer_start(sc, ST_STATUS);
362 			return;
363 		}
364 		if (max_bulk > sc->data_rem) {
365 			max_bulk = sc->data_rem;
366 		}
367 		xfer->timeout = sc->data_timeout;
368 		xfer->frlengths[0] = max_bulk;
369 
370 		usb2_set_frame_data(xfer, sc->data_ptr, 0);
371 		usb2_start_hardware(xfer);
372 		return;
373 
374 	default:			/* Error */
375 		if (xfer->error == USB_ERR_CANCELLED) {
376 			bbb_done(sc, 1);
377 		} else {
378 			bbb_transfer_start(sc, ST_DATA_WR_CS);
379 		}
380 		return;
381 
382 	}
383 }
384 
385 static void
386 bbb_data_wr_cs_callback(struct usb2_xfer *xfer)
387 {
388 	bbb_data_clear_stall_callback(xfer, ST_STATUS,
389 	    ST_DATA_WR);
390 }
391 
392 static void
393 bbb_status_callback(struct usb2_xfer *xfer)
394 {
395 	struct bbb_transfer *sc = xfer->priv_sc;
396 
397 	switch (USB_GET_STATE(xfer)) {
398 	case USB_ST_TRANSFERRED:
399 
400 		/* very simple status check */
401 
402 		if (xfer->actlen < sizeof(sc->csw)) {
403 			bbb_done(sc, 1);/* error */
404 		} else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) {
405 			bbb_done(sc, 0);/* success */
406 		} else {
407 			bbb_done(sc, 1);/* error */
408 		}
409 		break;
410 
411 	case USB_ST_SETUP:
412 		xfer->frlengths[0] = sizeof(sc->csw);
413 
414 		usb2_set_frame_data(xfer, &sc->csw, 0);
415 		usb2_start_hardware(xfer);
416 		break;
417 
418 	default:
419 		DPRINTFN(0, "Failed to read CSW: %s, try %d\n",
420 		    usb2_errstr(xfer->error), sc->status_try);
421 
422 		if ((xfer->error == USB_ERR_CANCELLED) ||
423 		    (sc->status_try)) {
424 			bbb_done(sc, 1);
425 		} else {
426 			sc->status_try = 1;
427 			bbb_transfer_start(sc, ST_DATA_RD_CS);
428 		}
429 		break;
430 	}
431 }
432 
433 /*------------------------------------------------------------------------*
434  *	bbb_command_start - execute a SCSI command synchronously
435  *
436  * Return values
437  * 0: Success
438  * Else: Failure
439  *------------------------------------------------------------------------*/
440 static uint8_t
441 bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun,
442     void *data_ptr, uint32_t data_len, uint8_t cmd_len,
443     uint32_t data_timeout)
444 {
445 	sc->lun = lun;
446 	sc->dir = data_len ? dir : DIR_NONE;
447 	sc->data_ptr = data_ptr;
448 	sc->data_len = data_len;
449 	sc->data_rem = data_len;
450 	sc->data_timeout = (data_timeout + USB_MS_HZ);
451 	sc->actlen = 0;
452 	sc->cmd_len = cmd_len;
453 
454 	usb2_transfer_start(sc->xfer[sc->state]);
455 
456 	while (usb2_transfer_pending(sc->xfer[sc->state])) {
457 		usb2_cv_wait(&sc->cv, &sc->mtx);
458 	}
459 	return (sc->error);
460 }
461 
462 /*------------------------------------------------------------------------*
463  *	usb2_test_autoinstall
464  *
465  * Return values:
466  * 0: This interface is an auto install disk (CD-ROM)
467  * Else: Not an auto install disk.
468  *------------------------------------------------------------------------*/
469 usb2_error_t
470 usb2_test_autoinstall(struct usb2_device *udev, uint8_t iface_index,
471     uint8_t do_eject)
472 {
473 	struct usb2_interface *iface;
474 	struct usb2_interface_descriptor *id;
475 	usb2_error_t err;
476 	uint8_t timeout;
477 	uint8_t sid_type;
478 	struct bbb_transfer *sc;
479 
480 	if (udev == NULL) {
481 		return (USB_ERR_INVAL);
482 	}
483 	iface = usb2_get_iface(udev, iface_index);
484 	if (iface == NULL) {
485 		return (USB_ERR_INVAL);
486 	}
487 	id = iface->idesc;
488 	if (id == NULL) {
489 		return (USB_ERR_INVAL);
490 	}
491 	if (id->bInterfaceClass != UICLASS_MASS) {
492 		return (USB_ERR_INVAL);
493 	}
494 	switch (id->bInterfaceSubClass) {
495 	case UISUBCLASS_SCSI:
496 	case UISUBCLASS_UFI:
497 		break;
498 	default:
499 		return (USB_ERR_INVAL);
500 	}
501 
502 	switch (id->bInterfaceProtocol) {
503 	case UIPROTO_MASS_BBB_OLD:
504 	case UIPROTO_MASS_BBB:
505 		break;
506 	default:
507 		return (USB_ERR_INVAL);
508 	}
509 
510 	sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO);
511 	if (sc == NULL) {
512 		return (USB_ERR_NOMEM);
513 	}
514 	mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF);
515 	usb2_cv_init(&sc->cv, "WBBB");
516 
517 	err = usb2_transfer_setup(udev,
518 	    &iface_index, sc->xfer, bbb_config,
519 	    ST_MAX, sc, &sc->mtx);
520 
521 	if (err) {
522 		goto done;
523 	}
524 	mtx_lock(&sc->mtx);
525 
526 	timeout = 4;			/* tries */
527 
528 repeat_inquiry:
529 
530 	sc->cbw.CBWCDB[0] = 0x12;	/* INQUIRY */
531 	sc->cbw.CBWCDB[1] = 0;
532 	sc->cbw.CBWCDB[2] = 0;
533 	sc->cbw.CBWCDB[3] = 0;
534 	sc->cbw.CBWCDB[4] = 0x24;	/* length */
535 	sc->cbw.CBWCDB[5] = 0;
536 	err = bbb_command_start(sc, DIR_IN, 0,
537 	    sc->buffer, 0x24, 6, USB_MS_HZ);
538 
539 	if ((sc->actlen != 0) && (err == 0)) {
540 		sid_type = sc->buffer[0] & 0x1F;
541 		if (sid_type == 0x05) {
542 			/* CD-ROM */
543 			if (do_eject) {
544 				/* 0: opcode: SCSI START/STOP */
545 				sc->cbw.CBWCDB[0] = 0x1b;
546 				/* 1: byte2: Not immediate */
547 				sc->cbw.CBWCDB[1] = 0x00;
548 				/* 2..3: reserved */
549 				sc->cbw.CBWCDB[2] = 0x00;
550 				sc->cbw.CBWCDB[3] = 0x00;
551 				/* 4: Load/Eject command */
552 				sc->cbw.CBWCDB[4] = 0x02;
553 				/* 5: control */
554 				sc->cbw.CBWCDB[5] = 0x00;
555 				err = bbb_command_start(sc, DIR_OUT, 0,
556 				    NULL, 0, 6, USB_MS_HZ);
557 
558 				DPRINTFN(0, "Eject CD command "
559 				    "status: %s\n", usb2_errstr(err));
560 			}
561 			err = 0;
562 			goto done;
563 		}
564 	} else if ((err != 2) && --timeout) {
565 		usb2_pause_mtx(&sc->mtx, hz);
566 		goto repeat_inquiry;
567 	}
568 	err = USB_ERR_INVAL;
569 	goto done;
570 
571 done:
572 	mtx_unlock(&sc->mtx);
573 	usb2_transfer_unsetup(sc->xfer, ST_MAX);
574 	mtx_destroy(&sc->mtx);
575 	usb2_cv_destroy(&sc->cv);
576 	free(sc, M_USB);
577 	return (err);
578 }
579