xref: /openbsd-src/sys/dev/usb/umass_scsi.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
1 /*	$OpenBSD: umass_scsi.c,v 1.25 2009/07/02 18:50:37 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 "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 
62 	struct scsi_sense	sc_sense_cmd;
63 };
64 
65 
66 #define UMASS_SCSIID_HOST	0x00
67 #define UMASS_SCSIID_DEVICE	0x01
68 
69 #define UMASS_ATAPI_DRIVE	0
70 
71 int umass_scsi_cmd(struct scsi_xfer *);
72 void umass_scsi_minphys(struct buf *, struct scsi_link *);
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 struct umass_scsi_softc *umass_scsi_setup(struct umass_softc *);
79 
80 struct scsi_device umass_scsi_dev = { NULL, NULL, NULL, NULL, };
81 
82 #if NATAPISCSI > 0
83 struct scsi_device umass_atapiscsi_dev = { NULL, NULL, NULL, NULL, };
84 #endif
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 	scbus->sc_link.device = &umass_scsi_dev;
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 #if NATAPISCSI > 0
116 int
117 umass_atapi_attach(struct umass_softc *sc)
118 {
119 	struct scsibus_attach_args saa;
120 	struct umass_scsi_softc *scbus;
121 
122 	scbus = umass_scsi_setup(sc);
123 	scbus->sc_link.adapter_target = UMASS_SCSIID_HOST;
124 	scbus->sc_link.luns = 1;
125 	scbus->sc_link.openings = 1;
126 	scbus->sc_link.flags |= SDEV_ATAPI;
127 	scbus->sc_link.device = &umass_atapiscsi_dev;
128 
129 	bzero(&saa, sizeof(saa));
130 	saa.saa_sc_link = &scbus->sc_link;
131 
132 	DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: ATAPI\n"
133 			     "sc = 0x%x, scbus = 0x%x\n",
134 			     sc->sc_dev.dv_xname, sc, scbus));
135 
136 	sc->sc_refcnt++;
137 	scbus->base.sc_child = config_found((struct device *)sc,
138 	    &saa, scsiprint);
139 	if (--sc->sc_refcnt < 0)
140 		usb_detach_wakeup(&sc->sc_dev);
141 
142 	return (0);
143 }
144 #endif
145 
146 struct umass_scsi_softc *
147 umass_scsi_setup(struct umass_softc *sc)
148 {
149 	struct umass_scsi_softc *scbus;
150 
151 	scbus = malloc(sizeof(*scbus), M_DEVBUF, M_WAITOK | M_ZERO);
152 
153 	sc->bus = (struct umassbus_softc *)scbus;
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 
159 	/* Fill in the link. */
160 	scbus->sc_link.adapter_buswidth = 2;
161 	scbus->sc_link.adapter = &scbus->sc_adapter;
162 	scbus->sc_link.adapter_softc = sc;
163 	scbus->sc_link.openings = 1;
164 	scbus->sc_link.quirks |= SDEV_ONLYBIG | sc->sc_busquirks;
165 
166 	return (scbus);
167 }
168 
169 int
170 umass_scsi_cmd(struct scsi_xfer *xs)
171 {
172 	struct scsi_link *sc_link = xs->sc_link;
173 	struct umass_softc *sc = sc_link->adapter_softc;
174 
175 	struct scsi_generic *cmd;
176 	int cmdlen, dir, rslt, s;
177 
178 #ifdef UMASS_DEBUG
179 	microtime(&sc->tv);
180 #endif
181 
182 	DIF(UDMASS_UPPER, sc_link->flags |= SCSIDEBUG_LEVEL);
183 
184 	DPRINTF(UDMASS_CMD, ("%s: umass_scsi_cmd: at %lu.%06lu: %d:%d "
185 		"xs=%p cmd=0x%02x datalen=%d (quirks=0x%x, poll=%d)\n",
186 		sc->sc_dev.dv_xname, sc->tv.tv_sec, sc->tv.tv_usec,
187 		sc_link->target, sc_link->lun, xs, xs->cmd->opcode,
188 		xs->datalen, sc_link->quirks, xs->flags & SCSI_POLL));
189 
190 	if (sc->sc_dying) {
191 		xs->error = XS_DRIVER_STUFFUP;
192 		goto done;
193 	}
194 
195 #if defined(UMASS_DEBUG)
196 	if (sc_link->target != UMASS_SCSIID_DEVICE) {
197 		DPRINTF(UDMASS_SCSI, ("%s: wrong SCSI ID %d\n",
198 			sc->sc_dev.dv_xname, sc_link->target));
199 		xs->error = XS_DRIVER_STUFFUP;
200 		goto done;
201 	}
202 #endif
203 
204 	cmd = xs->cmd;
205 	cmdlen = xs->cmdlen;
206 
207 	dir = DIR_NONE;
208 	if (xs->datalen) {
209 		switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
210 		case SCSI_DATA_IN:
211 			dir = DIR_IN;
212 			break;
213 		case SCSI_DATA_OUT:
214 			dir = DIR_OUT;
215 			break;
216 		}
217 	}
218 
219 	if (xs->datalen > UMASS_MAX_TRANSFER_SIZE) {
220 		printf("umass_cmd: large datalen, %d\n", xs->datalen);
221 		xs->error = XS_DRIVER_STUFFUP;
222 		goto done;
223 	}
224 
225 	if (xs->flags & SCSI_POLL) {
226 		DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: sync dir=%d\n", dir));
227 		usbd_set_polling(sc->sc_udev, 1);
228 		sc->sc_xfer_flags = USBD_SYNCHRONOUS;
229 		sc->polled_xfer_status = USBD_INVAL;
230 		sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen,
231 					  xs->data, xs->datalen, dir,
232 					  xs->timeout, umass_scsi_cb, xs);
233 		sc->sc_xfer_flags = 0;
234 		DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: done err=%d\n",
235 				      sc->polled_xfer_status));
236 		if (xs->error == XS_NOERROR) {
237 			switch (sc->polled_xfer_status) {
238 			case USBD_NORMAL_COMPLETION:
239 				xs->error = XS_NOERROR;
240 				break;
241 			case USBD_TIMEOUT:
242 				xs->error = XS_TIMEOUT;
243 				break;
244 			default:
245 				xs->error = XS_DRIVER_STUFFUP;
246 				break;
247 			}
248 		}
249 		usbd_set_polling(sc->sc_udev, 0);
250 		DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: done, error=%d\n",
251 		    xs->error));
252 	} else {
253 		DPRINTF(UDMASS_SCSI,
254 			("umass_scsi_cmd: async dir=%d, cmdlen=%d"
255 			 " datalen=%d\n",
256 			 dir, cmdlen, xs->datalen));
257 		sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen,
258 					  xs->data, xs->datalen, dir,
259 					  xs->timeout, umass_scsi_cb, xs);
260 		return (SUCCESSFULLY_QUEUED);
261 	}
262 
263 	/* Return if command finishes early. */
264  done:
265 	xs->flags |= ITSDONE;
266 	if (xs->flags & SCSI_POLL)
267 		rslt = COMPLETE;
268 	else
269 		rslt = SUCCESSFULLY_QUEUED;
270 
271 	s = splbio();
272 	scsi_done(xs);
273 	splx(s);
274 
275 	return (rslt);
276 }
277 
278 void
279 umass_scsi_minphys(struct buf *bp, struct scsi_link *sl)
280 {
281 	if (bp->b_bcount > UMASS_MAX_TRANSFER_SIZE)
282 		bp->b_bcount = UMASS_MAX_TRANSFER_SIZE;
283 
284 	minphys(bp);
285 }
286 
287 void
288 umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, int status)
289 {
290 	struct umass_scsi_softc *scbus = (struct umass_scsi_softc *)sc->bus;
291 	struct scsi_xfer *xs = priv;
292 	struct scsi_link *link = xs->sc_link;
293 	int cmdlen;
294 	int s;
295 #ifdef UMASS_DEBUG
296 	struct timeval tv;
297 	u_int delta;
298 	microtime(&tv);
299 	delta = (tv.tv_sec - sc->tv.tv_sec) * 1000000 +
300 		tv.tv_usec - sc->tv.tv_usec;
301 #endif
302 
303 	DPRINTF(UDMASS_CMD,
304 		("umass_scsi_cb: at %lu.%06lu, delta=%u: xs=%p residue=%d"
305 		 " status=%d\n", tv.tv_sec, tv.tv_usec, delta, xs, residue,
306 		 status));
307 
308 	xs->resid = residue;
309 
310 	switch (status) {
311 	case STATUS_CMD_OK:
312 		xs->error = XS_NOERROR;
313 		break;
314 
315 	case STATUS_CMD_UNKNOWN:
316 		DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd unknown\n"));
317 		/* we can't issue REQUEST SENSE */
318 		if (xs->sc_link->quirks & ADEV_NOSENSE) {
319 			/*
320 			 * If no residue and no other USB error,
321 			 * command succeeded.
322 			 */
323 			if (residue == 0) {
324 				xs->error = XS_NOERROR;
325 				break;
326 			}
327 
328 			/*
329 			 * Some devices return a short INQUIRY
330 			 * response, omitting response data from the
331 			 * "vendor specific data" on...
332 			 */
333 			if (xs->cmd->opcode == INQUIRY &&
334 			    residue < xs->datalen) {
335 				xs->error = XS_NOERROR;
336 				break;
337 			}
338 
339 			xs->error = XS_DRIVER_STUFFUP;
340 			break;
341 		}
342 		/* FALLTHROUGH */
343 	case STATUS_CMD_FAILED:
344 		DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd failed for "
345 		    "scsi op 0x%02x\n", xs->cmd->opcode));
346 		/* fetch sense data */
347 		sc->sc_sense = 1;
348 		memset(&scbus->sc_sense_cmd, 0, sizeof(scbus->sc_sense_cmd));
349 		scbus->sc_sense_cmd.opcode = REQUEST_SENSE;
350 		scbus->sc_sense_cmd.byte2 = link->lun << SCSI_CMD_LUN_SHIFT;
351 		scbus->sc_sense_cmd.length = sizeof(xs->sense);
352 
353 		cmdlen = sizeof(scbus->sc_sense_cmd);
354 		sc->sc_methods->wire_xfer(sc, link->lun,
355 					  &scbus->sc_sense_cmd, cmdlen,
356 					  &xs->sense, sizeof(xs->sense),
357 					  DIR_IN, xs->timeout,
358 					  umass_scsi_sense_cb, xs);
359 		return;
360 
361 	case STATUS_WIRE_FAILED:
362 		xs->error = XS_RESET;
363 		break;
364 
365 	default:
366 		panic("%s: Unknown status %d in umass_scsi_cb",
367 		      sc->sc_dev.dv_xname, status);
368 	}
369 
370 	if (xs->flags & SCSI_POLL)
371 		return;
372 
373 	xs->flags |= ITSDONE;
374 
375 	DPRINTF(UDMASS_CMD,("umass_scsi_cb: at %lu.%06lu: return error=%d, "
376 			    "status=0x%x resid=%d\n",
377 			    tv.tv_sec, tv.tv_usec,
378 			    xs->error, xs->status, xs->resid));
379 
380 	s = splbio();
381 	scsi_done(xs);
382 	splx(s);
383 }
384 
385 /*
386  * Finalise a completed autosense operation
387  */
388 void
389 umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue,
390 		    int status)
391 {
392 	struct scsi_xfer *xs = priv;
393 	int s;
394 
395 	DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: xs=%p residue=%d "
396 		"status=%d\n", xs, residue, status));
397 
398 	sc->sc_sense = 0;
399 	switch (status) {
400 	case STATUS_CMD_OK:
401 	case STATUS_CMD_UNKNOWN:
402 		/* getting sense data succeeded */
403 		if (residue == 0 || residue == 14)/* XXX */
404 			xs->error = XS_SENSE;
405 		else
406 			xs->error = XS_SHORTSENSE;
407 		break;
408 	default:
409 		DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n",
410 			sc->sc_dev.dv_xname, status));
411 		xs->error = XS_DRIVER_STUFFUP;
412 		break;
413 	}
414 
415 	xs->flags |= ITSDONE;
416 
417 	DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: return xs->error=%d, "
418 		"xs->flags=0x%x xs->resid=%d\n", xs->error, xs->status,
419 		xs->resid));
420 
421 	s = splbio();
422 	scsi_done(xs);
423 	splx(s);
424 }
425 
426