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