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