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