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