xref: /openbsd-src/sys/dev/usb/umass_scsi.c (revision ae3cb403620ab940fbaabb3055fac045a63d56b7)
1 /*	$OpenBSD: umass_scsi.c,v 1.45 2016/08/03 13:44:49 krw 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 void *umass_io_get(void *);
82 void umass_io_put(void *, void *);
83 
84 int
85 umass_scsi_attach(struct umass_softc *sc)
86 {
87 	struct scsibus_attach_args saa;
88 	struct umass_scsi_softc *scbus;
89 
90 	scbus = malloc(sizeof(*scbus), M_DEVBUF, M_WAITOK | M_ZERO);
91 
92 	sc->bus = scbus;
93 
94 	scsi_iopool_init(&scbus->sc_iopool, scbus, umass_io_get, umass_io_put);
95 
96 	/* Fill in the link. */
97 	scbus->sc_link.adapter_buswidth = 2;
98 	scbus->sc_link.adapter = &umass_scsi_switch;
99 	scbus->sc_link.adapter_softc = sc;
100 	scbus->sc_link.adapter_target = UMASS_SCSIID_HOST;
101 	scbus->sc_link.openings = 1;
102 	scbus->sc_link.quirks = SDEV_ONLYBIG | sc->sc_busquirks;
103 	scbus->sc_link.pool = &scbus->sc_iopool;
104 	scbus->sc_link.luns = sc->maxlun + 1;
105 	scbus->sc_link.flags = SDEV_UMASS;
106 
107 	bzero(&saa, sizeof(saa));
108 	saa.saa_sc_link = &scbus->sc_link;
109 
110 	switch (sc->sc_cmd) {
111 	case UMASS_CPROTO_RBC:
112 	case UMASS_CPROTO_SCSI:
113 		DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: SCSI\n"
114 				     "sc = 0x%p, scbus = 0x%p\n",
115 				     sc->sc_dev.dv_xname, sc, scbus));
116 		break;
117 	case UMASS_CPROTO_UFI:
118 	case UMASS_CPROTO_ATAPI:
119 		scbus->sc_link.flags |= SDEV_ATAPI;
120 		DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: ATAPI\n"
121 				     "sc = 0x%p, scbus = 0x%p\n",
122 				     sc->sc_dev.dv_xname, sc, scbus));
123 		break;
124 	default:
125 		break;
126 	}
127 
128 	sc->sc_refcnt++;
129 	scbus->sc_child = config_found((struct device *)sc, &saa, scsiprint);
130 	if (--sc->sc_refcnt < 0)
131 		usb_detach_wakeup(&sc->sc_dev);
132 
133 	return (0);
134 }
135 
136 int
137 umass_scsi_detach(struct umass_softc *sc, int flags)
138 {
139 	struct umass_scsi_softc *scbus = sc->bus;
140 	int rv = 0;
141 
142 	if (scbus != NULL) {
143 		if (scbus->sc_child != NULL)
144 			rv = config_detach(scbus->sc_child, flags);
145 		free(scbus, M_DEVBUF, sizeof(*scbus));
146 		sc->bus = NULL;
147 	}
148 
149 	return (rv);
150 }
151 
152 int
153 umass_scsi_probe(struct scsi_link *link)
154 {
155 	struct umass_softc *sc = link->adapter_softc;
156 	struct usb_device_info udi;
157 	size_t len;
158 
159 	/* dont fake devids when more than one scsi device can attach. */
160 	if (sc->maxlun > 0)
161 		return (0);
162 
163 	usbd_fill_deviceinfo(sc->sc_udev, &udi, 1);
164 
165 	/*
166 	 * Create a fake devid using the vendor and product ids and the last
167 	 * 12 characters of serial number, as recommended by Section 4.1.1 of
168 	 * the USB Mass Storage Class - Bulk Only Transport spec.
169 	 */
170 	len = strlen(udi.udi_serial);
171 	if (len >= 12) {
172 		char buf[21];
173 		snprintf(buf, sizeof(buf), "%04x%04x%s", udi.udi_vendorNo,
174 		    udi.udi_productNo, udi.udi_serial + len - 12);
175 		link->id = devid_alloc(DEVID_SERIAL, DEVID_F_PRINT,
176 		    sizeof(buf) - 1, buf);
177 	}
178 
179 	return (0);
180 }
181 
182 void
183 umass_scsi_cmd(struct scsi_xfer *xs)
184 {
185 	struct scsi_link *sc_link = xs->sc_link;
186 	struct umass_softc *sc = sc_link->adapter_softc;
187 	struct scsi_generic *cmd;
188 	int cmdlen, dir;
189 
190 #ifdef UMASS_DEBUG
191 	microtime(&sc->tv);
192 #endif
193 
194 	DIF(UDMASS_UPPER, sc_link->flags |= SCSIDEBUG_LEVEL);
195 
196 	DPRINTF(UDMASS_CMD, ("%s: umass_scsi_cmd: at %lld.%06ld: %d:%d "
197 		"xs=%p cmd=0x%02x datalen=%d (quirks=0x%x, poll=%d)\n",
198 		sc->sc_dev.dv_xname, (long long)sc->tv.tv_sec, sc->tv.tv_usec,
199 		sc_link->target, sc_link->lun, xs, xs->cmd->opcode,
200 		xs->datalen, sc_link->quirks, xs->flags & SCSI_POLL));
201 
202 	if (usbd_is_dying(sc->sc_udev)) {
203 		xs->error = XS_DRIVER_STUFFUP;
204 		goto done;
205 	}
206 
207 #if defined(UMASS_DEBUG)
208 	if (sc_link->target != UMASS_SCSIID_DEVICE) {
209 		DPRINTF(UDMASS_SCSI, ("%s: wrong SCSI ID %d\n",
210 			sc->sc_dev.dv_xname, sc_link->target));
211 		xs->error = XS_DRIVER_STUFFUP;
212 		goto done;
213 	}
214 #endif
215 
216 	cmd = xs->cmd;
217 	cmdlen = xs->cmdlen;
218 
219 	dir = DIR_NONE;
220 	if (xs->datalen) {
221 		switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
222 		case SCSI_DATA_IN:
223 			dir = DIR_IN;
224 			break;
225 		case SCSI_DATA_OUT:
226 			dir = DIR_OUT;
227 			break;
228 		}
229 	}
230 
231 	if (xs->datalen > UMASS_MAX_TRANSFER_SIZE) {
232 		printf("umass_cmd: large datalen, %d\n", xs->datalen);
233 		xs->error = XS_DRIVER_STUFFUP;
234 		goto done;
235 	}
236 
237 	if (xs->flags & SCSI_POLL) {
238 		DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: sync dir=%d\n", dir));
239 		usbd_set_polling(sc->sc_udev, 1);
240 		sc->sc_xfer_flags = USBD_SYNCHRONOUS;
241 		sc->polled_xfer_status = USBD_INVAL;
242 		sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen,
243 					  xs->data, xs->datalen, dir,
244 					  xs->timeout, umass_scsi_cb, xs);
245 		sc->sc_xfer_flags = 0;
246 		DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: done err=%d\n",
247 				      sc->polled_xfer_status));
248 		usbd_set_polling(sc->sc_udev, 0);
249 		/* scsi_done() has already been called. */
250 		return;
251 	} else {
252 		DPRINTF(UDMASS_SCSI,
253 			("umass_scsi_cmd: async dir=%d, cmdlen=%d"
254 			 " datalen=%d\n",
255 			 dir, cmdlen, xs->datalen));
256 		sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen,
257 					  xs->data, xs->datalen, dir,
258 					  xs->timeout, umass_scsi_cb, xs);
259 		/* scsi_done() has already been called. */
260 		return;
261 	}
262 
263 	/* Return if command finishes early. */
264  done:
265 	scsi_done(xs);
266 }
267 
268 void
269 umass_scsi_minphys(struct buf *bp, struct scsi_link *sl)
270 {
271 	if (bp->b_bcount > UMASS_MAX_TRANSFER_SIZE)
272 		bp->b_bcount = UMASS_MAX_TRANSFER_SIZE;
273 
274 	minphys(bp);
275 }
276 
277 void
278 umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, int status)
279 {
280 	struct umass_scsi_softc *scbus = sc->bus;
281 	struct scsi_xfer *xs = priv;
282 	struct scsi_link *link = xs->sc_link;
283 	int cmdlen;
284 #ifdef UMASS_DEBUG
285 	struct timeval tv;
286 	u_int delta;
287 	microtime(&tv);
288 	delta = (tv.tv_sec - sc->tv.tv_sec) * 1000000 +
289 		tv.tv_usec - sc->tv.tv_usec;
290 #endif
291 
292 	DPRINTF(UDMASS_CMD,
293 		("umass_scsi_cb: at %lld.%06ld, delta=%u: xs=%p residue=%d"
294 		 " status=%d\n", (long long)tv.tv_sec, tv.tv_usec, delta, xs, residue,
295 		 status));
296 
297 	xs->resid = residue;
298 
299 	switch (status) {
300 	case STATUS_CMD_OK:
301 		xs->error = XS_NOERROR;
302 		break;
303 
304 	case STATUS_CMD_UNKNOWN:
305 		DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd unknown\n"));
306 		/* we can't issue REQUEST SENSE */
307 		if (xs->sc_link->quirks & ADEV_NOSENSE) {
308 			/*
309 			 * If no residue and no other USB error,
310 			 * command succeeded.
311 			 */
312 			if (residue == 0) {
313 				xs->error = XS_NOERROR;
314 				break;
315 			}
316 
317 			/*
318 			 * Some devices return a short INQUIRY
319 			 * response, omitting response data from the
320 			 * "vendor specific data" on...
321 			 */
322 			if (xs->cmd->opcode == INQUIRY &&
323 			    residue < xs->datalen) {
324 				xs->error = XS_NOERROR;
325 				break;
326 			}
327 
328 			xs->error = XS_DRIVER_STUFFUP;
329 			break;
330 		}
331 		/* FALLTHROUGH */
332 	case STATUS_CMD_FAILED:
333 		DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd failed for "
334 		    "scsi op 0x%02x\n", xs->cmd->opcode));
335 		/* fetch sense data */
336 		sc->sc_sense = 1;
337 		memset(&scbus->sc_sense_cmd, 0, sizeof(scbus->sc_sense_cmd));
338 		scbus->sc_sense_cmd.opcode = REQUEST_SENSE;
339 		scbus->sc_sense_cmd.byte2 = link->lun << SCSI_CMD_LUN_SHIFT;
340 		scbus->sc_sense_cmd.length = sizeof(xs->sense);
341 
342 		cmdlen = sizeof(scbus->sc_sense_cmd);
343 		if (xs->flags & SCSI_POLL) {
344 			usbd_set_polling(sc->sc_udev, 1);
345 			sc->sc_xfer_flags = USBD_SYNCHRONOUS;
346 			sc->polled_xfer_status = USBD_INVAL;
347 		}
348 		/* scsi_done() has already been called. */
349 		sc->sc_methods->wire_xfer(sc, link->lun,
350 					  &scbus->sc_sense_cmd, cmdlen,
351 					  &xs->sense, sizeof(xs->sense),
352 					  DIR_IN, xs->timeout,
353 					  umass_scsi_sense_cb, xs);
354 		if (xs->flags & SCSI_POLL) {
355 			sc->sc_xfer_flags = 0;
356 			usbd_set_polling(sc->sc_udev, 0);
357 		}
358 		return;
359 
360 	case STATUS_WIRE_FAILED:
361 		xs->error = XS_RESET;
362 		break;
363 
364 	default:
365 		panic("%s: Unknown status %d in umass_scsi_cb",
366 		      sc->sc_dev.dv_xname, status);
367 	}
368 
369 	DPRINTF(UDMASS_CMD,("umass_scsi_cb: at %lld.%06ld: return error=%d, "
370 			    "status=0x%x resid=%zu\n",
371 			    (long long)tv.tv_sec, tv.tv_usec,
372 			    xs->error, xs->status, xs->resid));
373 
374 	if ((xs->flags & SCSI_POLL) && (xs->error == XS_NOERROR)) {
375 		switch (sc->polled_xfer_status) {
376 		case USBD_NORMAL_COMPLETION:
377 			xs->error = XS_NOERROR;
378 			break;
379 		case USBD_TIMEOUT:
380 			xs->error = XS_TIMEOUT;
381 			break;
382 		default:
383 			xs->error = XS_DRIVER_STUFFUP;
384 			break;
385 		}
386 	}
387 
388 	scsi_done(xs);
389 }
390 
391 /*
392  * Finalise a completed autosense operation
393  */
394 void
395 umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue,
396 		    int status)
397 {
398 	struct scsi_xfer *xs = priv;
399 
400 	DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: xs=%p residue=%d "
401 		"status=%d\n", xs, residue, status));
402 
403 	sc->sc_sense = 0;
404 	switch (status) {
405 	case STATUS_CMD_OK:
406 	case STATUS_CMD_UNKNOWN:
407 		/* getting sense data succeeded */
408 		if (residue == 0 || residue == 14)/* XXX */
409 			xs->error = XS_SENSE;
410 		else
411 			xs->error = XS_SHORTSENSE;
412 		break;
413 	default:
414 		DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n",
415 			sc->sc_dev.dv_xname, status));
416 		xs->error = XS_DRIVER_STUFFUP;
417 		break;
418 	}
419 
420 	DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: return xs->error=%d, "
421 		"xs->flags=0x%x xs->resid=%zu\n", xs->error, xs->status,
422 		xs->resid));
423 
424 	if ((xs->flags & SCSI_POLL) && (xs->error == XS_NOERROR)) {
425 		switch (sc->polled_xfer_status) {
426 		case USBD_NORMAL_COMPLETION:
427 			xs->error = XS_NOERROR;
428 			break;
429 		case USBD_TIMEOUT:
430 			xs->error = XS_TIMEOUT;
431 			break;
432 		default:
433 			xs->error = XS_DRIVER_STUFFUP;
434 			break;
435 		}
436 	}
437 
438 	scsi_done(xs);
439 }
440 
441 void *
442 umass_io_get(void *cookie)
443 {
444 	struct umass_scsi_softc *scbus = cookie;
445 	void *io = NULL;
446 	int s;
447 
448 	s = splusb();
449 	if (!scbus->sc_open) {
450 		scbus->sc_open = 1;
451 		io = scbus; /* just has to be non-NULL */
452 	}
453 	splx(s);
454 
455 	return (io);
456 }
457 
458 void
459 umass_io_put(void *cookie, void *io)
460 {
461 	struct umass_scsi_softc *scbus = cookie;
462 	int s;
463 
464 	s = splusb();
465 	scbus->sc_open = 0;
466 	splx(s);
467 }
468