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