xref: /openbsd/sys/dev/usb/umass_scsi.c (revision db3296cf)
1 /*	$OpenBSD: umass_scsi.c,v 1.4 2003/05/17 18:25:51 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  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *        This product includes software developed by the NetBSD
22  *        Foundation, Inc. and its contributors.
23  * 4. Neither the name of The NetBSD Foundation nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 #include "atapiscsi.h"
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/conf.h>
46 #include <sys/buf.h>
47 #include <sys/device.h>
48 #include <sys/ioctl.h>
49 #include <sys/malloc.h>
50 
51 #include <dev/usb/usb.h>
52 #include <dev/usb/usbdi.h>
53 #include <dev/usb/usbdi_util.h>
54 #include <dev/usb/usbdevs.h>
55 
56 #include <dev/usb/umassvar.h>
57 #include <dev/usb/umass_scsi.h>
58 
59 #include <scsi/scsi_all.h>
60 #include <scsi/scsiconf.h>
61 #include <scsi/scsi_disk.h>
62 #include <machine/bus.h>
63 
64 struct umass_scsi_softc {
65 	struct umassbus_softc	base;
66 	struct scsi_link	sc_link;
67 	struct scsi_adapter	sc_adapter;
68 
69 	usbd_status		sc_sync_status;
70 	struct scsi_sense	sc_sense_cmd;
71 };
72 
73 
74 #define SHORT_INQUIRY_LENGTH    36 /* XXX */
75 
76 #define UMASS_SCSIID_HOST	0x00
77 #define UMASS_SCSIID_DEVICE	0x01
78 
79 #define UMASS_ATAPI_DRIVE	0
80 
81 int umass_scsi_cmd(struct scsi_xfer *);
82 void umass_scsi_minphys(struct buf *);
83 
84 void umass_scsi_cb(struct umass_softc *sc, void *priv, int residue,
85 		   int status);
86 void umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue,
87 			 int status);
88 struct umass_scsi_softc *umass_scsi_setup(struct umass_softc *);
89 
90 struct scsi_device umass_scsi_dev = { NULL, NULL, NULL, NULL, };
91 
92 #if NATAPISCSI > 0
93 struct scsi_device umass_atapiscsi_dev = { NULL, NULL, NULL, NULL, };
94 #endif
95 
96 int
97 umass_scsi_attach(struct umass_softc *sc)
98 {
99 	struct umass_scsi_softc *scbus;
100 
101 	scbus = umass_scsi_setup(sc);
102 	scbus->sc_link.adapter_target = UMASS_SCSIID_HOST;
103 	scbus->sc_link.luns = sc->maxlun + 1;
104 	scbus->sc_link.flags &= ~SDEV_ATAPI;
105 	scbus->sc_link.device = &umass_scsi_dev;
106 
107 	DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: SCSI\n"
108 			     "sc = 0x%x, scbus = 0x%x\n",
109 			     USBDEVNAME(sc->sc_dev), sc, scbus));
110 
111 	sc->sc_refcnt++;
112 	scbus->base.sc_child =
113 	  config_found((struct device *)sc, &scbus->sc_link, scsiprint);
114 	if (--sc->sc_refcnt < 0)
115 		usb_detach_wakeup(USBDEV(sc->sc_dev));
116 
117 	return (0);
118 }
119 
120 #if NATAPISCSI > 0
121 int
122 umass_atapi_attach(struct umass_softc *sc)
123 {
124 	struct umass_scsi_softc *scbus;
125 
126 	scbus = umass_scsi_setup(sc);
127 	scbus->sc_link.adapter_target = UMASS_SCSIID_HOST;
128 	scbus->sc_link.luns = 1;
129 	scbus->sc_link.flags |= SDEV_ATAPI;
130 	scbus->sc_link.quirks |= SDEV_NOLUNS;
131 	scbus->sc_link.device = &umass_atapiscsi_dev;
132 
133 	DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: ATAPI\n"
134 			     "sc = 0x%x, scbus = 0x%x\n",
135 			     USBDEVNAME(sc->sc_dev), sc, scbus));
136 
137 	sc->sc_refcnt++;
138 	scbus->base.sc_child =
139 	  config_found((struct device *)sc, &scbus->sc_link, scsiprint);
140 	if (--sc->sc_refcnt < 0)
141 		usb_detach_wakeup(USBDEV(sc->sc_dev));
142 
143 	return (0);
144 }
145 #endif
146 
147 struct umass_scsi_softc *
148 umass_scsi_setup(struct umass_softc *sc)
149 {
150 	struct umass_scsi_softc *scbus;
151 
152 	scbus = malloc(sizeof(struct umass_scsi_softc), M_DEVBUF, M_WAITOK);
153 	memset(&scbus->sc_link, 0, sizeof(struct scsi_link));
154 	memset(&scbus->sc_adapter, 0, sizeof(struct scsi_adapter));
155 
156 	sc->bus = (struct umassbus_softc *)scbus;
157 
158 	/* Fill in the adapter. */
159 	scbus->sc_adapter.scsi_cmd = umass_scsi_cmd;
160 	scbus->sc_adapter.scsi_minphys = umass_scsi_minphys;
161 
162 	/* Fill in the link. */
163 	scbus->sc_link.adapter_buswidth = 2;
164 	scbus->sc_link.openings = 1;
165 	scbus->sc_link.adapter = &scbus->sc_adapter;
166 	scbus->sc_link.adapter_softc = sc;
167 	scbus->sc_link.openings = 1;
168 	scbus->sc_link.quirks |= PQUIRK_ONLYBIG | PQUIRK_NOMODESENSE |
169 		sc->sc_busquirks;
170 
171 	return (scbus);
172 }
173 
174 int
175 umass_scsi_cmd(struct scsi_xfer *xs)
176 {
177 	struct scsi_link *sc_link = xs->sc_link;
178 	struct umass_softc *sc = sc_link->adapter_softc;
179 	struct umass_scsi_softc *scbus = (struct umass_scsi_softc *)sc->bus;
180 
181 	struct scsi_generic *cmd, trcmd;
182 	int cmdlen, dir, s;
183 
184 #ifdef UMASS_DEBUG
185 	microtime(&sc->tv);
186 #endif
187 
188 	memset(&trcmd, 0, sizeof(trcmd));
189 
190 	DIF(UDMASS_UPPER, sc_link->flags |= SCSIDEBUG_LEVEL);
191 
192 	DPRINTF(UDMASS_CMD, ("%s: umass_scsi_cmd: at %lu.%06lu: %d:%d "
193 		"xs=%p cmd=0x%02x datalen=%d (quirks=0x%x, poll=%d)\n",
194 		USBDEVNAME(sc->sc_dev), sc->tv.tv_sec, sc->tv.tv_usec,
195 		sc_link->target, sc_link->lun, xs, xs->cmd->opcode,
196 		xs->datalen, sc_link->quirks, xs->flags & SCSI_POLL));
197 
198 #if defined(USB_DEBUG) && defined(SCSIDEBUG)
199 	if (umassdebug & UDMASS_SCSI)
200 		show_scsi_xs(xs);
201 	else if (umassdebug & ~UDMASS_CMD)
202 		show_scsi_cmd(xs);
203 #endif
204 
205 	if (sc->sc_dying) {
206 		xs->error = XS_DRIVER_STUFFUP;
207 		goto done;
208 	}
209 
210 #if defined(UMASS_DEBUG)
211 	if (sc_link->target != UMASS_SCSIID_DEVICE) {
212 		DPRINTF(UDMASS_SCSI, ("%s: wrong SCSI ID %d\n",
213 			USBDEVNAME(sc->sc_dev), sc_link->target));
214 		xs->error = XS_DRIVER_STUFFUP;
215 		goto done;
216 	}
217 #endif
218 
219 	cmd = xs->cmd;
220 	cmdlen = xs->cmdlen;
221 
222 	if (cmd->opcode == MODE_SENSE &&
223 	    (sc_link->quirks & SDEV_NOMODESENSE)) {
224 		xs->error = XS_TIMEOUT;
225 		goto done;
226 	}
227 
228 	if (cmd->opcode == START_STOP &&
229 	    (sc->sc_quirks & UMASS_QUIRK_NO_START_STOP)) {
230 		xs->error = XS_NOERROR;
231 		goto done;
232 	}
233 
234 	if (cmd->opcode == INQUIRY &&
235 	    (sc->sc_quirks & UMASS_QUIRK_FORCE_SHORT_INQUIRY)) {
236 			/*
237 			 * Some drives wedge when asked for full inquiry
238 			 * information.
239 			 */
240 		memcpy(&trcmd, cmd, sizeof(trcmd));
241 		trcmd.bytes[4] = SHORT_INQUIRY_LENGTH;
242 		cmd = &trcmd;
243 		xs->datalen = SHORT_INQUIRY_LENGTH;
244 	}
245 
246 	dir = DIR_NONE;
247 	if (xs->datalen) {
248 		switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
249 		case SCSI_DATA_IN:
250 			dir = DIR_IN;
251 			break;
252 		case SCSI_DATA_OUT:
253 			dir = DIR_OUT;
254 			break;
255 		}
256 	}
257 
258 	if (xs->datalen > UMASS_MAX_TRANSFER_SIZE) {
259 		printf("umass_cmd: large datalen, %d\n", xs->datalen);
260 		xs->error = XS_DRIVER_STUFFUP;
261 		goto done;
262 	}
263 
264 	if (xs->flags & SCSI_POLL) {
265 		/* Use sync transfer. XXX Broken! */
266 		DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: sync dir=%d\n", dir));
267 		sc->sc_xfer_flags = USBD_SYNCHRONOUS;
268 		scbus->sc_sync_status = USBD_INVAL;
269 		sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen,
270 					  xs->data, xs->datalen, dir,
271 					  xs->timeout, 0, xs);
272 		sc->sc_xfer_flags = 0;
273 		DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: done err=%d\n",
274 				      scbus->sc_sync_status));
275 		switch (scbus->sc_sync_status) {
276 		case USBD_NORMAL_COMPLETION:
277 			xs->error = XS_NOERROR;
278 			break;
279 		case USBD_TIMEOUT:
280 			xs->error = XS_TIMEOUT;
281 			break;
282 		default:
283 			xs->error = XS_DRIVER_STUFFUP;
284 			break;
285 		}
286 		goto done;
287 	} else {
288 		DPRINTF(UDMASS_SCSI,
289 			("umass_scsi_cmd: async dir=%d, cmdlen=%d"
290 			 " datalen=%d\n",
291 			 dir, cmdlen, xs->datalen));
292 		sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen,
293 					  xs->data, xs->datalen, dir,
294 					  xs->timeout, umass_scsi_cb, xs);
295 		return (SUCCESSFULLY_QUEUED);
296 	}
297 
298 	/* Return if command finishes early. */
299  done:
300 	xs->flags |= ITSDONE;
301 
302 	s = splbio();
303 	scsi_done(xs);
304 	splx(s);
305 	if (xs->flags & SCSI_POLL)
306 		return (COMPLETE);
307 	else
308 		return (SUCCESSFULLY_QUEUED);
309 }
310 
311 void
312 umass_scsi_minphys(struct buf *bp)
313 {
314 	if (bp->b_bcount > UMASS_MAX_TRANSFER_SIZE)
315 		bp->b_bcount = UMASS_MAX_TRANSFER_SIZE;
316 
317 	minphys(bp);
318 }
319 
320 void
321 umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, int status)
322 {
323 	struct umass_scsi_softc *scbus = (struct umass_scsi_softc *)sc->bus;
324 	struct scsi_xfer *xs = priv;
325 	struct scsi_link *link = xs->sc_link;
326 	int cmdlen;
327 	int s;
328 #ifdef UMASS_DEBUG
329 	struct timeval tv;
330 	u_int delta;
331 	microtime(&tv);
332 	delta = (tv.tv_sec - sc->tv.tv_sec) * 1000000 +
333 		tv.tv_usec - sc->tv.tv_usec;
334 #endif
335 
336 	DPRINTF(UDMASS_CMD,
337 		("umass_scsi_cb: at %lu.%06lu, delta=%u: xs=%p residue=%d"
338 		 " status=%d\n", tv.tv_sec, tv.tv_usec, delta, xs, residue,
339 		 status));
340 
341 	xs->resid = residue;
342 
343 	switch (status) {
344 	case STATUS_CMD_OK:
345 		xs->error = XS_NOERROR;
346 		break;
347 
348 	case STATUS_CMD_UNKNOWN:
349 		DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd unknown\n"));
350 		/* we can't issue REQUEST SENSE */
351 		if (xs->sc_link->quirks & PQUIRK_NOSENSE) {
352 			/*
353 			 * If no residue and no other USB error,
354 			 * command succeeded.
355 			 */
356 			if (residue == 0) {
357 				xs->error = XS_NOERROR;
358 				break;
359 			}
360 
361 			/*
362 			 * Some devices return a short INQUIRY
363 			 * response, omitting response data from the
364 			 * "vendor specific data" on...
365 			 */
366 			if (xs->cmd->opcode == INQUIRY &&
367 			    residue < xs->datalen) {
368 				xs->error = XS_NOERROR;
369 				break;
370 			}
371 
372 			xs->error = XS_DRIVER_STUFFUP;
373 			break;
374 		}
375 		/* FALLTHROUGH */
376 	case STATUS_CMD_FAILED:
377 		printf("umass_scsi_cb: status cmd failed for scsi op 0x%02x\n",
378 		    xs->cmd->opcode);
379 		/* fetch sense data */
380 		memset(&scbus->sc_sense_cmd, 0, sizeof(scbus->sc_sense_cmd));
381 		scbus->sc_sense_cmd.opcode = REQUEST_SENSE;
382 		scbus->sc_sense_cmd.byte2 = link->lun << SCSI_CMD_LUN_SHIFT;
383 		scbus->sc_sense_cmd.length = sizeof(xs->sense);
384 
385 		cmdlen = sizeof(scbus->sc_sense_cmd);
386 		if (sc->sc_cmd == UMASS_CPROTO_UFI) /* XXX */
387 			cmdlen = UFI_COMMAND_LENGTH;
388 		sc->sc_methods->wire_xfer(sc, link->lun,
389 					  &scbus->sc_sense_cmd, cmdlen,
390 					  &xs->sense, sizeof(xs->sense),
391 					  DIR_IN, xs->timeout,
392 					  umass_scsi_sense_cb, xs);
393 		return;
394 
395 	case STATUS_WIRE_FAILED:
396 		xs->error = XS_RESET;
397 		break;
398 
399 	default:
400 		panic("%s: Unknown status %d in umass_scsi_cb",
401 		      USBDEVNAME(sc->sc_dev), status);
402 	}
403 
404 	xs->flags |= ITSDONE;
405 
406 	DPRINTF(UDMASS_CMD,("umass_scsi_cb: at %lu.%06lu: return error=%d, "
407 			    "status=0x%x resid=%d\n",
408 			    tv.tv_sec, tv.tv_usec,
409 			    xs->error, xs->status, xs->resid));
410 
411 	s = splbio();
412 	scsi_done(xs);
413 	splx(s);
414 }
415 
416 /*
417  * Finalise a completed autosense operation
418  */
419 void
420 umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue,
421 		    int status)
422 {
423 	struct scsi_xfer *xs = priv;
424 	int s;
425 
426 	DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: xs=%p residue=%d "
427 		"status=%d\n", xs, residue, status));
428 
429 	switch (status) {
430 	case STATUS_CMD_OK:
431 	case STATUS_CMD_UNKNOWN:
432 		/* getting sense data succeeded */
433 		if (xs->cmd->opcode == INQUIRY && (xs->resid < xs->datalen ||
434 		    (sc->sc_quirks & UMASS_QUIRK_RS_NO_CLEAR_UA /* XXX */))) {
435 			/*
436 			 * Some drivers return SENSE errors even after INQUIRY.
437 			 * The upper layer doesn't like that.
438 			 */
439 			xs->error = XS_NOERROR;
440 			break;
441 		}
442 		/* XXX look at residue */
443 		if (residue == 0 || residue == 14)/* XXX */
444 			xs->error = XS_SENSE;
445 		else
446 			xs->error = XS_SHORTSENSE;
447 		break;
448 	default:
449 		DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n",
450 			USBDEVNAME(sc->sc_dev), status));
451 		xs->error = XS_DRIVER_STUFFUP;
452 		break;
453 	}
454 
455 	xs->flags |= ITSDONE;
456 
457 	DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: return xs->error=%d, "
458 		"xs->flags=0x%x xs->resid=%d\n", xs->error, xs->status,
459 		xs->resid));
460 
461 	s = splbio();
462 	scsi_done(xs);
463 	splx(s);
464 }
465 
466