xref: /openbsd-src/sys/dev/usb/umass_scsi.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: umass_scsi.c,v 1.24 2009/02/16 21:19:07 miod 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, 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 	s = splbio();
267 	scsi_done(xs);
268 	splx(s);
269 	return (COMPLETE);
270 }
271 
272 void
273 umass_scsi_minphys(struct buf *bp, struct scsi_link *sl)
274 {
275 	if (bp->b_bcount > UMASS_MAX_TRANSFER_SIZE)
276 		bp->b_bcount = UMASS_MAX_TRANSFER_SIZE;
277 
278 	minphys(bp);
279 }
280 
281 void
282 umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, int status)
283 {
284 	struct umass_scsi_softc *scbus = (struct umass_scsi_softc *)sc->bus;
285 	struct scsi_xfer *xs = priv;
286 	struct scsi_link *link = xs->sc_link;
287 	int cmdlen;
288 	int s;
289 #ifdef UMASS_DEBUG
290 	struct timeval tv;
291 	u_int delta;
292 	microtime(&tv);
293 	delta = (tv.tv_sec - sc->tv.tv_sec) * 1000000 +
294 		tv.tv_usec - sc->tv.tv_usec;
295 #endif
296 
297 	DPRINTF(UDMASS_CMD,
298 		("umass_scsi_cb: at %lu.%06lu, delta=%u: xs=%p residue=%d"
299 		 " status=%d\n", tv.tv_sec, tv.tv_usec, delta, xs, residue,
300 		 status));
301 
302 	xs->resid = residue;
303 
304 	switch (status) {
305 	case STATUS_CMD_OK:
306 		xs->error = XS_NOERROR;
307 		break;
308 
309 	case STATUS_CMD_UNKNOWN:
310 		DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd unknown\n"));
311 		/* we can't issue REQUEST SENSE */
312 		if (xs->sc_link->quirks & ADEV_NOSENSE) {
313 			/*
314 			 * If no residue and no other USB error,
315 			 * command succeeded.
316 			 */
317 			if (residue == 0) {
318 				xs->error = XS_NOERROR;
319 				break;
320 			}
321 
322 			/*
323 			 * Some devices return a short INQUIRY
324 			 * response, omitting response data from the
325 			 * "vendor specific data" on...
326 			 */
327 			if (xs->cmd->opcode == INQUIRY &&
328 			    residue < xs->datalen) {
329 				xs->error = XS_NOERROR;
330 				break;
331 			}
332 
333 			xs->error = XS_DRIVER_STUFFUP;
334 			break;
335 		}
336 		/* FALLTHROUGH */
337 	case STATUS_CMD_FAILED:
338 		DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd failed for "
339 		    "scsi op 0x%02x\n", xs->cmd->opcode));
340 		/* fetch sense data */
341 		sc->sc_sense = 1;
342 		memset(&scbus->sc_sense_cmd, 0, sizeof(scbus->sc_sense_cmd));
343 		scbus->sc_sense_cmd.opcode = REQUEST_SENSE;
344 		scbus->sc_sense_cmd.byte2 = link->lun << SCSI_CMD_LUN_SHIFT;
345 		scbus->sc_sense_cmd.length = sizeof(xs->sense);
346 
347 		cmdlen = sizeof(scbus->sc_sense_cmd);
348 		sc->sc_methods->wire_xfer(sc, link->lun,
349 					  &scbus->sc_sense_cmd, cmdlen,
350 					  &xs->sense, sizeof(xs->sense),
351 					  DIR_IN, xs->timeout,
352 					  umass_scsi_sense_cb, xs);
353 		return;
354 
355 	case STATUS_WIRE_FAILED:
356 		xs->error = XS_RESET;
357 		break;
358 
359 	default:
360 		panic("%s: Unknown status %d in umass_scsi_cb",
361 		      sc->sc_dev.dv_xname, status);
362 	}
363 
364 	if (xs->flags & SCSI_POLL)
365 		return;
366 
367 	xs->flags |= ITSDONE;
368 
369 	DPRINTF(UDMASS_CMD,("umass_scsi_cb: at %lu.%06lu: return error=%d, "
370 			    "status=0x%x resid=%d\n",
371 			    tv.tv_sec, tv.tv_usec,
372 			    xs->error, xs->status, xs->resid));
373 
374 	s = splbio();
375 	scsi_done(xs);
376 	splx(s);
377 }
378 
379 /*
380  * Finalise a completed autosense operation
381  */
382 void
383 umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue,
384 		    int status)
385 {
386 	struct scsi_xfer *xs = priv;
387 	int s;
388 
389 	DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: xs=%p residue=%d "
390 		"status=%d\n", xs, residue, status));
391 
392 	sc->sc_sense = 0;
393 	switch (status) {
394 	case STATUS_CMD_OK:
395 	case STATUS_CMD_UNKNOWN:
396 		/* getting sense data succeeded */
397 		if (residue == 0 || residue == 14)/* XXX */
398 			xs->error = XS_SENSE;
399 		else
400 			xs->error = XS_SHORTSENSE;
401 		break;
402 	default:
403 		DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n",
404 			sc->sc_dev.dv_xname, status));
405 		xs->error = XS_DRIVER_STUFFUP;
406 		break;
407 	}
408 
409 	xs->flags |= ITSDONE;
410 
411 	DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: return xs->error=%d, "
412 		"xs->flags=0x%x xs->resid=%d\n", xs->error, xs->status,
413 		xs->resid));
414 
415 	s = splbio();
416 	scsi_done(xs);
417 	splx(s);
418 }
419 
420