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