1 /* $NetBSD: umass_scsipi.c,v 1.34 2009/01/11 11:06:08 cegger Exp $ */
4 * Copyright (c) 2001, 2003 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (lennart@augustsson.net) at
9 * Carlstedt Research & Technology and by Charles M. Hamnnum.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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.
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: umass_scsipi.c,v 1.34 2009/01/11 11:06:08 cegger Exp $");
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
45 #include <sys/device.h>
46 #include <sys/ioctl.h>
47 #include <sys/malloc.h>
50 #include <sys/scsiio.h>
51 #include <dev/scsipi/scsi_spc.h>
52 #include <dev/scsipi/scsi_all.h>
53 #include <dev/scsipi/scsipi_all.h>
54 #include <dev/scsipi/scsiconf.h>
56 #include <dev/scsipi/atapiconf.h>
58 #include <dev/scsipi/scsipi_disk.h>
59 #include <dev/scsipi/scsi_disk.h>
60 #include <dev/scsipi/scsi_changer.h>
62 #include <sys/disk.h> /* XXX */
63 #include <dev/scsipi/sdvar.h> /* XXX */
66 #include <dev/usb/usb.h>
67 #include <dev/usb/usbdi.h>
68 #include <dev/usb/usbdi_util.h>
69 #include <dev/usb/usbdevs.h>
71 #include <dev/usb/umassvar.h>
72 #include <dev/usb/umass_scsipi.h>
74 struct umass_scsipi_softc
{
75 struct umassbus_softc base
;
77 struct atapi_adapter sc_atapi_adapter
;
78 #define sc_adapter sc_atapi_adapter._generic
79 struct scsipi_channel sc_channel
;
80 usbd_status sc_sync_status
;
81 struct scsi_request_sense sc_sense_cmd
;
85 #define SHORT_INQUIRY_LENGTH 36 /* XXX */
87 #define UMASS_ATAPI_DRIVE 0
89 Static
void umass_scsipi_request(struct scsipi_channel
*,
90 scsipi_adapter_req_t
, void *);
91 Static
void umass_scsipi_minphys(struct buf
*bp
);
92 Static
int umass_scsipi_ioctl(struct scsipi_channel
*, u_long
,
93 void *, int, usb_proc_ptr
);
94 Static
int umass_scsipi_getgeom(struct scsipi_periph
*periph
,
95 struct disk_parms
*, u_long sectors
);
97 Static
void umass_scsipi_cb(struct umass_softc
*sc
, void *priv
,
98 int residue
, int status
);
99 Static
void umass_scsipi_sense_cb(struct umass_softc
*sc
, void *priv
,
100 int residue
, int status
);
102 Static
struct umass_scsipi_softc
*umass_scsipi_setup(struct umass_softc
*sc
);
105 Static
void umass_atapi_probe_device(struct atapibus_softc
*, int);
107 const struct scsipi_bustype umass_atapi_bustype
= {
108 SCSIPI_BUSTYPE_ATAPI
,
110 atapi_interpret_sense
,
119 umass_scsi_attach(struct umass_softc
*sc
)
121 struct umass_scsipi_softc
*scbus
;
123 scbus
= umass_scsipi_setup(sc
);
125 scbus
->sc_channel
.chan_bustype
= &scsi_bustype
;
126 scbus
->sc_channel
.chan_ntargets
= 2;
127 scbus
->sc_channel
.chan_nluns
= sc
->maxlun
+ 1;
128 scbus
->sc_channel
.chan_id
= scbus
->sc_channel
.chan_ntargets
- 1;
129 DPRINTF(UDMASS_USB
, ("%s: umass_attach_bus: SCSI\n",
130 USBDEVNAME(sc
->sc_dev
)));
133 scbus
->base
.sc_child
=
134 config_found_ia(sc
->sc_dev
, "scsi", &scbus
->sc_channel
,
136 if (--sc
->sc_refcnt
< 0)
137 usb_detach_wakeup(USBDEV(sc
->sc_dev
));
145 umass_atapi_attach(struct umass_softc
*sc
)
147 struct umass_scsipi_softc
*scbus
;
149 scbus
= umass_scsipi_setup(sc
);
150 scbus
->sc_atapi_adapter
.atapi_probe_device
= umass_atapi_probe_device
;
152 scbus
->sc_channel
.chan_bustype
= &umass_atapi_bustype
;
153 scbus
->sc_channel
.chan_ntargets
= 2;
154 scbus
->sc_channel
.chan_nluns
= 1;
156 scbus
->sc_channel
.chan_defquirks
|= sc
->sc_busquirks
;
157 DPRINTF(UDMASS_USB
, ("%s: umass_attach_bus: ATAPI\n",
158 USBDEVNAME(sc
->sc_dev
)));
161 scbus
->base
.sc_child
=
162 config_found_ia(sc
->sc_dev
, "atapi", &scbus
->sc_channel
,
164 if (--sc
->sc_refcnt
< 0)
165 usb_detach_wakeup(USBDEV(sc
->sc_dev
));
171 Static
struct umass_scsipi_softc
*
172 umass_scsipi_setup(struct umass_softc
*sc
)
174 struct umass_scsipi_softc
*scbus
;
176 scbus
= malloc(sizeof *scbus
, M_DEVBUF
, M_WAITOK
| M_ZERO
);
177 sc
->bus
= &scbus
->base
;
179 /* Only use big commands for USB SCSI devices. */
180 sc
->sc_busquirks
|= PQUIRK_ONLYBIG
;
182 /* Fill in the adapter. */
183 memset(&scbus
->sc_adapter
, 0, sizeof(scbus
->sc_adapter
));
184 scbus
->sc_adapter
.adapt_dev
= sc
->sc_dev
;
185 scbus
->sc_adapter
.adapt_nchannels
= 1;
186 scbus
->sc_adapter
.adapt_request
= umass_scsipi_request
;
187 scbus
->sc_adapter
.adapt_minphys
= umass_scsipi_minphys
;
188 scbus
->sc_adapter
.adapt_ioctl
= umass_scsipi_ioctl
;
189 scbus
->sc_adapter
.adapt_getgeom
= umass_scsipi_getgeom
;
191 /* Fill in the channel. */
192 memset(&scbus
->sc_channel
, 0, sizeof(scbus
->sc_channel
));
193 scbus
->sc_channel
.chan_adapter
= &scbus
->sc_adapter
;
194 scbus
->sc_channel
.chan_channel
= 0;
195 scbus
->sc_channel
.chan_flags
= SCSIPI_CHAN_OPENINGS
| SCSIPI_CHAN_NOSETTLE
;
196 scbus
->sc_channel
.chan_openings
= 1;
197 scbus
->sc_channel
.chan_max_periph
= 1;
198 scbus
->sc_channel
.chan_defquirks
|= sc
->sc_busquirks
;
204 umass_scsipi_request(struct scsipi_channel
*chan
,
205 scsipi_adapter_req_t req
, void *arg
)
207 struct scsipi_adapter
*adapt
= chan
->chan_adapter
;
208 struct scsipi_periph
*periph
;
209 struct scsipi_xfer
*xs
;
210 struct umass_softc
*sc
= device_private(adapt
->adapt_dev
);
211 struct umass_scsipi_softc
*scbus
= (struct umass_scsipi_softc
*)sc
->bus
;
212 struct scsipi_generic
*cmd
;
219 case ADAPTER_REQ_RUN_XFER
:
221 periph
= xs
->xs_periph
;
222 DIF(UDMASS_UPPER
, periph
->periph_dbflags
|= SCSIPI_DEBUG_FLAGS
);
224 DPRINTF(UDMASS_CMD
, ("%s: umass_scsi_cmd: at %"PRIu64
".%06"PRIu64
": %d:%d "
225 "xs=%p cmd=0x%02x datalen=%d (quirks=0x%x, poll=%d)\n",
226 USBDEVNAME(sc
->sc_dev
), sc
->tv
.tv_sec
, (uint64_t)sc
->tv
.tv_usec
,
227 periph
->periph_target
, periph
->periph_lun
,
228 xs
, xs
->cmd
->opcode
, xs
->datalen
,
229 periph
->periph_quirks
, xs
->xs_control
& XS_CTL_POLL
));
230 #if defined(USB_DEBUG) && defined(SCSIPI_DEBUG)
231 if (umassdebug
& UDMASS_SCSI
)
233 else if (umassdebug
& ~UDMASS_CMD
)
238 xs
->error
= XS_DRIVER_STUFFUP
;
243 if (chan
->chan_bustype
->bustype_type
== SCSIPI_BUSTYPE_ATAPI
?
244 periph
->periph_target
!= UMASS_ATAPI_DRIVE
:
245 periph
->periph_target
== chan
->chan_id
) {
246 DPRINTF(UDMASS_SCSI
, ("%s: wrong SCSI ID %d\n",
247 USBDEVNAME(sc
->sc_dev
),
248 periph
->periph_target
));
249 xs
->error
= XS_DRIVER_STUFFUP
;
259 switch (xs
->xs_control
&
260 (XS_CTL_DATA_IN
| XS_CTL_DATA_OUT
)) {
264 case XS_CTL_DATA_OUT
:
270 if (xs
->datalen
> UMASS_MAX_TRANSFER_SIZE
) {
271 printf("umass_cmd: large datalen, %d\n", xs
->datalen
);
272 xs
->error
= XS_DRIVER_STUFFUP
;
276 if (xs
->xs_control
& XS_CTL_POLL
) {
277 /* Use sync transfer. XXX Broken! */
279 ("umass_scsi_cmd: sync dir=%d\n", dir
));
280 sc
->sc_xfer_flags
= USBD_SYNCHRONOUS
;
281 scbus
->sc_sync_status
= USBD_INVAL
;
282 sc
->sc_methods
->wire_xfer(sc
, periph
->periph_lun
, cmd
,
286 sc
->sc_xfer_flags
= 0;
287 DPRINTF(UDMASS_SCSI
, ("umass_scsi_cmd: done err=%d\n",
288 scbus
->sc_sync_status
));
289 switch (scbus
->sc_sync_status
) {
290 case USBD_NORMAL_COMPLETION
:
291 xs
->error
= XS_NOERROR
;
294 xs
->error
= XS_TIMEOUT
;
297 xs
->error
= XS_DRIVER_STUFFUP
;
303 ("umass_scsi_cmd: async dir=%d, cmdlen=%d"
305 dir
, cmdlen
, xs
->datalen
));
306 sc
->sc_methods
->wire_xfer(sc
, periph
->periph_lun
, cmd
,
310 umass_scsipi_cb
, xs
);
314 /* Return if command finishes early. */
319 /* Not supported, nothing to do. */
325 umass_scsipi_minphys(struct buf
*bp
)
328 if (bp
->b_bcount
<= 0) {
329 printf("umass_scsipi_minphys count(%d) <= 0\n",
331 bp
->b_bcount
= UMASS_MAX_TRANSFER_SIZE
;
334 if (bp
->b_bcount
> UMASS_MAX_TRANSFER_SIZE
)
335 bp
->b_bcount
= UMASS_MAX_TRANSFER_SIZE
;
340 umass_scsipi_ioctl(struct scsipi_channel
*chan
, u_long cmd
,
341 void *arg
, int flag
, usb_proc_ptr p
)
343 /*struct umass_softc *sc = link->adapter_softc;*/
344 /*struct umass_scsipi_softc *scbus = sc->bus;*/
349 ccb
->ccb_h
.status
= CAM_REQ_INPROG
;
350 umass_reset(sc
, umass_cam_cb
, (void *) ccb
);
359 umass_scsipi_getgeom(struct scsipi_periph
*periph
, struct disk_parms
*dp
,
362 struct umass_softc
*sc
=
363 device_private(periph
->periph_channel
->chan_adapter
->adapt_dev
);
365 /* If it's not a floppy, we don't know what to do. */
366 if (sc
->sc_cmd
!= UMASS_CPROTO_UFI
)
371 /* Most likely a single density 3.5" floppy. */
377 /* Most likely a double density 3.5" floppy. */
388 umass_scsipi_cb(struct umass_softc
*sc
, void *priv
, int residue
, int status
)
390 struct umass_scsipi_softc
*scbus
= (struct umass_scsipi_softc
*)sc
->bus
;
391 struct scsipi_xfer
*xs
= priv
;
392 struct scsipi_periph
*periph
= xs
->xs_periph
;
399 delta
= (tv
.tv_sec
- sc
->tv
.tv_sec
) * 1000000 + tv
.tv_usec
- sc
->tv
.tv_usec
;
402 DPRINTF(UDMASS_CMD
,("umass_scsipi_cb: at %"PRIu64
".%06"PRIu64
", delta=%u: xs=%p residue=%d"
403 " status=%d\n", tv
.tv_sec
, (uint64_t)tv
.tv_usec
, delta
, xs
, residue
, status
));
409 xs
->error
= XS_NOERROR
;
412 case STATUS_CMD_UNKNOWN
:
414 case STATUS_CMD_FAILED
:
415 /* fetch sense data */
417 memset(&scbus
->sc_sense_cmd
, 0, sizeof(scbus
->sc_sense_cmd
));
418 scbus
->sc_sense_cmd
.opcode
= SCSI_REQUEST_SENSE
;
419 scbus
->sc_sense_cmd
.byte2
= periph
->periph_lun
<<
421 scbus
->sc_sense_cmd
.length
= sizeof(xs
->sense
);
423 if (sc
->sc_cmd
== UMASS_CPROTO_UFI
||
424 sc
->sc_cmd
== UMASS_CPROTO_ATAPI
)
425 cmdlen
= UFI_COMMAND_LENGTH
; /* XXX */
427 cmdlen
= sizeof(scbus
->sc_sense_cmd
);
428 sc
->sc_methods
->wire_xfer(sc
, periph
->periph_lun
,
429 &scbus
->sc_sense_cmd
, cmdlen
,
430 &xs
->sense
, sizeof(xs
->sense
),
432 umass_scsipi_sense_cb
, xs
);
435 case STATUS_WIRE_FAILED
:
436 xs
->error
= XS_RESET
;
440 panic("%s: Unknown status %d in umass_scsipi_cb",
441 USBDEVNAME(sc
->sc_dev
), status
);
444 DPRINTF(UDMASS_CMD
,("umass_scsipi_cb: at %"PRIu64
".%06"PRIu64
": return xs->error="
445 "%d, xs->xs_status=0x%x xs->resid=%d\n",
446 tv
.tv_sec
, (uint64_t)tv
.tv_usec
,
447 xs
->error
, xs
->xs_status
, xs
->resid
));
455 * Finalise a completed autosense operation
458 umass_scsipi_sense_cb(struct umass_softc
*sc
, void *priv
, int residue
,
461 struct scsipi_xfer
*xs
= priv
;
464 DPRINTF(UDMASS_CMD
,("umass_scsipi_sense_cb: xs=%p residue=%d "
465 "status=%d\n", xs
, residue
, status
));
470 case STATUS_CMD_UNKNOWN
:
471 /* getting sense data succeeded */
472 if (residue
== 0 || residue
== 14)/* XXX */
473 xs
->error
= XS_SENSE
;
475 xs
->error
= XS_SHORTSENSE
;
478 DPRINTF(UDMASS_SCSI
, ("%s: Autosense failed, status %d\n",
479 USBDEVNAME(sc
->sc_dev
), status
));
480 xs
->error
= XS_DRIVER_STUFFUP
;
484 DPRINTF(UDMASS_CMD
,("umass_scsipi_sense_cb: return xs->error=%d, "
485 "xs->xs_status=0x%x xs->resid=%d\n", xs
->error
, xs
->xs_status
,
495 umass_atapi_probe_device(struct atapibus_softc
*atapi
, int target
)
497 struct scsipi_channel
*chan
= atapi
->sc_channel
;
498 struct scsipi_periph
*periph
;
499 struct scsipibus_attach_args sa
;
500 char vendor
[33], product
[65], revision
[17];
501 struct scsipi_inquiry_data inqbuf
;
503 DPRINTF(UDMASS_SCSI
,("umass_atapi_probe_device: atapi=%p target=%d\n",
506 if (target
!= UMASS_ATAPI_DRIVE
) /* only probe drive 0 */
509 /* skip if already attached */
510 if (scsipi_lookup_periph(chan
, target
, 0) != NULL
)
513 periph
= scsipi_alloc_periph(M_NOWAIT
);
514 if (periph
== NULL
) {
515 aprint_error_dev(atapi
->sc_dev
,
516 "can't allocate link for drive %d\n", target
);
520 DIF(UDMASS_UPPER
, periph
->periph_dbflags
|= 1); /* XXX 1 */
521 periph
->periph_channel
= chan
;
522 periph
->periph_switch
= &atapi_probe_periphsw
;
523 periph
->periph_target
= target
;
524 periph
->periph_quirks
= chan
->chan_defquirks
;
526 DPRINTF(UDMASS_SCSI
, ("umass_atapi_probe_device: doing inquiry\n"));
527 /* Now go ask the device all about itself. */
528 memset(&inqbuf
, 0, sizeof(inqbuf
));
529 if (scsipi_inquire(periph
, &inqbuf
, XS_CTL_DISCOVERY
) != 0) {
530 DPRINTF(UDMASS_SCSI
, ("umass_atapi_probe_device: "
531 "scsipi_inquire failed\n"));
532 free(periph
, M_DEVBUF
);
536 scsipi_strvis(vendor
, 33, inqbuf
.vendor
, 8);
537 scsipi_strvis(product
, 65, inqbuf
.product
, 16);
538 scsipi_strvis(revision
, 17, inqbuf
.revision
, 4);
540 sa
.sa_periph
= periph
;
541 sa
.sa_inqbuf
.type
= inqbuf
.device
;
542 sa
.sa_inqbuf
.removable
= inqbuf
.dev_qual2
& SID_REMOVABLE
?
544 if (sa
.sa_inqbuf
.removable
)
545 periph
->periph_flags
|= PERIPH_REMOVABLE
;
546 sa
.sa_inqbuf
.vendor
= vendor
;
547 sa
.sa_inqbuf
.product
= product
;
548 sa
.sa_inqbuf
.revision
= revision
;
551 DPRINTF(UDMASS_SCSI
, ("umass_atapi_probedev: doing atapi_probedev on "
552 "'%s' '%s' '%s'\n", vendor
, product
, revision
));
553 atapi_probe_device(atapi
, target
, periph
, &sa
);
554 /* atapi_probe_device() frees the periph when there is no device.*/