xref: /openbsd-src/sys/dev/usb/umass_scsi.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: umass_scsi.c,v 1.43 2015/12/16 14:50:26 mpi Exp $ */
2 /*	$NetBSD: umass_scsipi.c,v 1.9 2003/02/16 23:14:08 augustss Exp $	*/
3 /*
4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Lennart Augustsson (lennart@augustsson.net) at
9  * Carlstedt Research & Technology.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/conf.h>
37 #include <sys/buf.h>
38 #include <sys/device.h>
39 #include <sys/ioctl.h>
40 #include <sys/malloc.h>
41 
42 #include <dev/usb/usb.h>
43 #include <dev/usb/usbdi.h>
44 #include <dev/usb/usbdi_util.h>
45 
46 #include <dev/usb/umassvar.h>
47 #include <dev/usb/umass_scsi.h>
48 
49 #include <scsi/scsi_all.h>
50 #include <scsi/scsiconf.h>
51 #include <scsi/scsi_disk.h>
52 #include <machine/bus.h>
53 
54 struct umass_scsi_softc {
55 	struct device		*sc_child;
56 	struct scsi_link	sc_link;
57 	struct scsi_iopool	sc_iopool;
58 	int			sc_open;
59 
60 	struct scsi_sense	sc_sense_cmd;
61 };
62 
63 
64 #define UMASS_SCSIID_HOST	0x00
65 #define UMASS_SCSIID_DEVICE	0x01
66 
67 int umass_scsi_probe(struct scsi_link *);
68 void umass_scsi_cmd(struct scsi_xfer *);
69 void umass_scsi_minphys(struct buf *, struct scsi_link *);
70 
71 struct scsi_adapter umass_scsi_switch = {
72 	umass_scsi_cmd,
73 	umass_scsi_minphys,
74 	umass_scsi_probe
75 };
76 
77 void umass_scsi_cb(struct umass_softc *sc, void *priv, int residue,
78 		   int status);
79 void umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue,
80 			 int status);
81 struct umass_scsi_softc *umass_scsi_setup(struct umass_softc *);
82 
83 void *umass_io_get(void *);
84 void umass_io_put(void *, void *);
85 
86 int
87 umass_scsi_attach(struct umass_softc *sc)
88 {
89 	struct scsibus_attach_args saa;
90 	struct umass_scsi_softc *scbus;
91 
92 	scbus = umass_scsi_setup(sc);
93 	scbus->sc_link.adapter_target = UMASS_SCSIID_HOST;
94 	scbus->sc_link.luns = sc->maxlun + 1;
95 	scbus->sc_link.flags &= ~SDEV_ATAPI;
96 	scbus->sc_link.flags |= SDEV_UMASS;
97 
98 	bzero(&saa, sizeof(saa));
99 	saa.saa_sc_link = &scbus->sc_link;
100 
101 	DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: SCSI\n"
102 			     "sc = 0x%p, scbus = 0x%p\n",
103 			     sc->sc_dev.dv_xname, sc, scbus));
104 
105 	sc->sc_refcnt++;
106 	scbus->sc_child = config_found((struct device *)sc, &saa, scsiprint);
107 	if (--sc->sc_refcnt < 0)
108 		usb_detach_wakeup(&sc->sc_dev);
109 
110 	return (0);
111 }
112 
113 int
114 umass_atapi_attach(struct umass_softc *sc)
115 {
116 	struct scsibus_attach_args saa;
117 	struct umass_scsi_softc *scbus;
118 
119 	scbus = umass_scsi_setup(sc);
120 	scbus->sc_link.adapter_target = UMASS_SCSIID_HOST;
121 	scbus->sc_link.luns = 1;
122 	scbus->sc_link.openings = 1;
123 	scbus->sc_link.flags |= SDEV_ATAPI;
124 
125 	bzero(&saa, sizeof(saa));
126 	saa.saa_sc_link = &scbus->sc_link;
127 
128 	DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: ATAPI\n"
129 			     "sc = 0x%p, scbus = 0x%p\n",
130 			     sc->sc_dev.dv_xname, sc, scbus));
131 
132 	sc->sc_refcnt++;
133 	scbus->sc_child = config_found((struct device *)sc, &saa, scsiprint);
134 	if (--sc->sc_refcnt < 0)
135 		usb_detach_wakeup(&sc->sc_dev);
136 
137 	return (0);
138 }
139 
140 struct umass_scsi_softc *
141 umass_scsi_setup(struct umass_softc *sc)
142 {
143 	struct umass_scsi_softc *scbus;
144 
145 	scbus = malloc(sizeof(*scbus), M_DEVBUF, M_WAITOK | M_ZERO);
146 
147 	sc->bus = scbus;
148 
149 	scsi_iopool_init(&scbus->sc_iopool, scbus, umass_io_get, umass_io_put);
150 
151 	/* Fill in the link. */
152 	scbus->sc_link.adapter_buswidth = 2;
153 	scbus->sc_link.adapter = &umass_scsi_switch;
154 	scbus->sc_link.adapter_softc = sc;
155 	scbus->sc_link.openings = 1;
156 	scbus->sc_link.quirks |= SDEV_ONLYBIG | sc->sc_busquirks;
157 	scbus->sc_link.pool = &scbus->sc_iopool;
158 
159 	return (scbus);
160 }
161 
162 int
163 umass_scsi_detach(struct umass_softc *sc, int flags)
164 {
165 	struct umass_scsi_softc *scbus = sc->bus;
166 	int rv = 0;
167 
168 	if (scbus != NULL) {
169 		if (scbus->sc_child != NULL)
170 			rv = config_detach(scbus->sc_child, flags);
171 		free(scbus, M_DEVBUF, sizeof(*scbus));
172 		sc->bus = NULL;
173 	}
174 
175 	return (rv);
176 }
177 
178 int
179 umass_scsi_probe(struct scsi_link *link)
180 {
181 	struct umass_softc *sc = link->adapter_softc;
182 	struct usb_device_info udi;
183 	size_t len;
184 
185 	/* dont fake devids when more than one scsi device can attach. */
186 	if (sc->maxlun > 0)
187 		return (0);
188 
189 	usbd_fill_deviceinfo(sc->sc_udev, &udi, 1);
190 
191 	/*
192 	 * Create a fake devid using the vendor and product ids and the last
193 	 * 12 characters of serial number, as recommended by Section 4.1.1 of
194 	 * the USB Mass Storage Class - Bulk Only Transport spec.
195 	 */
196 	len = strlen(udi.udi_serial);
197 	if (len >= 12) {
198 		char buf[21];
199 		snprintf(buf, sizeof(buf), "%04x%04x%s", udi.udi_vendorNo,
200 		    udi.udi_productNo, udi.udi_serial + len - 12);
201 		link->id = devid_alloc(DEVID_SERIAL, DEVID_F_PRINT,
202 		    sizeof(buf) - 1, buf);
203 	}
204 
205 	return (0);
206 }
207 
208 void
209 umass_scsi_cmd(struct scsi_xfer *xs)
210 {
211 	struct scsi_link *sc_link = xs->sc_link;
212 	struct umass_softc *sc = sc_link->adapter_softc;
213 	struct scsi_generic *cmd;
214 	int cmdlen, dir;
215 
216 #ifdef UMASS_DEBUG
217 	microtime(&sc->tv);
218 #endif
219 
220 	DIF(UDMASS_UPPER, sc_link->flags |= SCSIDEBUG_LEVEL);
221 
222 	DPRINTF(UDMASS_CMD, ("%s: umass_scsi_cmd: at %lld.%06ld: %d:%d "
223 		"xs=%p cmd=0x%02x datalen=%d (quirks=0x%x, poll=%d)\n",
224 		sc->sc_dev.dv_xname, (long long)sc->tv.tv_sec, sc->tv.tv_usec,
225 		sc_link->target, sc_link->lun, xs, xs->cmd->opcode,
226 		xs->datalen, sc_link->quirks, xs->flags & SCSI_POLL));
227 
228 	if (usbd_is_dying(sc->sc_udev)) {
229 		xs->error = XS_DRIVER_STUFFUP;
230 		goto done;
231 	}
232 
233 #if defined(UMASS_DEBUG)
234 	if (sc_link->target != UMASS_SCSIID_DEVICE) {
235 		DPRINTF(UDMASS_SCSI, ("%s: wrong SCSI ID %d\n",
236 			sc->sc_dev.dv_xname, sc_link->target));
237 		xs->error = XS_DRIVER_STUFFUP;
238 		goto done;
239 	}
240 #endif
241 
242 	cmd = xs->cmd;
243 	cmdlen = xs->cmdlen;
244 
245 	dir = DIR_NONE;
246 	if (xs->datalen) {
247 		switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
248 		case SCSI_DATA_IN:
249 			dir = DIR_IN;
250 			break;
251 		case SCSI_DATA_OUT:
252 			dir = DIR_OUT;
253 			break;
254 		}
255 	}
256 
257 	if (xs->datalen > UMASS_MAX_TRANSFER_SIZE) {
258 		printf("umass_cmd: large datalen, %d\n", xs->datalen);
259 		xs->error = XS_DRIVER_STUFFUP;
260 		goto done;
261 	}
262 
263 	if (xs->flags & SCSI_POLL) {
264 		DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: sync dir=%d\n", dir));
265 		usbd_set_polling(sc->sc_udev, 1);
266 		sc->sc_xfer_flags = USBD_SYNCHRONOUS;
267 		sc->polled_xfer_status = USBD_INVAL;
268 		sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen,
269 					  xs->data, xs->datalen, dir,
270 					  xs->timeout, umass_scsi_cb, xs);
271 		sc->sc_xfer_flags = 0;
272 		DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: done err=%d\n",
273 				      sc->polled_xfer_status));
274 		usbd_set_polling(sc->sc_udev, 0);
275 		/* scsi_done() has already been called. */
276 		return;
277 	} else {
278 		DPRINTF(UDMASS_SCSI,
279 			("umass_scsi_cmd: async dir=%d, cmdlen=%d"
280 			 " datalen=%d\n",
281 			 dir, cmdlen, xs->datalen));
282 		sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen,
283 					  xs->data, xs->datalen, dir,
284 					  xs->timeout, umass_scsi_cb, xs);
285 		/* scsi_done() has already been called. */
286 		return;
287 	}
288 
289 	/* Return if command finishes early. */
290  done:
291 	scsi_done(xs);
292 }
293 
294 void
295 umass_scsi_minphys(struct buf *bp, struct scsi_link *sl)
296 {
297 	if (bp->b_bcount > UMASS_MAX_TRANSFER_SIZE)
298 		bp->b_bcount = UMASS_MAX_TRANSFER_SIZE;
299 
300 	minphys(bp);
301 }
302 
303 void
304 umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, int status)
305 {
306 	struct umass_scsi_softc *scbus = sc->bus;
307 	struct scsi_xfer *xs = priv;
308 	struct scsi_link *link = xs->sc_link;
309 	int cmdlen;
310 #ifdef UMASS_DEBUG
311 	struct timeval tv;
312 	u_int delta;
313 	microtime(&tv);
314 	delta = (tv.tv_sec - sc->tv.tv_sec) * 1000000 +
315 		tv.tv_usec - sc->tv.tv_usec;
316 #endif
317 
318 	DPRINTF(UDMASS_CMD,
319 		("umass_scsi_cb: at %lld.%06ld, delta=%u: xs=%p residue=%d"
320 		 " status=%d\n", (long long)tv.tv_sec, tv.tv_usec, delta, xs, residue,
321 		 status));
322 
323 	xs->resid = residue;
324 
325 	switch (status) {
326 	case STATUS_CMD_OK:
327 		xs->error = XS_NOERROR;
328 		break;
329 
330 	case STATUS_CMD_UNKNOWN:
331 		DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd unknown\n"));
332 		/* we can't issue REQUEST SENSE */
333 		if (xs->sc_link->quirks & ADEV_NOSENSE) {
334 			/*
335 			 * If no residue and no other USB error,
336 			 * command succeeded.
337 			 */
338 			if (residue == 0) {
339 				xs->error = XS_NOERROR;
340 				break;
341 			}
342 
343 			/*
344 			 * Some devices return a short INQUIRY
345 			 * response, omitting response data from the
346 			 * "vendor specific data" on...
347 			 */
348 			if (xs->cmd->opcode == INQUIRY &&
349 			    residue < xs->datalen) {
350 				xs->error = XS_NOERROR;
351 				break;
352 			}
353 
354 			xs->error = XS_DRIVER_STUFFUP;
355 			break;
356 		}
357 		/* FALLTHROUGH */
358 	case STATUS_CMD_FAILED:
359 		DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd failed for "
360 		    "scsi op 0x%02x\n", xs->cmd->opcode));
361 		/* fetch sense data */
362 		sc->sc_sense = 1;
363 		memset(&scbus->sc_sense_cmd, 0, sizeof(scbus->sc_sense_cmd));
364 		scbus->sc_sense_cmd.opcode = REQUEST_SENSE;
365 		scbus->sc_sense_cmd.byte2 = link->lun << SCSI_CMD_LUN_SHIFT;
366 		scbus->sc_sense_cmd.length = sizeof(xs->sense);
367 
368 		cmdlen = sizeof(scbus->sc_sense_cmd);
369 		if (xs->flags & SCSI_POLL) {
370 			usbd_set_polling(sc->sc_udev, 1);
371 			sc->sc_xfer_flags = USBD_SYNCHRONOUS;
372 			sc->polled_xfer_status = USBD_INVAL;
373 		}
374 		/* scsi_done() has already been called. */
375 		sc->sc_methods->wire_xfer(sc, link->lun,
376 					  &scbus->sc_sense_cmd, cmdlen,
377 					  &xs->sense, sizeof(xs->sense),
378 					  DIR_IN, xs->timeout,
379 					  umass_scsi_sense_cb, xs);
380 		if (xs->flags & SCSI_POLL) {
381 			sc->sc_xfer_flags = 0;
382 			usbd_set_polling(sc->sc_udev, 0);
383 		}
384 		return;
385 
386 	case STATUS_WIRE_FAILED:
387 		xs->error = XS_RESET;
388 		break;
389 
390 	default:
391 		panic("%s: Unknown status %d in umass_scsi_cb",
392 		      sc->sc_dev.dv_xname, status);
393 	}
394 
395 	DPRINTF(UDMASS_CMD,("umass_scsi_cb: at %lld.%06ld: return error=%d, "
396 			    "status=0x%x resid=%zu\n",
397 			    (long long)tv.tv_sec, tv.tv_usec,
398 			    xs->error, xs->status, xs->resid));
399 
400 	if ((xs->flags & SCSI_POLL) && (xs->error == XS_NOERROR)) {
401 		switch (sc->polled_xfer_status) {
402 		case USBD_NORMAL_COMPLETION:
403 			xs->error = XS_NOERROR;
404 			break;
405 		case USBD_TIMEOUT:
406 			xs->error = XS_TIMEOUT;
407 			break;
408 		default:
409 			xs->error = XS_DRIVER_STUFFUP;
410 			break;
411 		}
412 	}
413 
414 	scsi_done(xs);
415 }
416 
417 /*
418  * Finalise a completed autosense operation
419  */
420 void
421 umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue,
422 		    int status)
423 {
424 	struct scsi_xfer *xs = priv;
425 
426 	DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: xs=%p residue=%d "
427 		"status=%d\n", xs, residue, status));
428 
429 	sc->sc_sense = 0;
430 	switch (status) {
431 	case STATUS_CMD_OK:
432 	case STATUS_CMD_UNKNOWN:
433 		/* getting sense data succeeded */
434 		if (residue == 0 || residue == 14)/* XXX */
435 			xs->error = XS_SENSE;
436 		else
437 			xs->error = XS_SHORTSENSE;
438 		break;
439 	default:
440 		DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n",
441 			sc->sc_dev.dv_xname, status));
442 		xs->error = XS_DRIVER_STUFFUP;
443 		break;
444 	}
445 
446 	DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: return xs->error=%d, "
447 		"xs->flags=0x%x xs->resid=%zu\n", xs->error, xs->status,
448 		xs->resid));
449 
450 	if ((xs->flags & SCSI_POLL) && (xs->error == XS_NOERROR)) {
451 		switch (sc->polled_xfer_status) {
452 		case USBD_NORMAL_COMPLETION:
453 			xs->error = XS_NOERROR;
454 			break;
455 		case USBD_TIMEOUT:
456 			xs->error = XS_TIMEOUT;
457 			break;
458 		default:
459 			xs->error = XS_DRIVER_STUFFUP;
460 			break;
461 		}
462 	}
463 
464 	scsi_done(xs);
465 }
466 
467 void *
468 umass_io_get(void *cookie)
469 {
470 	struct umass_scsi_softc *scbus = cookie;
471 	void *io = NULL;
472 	int s;
473 
474 	s = splusb();
475 	if (!scbus->sc_open) {
476 		scbus->sc_open = 1;
477 		io = scbus; /* just has to be non-NULL */
478 	}
479 	splx(s);
480 
481 	return (io);
482 }
483 
484 void
485 umass_io_put(void *cookie, void *io)
486 {
487 	struct umass_scsi_softc *scbus = cookie;
488 	int s;
489 
490 	s = splusb();
491 	scbus->sc_open = 0;
492 	splx(s);
493 }
494