From owner-svn-src-stable-11@freebsd.org Sun Apr 9 19:30:52 2017 Return-Path: Delivered-To: svn-src-stable-11@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 0E149D36078; Sun, 9 Apr 2017 19:30:52 +0000 (UTC) (envelope-from trasz@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id BF0A9EB9; Sun, 9 Apr 2017 19:30:51 +0000 (UTC) (envelope-from trasz@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id v39JUoaN002770; Sun, 9 Apr 2017 19:30:50 GMT (envelope-from trasz@FreeBSD.org) Received: (from trasz@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id v39JUosg002035; Sun, 9 Apr 2017 19:30:50 GMT (envelope-from trasz@FreeBSD.org) Message-Id: <201704091930.v39JUosg002035@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: trasz set sender to trasz@FreeBSD.org using -f From: Edward Tomasz Napierala Date: Sun, 9 Apr 2017 19:30:50 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org Subject: svn commit: r316660 - in stable/11: share/man/man4 sys/conf sys/dev/usb/storage sys/modules/usb sys/modules/usb/cfumass X-SVN-Group: stable-11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable-11@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: SVN commit messages for only the 11-stable src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 09 Apr 2017 19:30:52 -0000 Author: trasz Date: Sun Apr 9 19:30:49 2017 New Revision: 316660 URL: https://svnweb.freebsd.org/changeset/base/316660 Log: MFC r313959: Add USB Mass Storage CTL frontend. This makes it possible for USB OTG-capable hardware to implement device side of USB Mass Storage, ie pretend it's a flash drive. It's configured in the same way as other CTL frontends, using ctladm(8) or ctld(8). Differently from usfs(4), all the configuration can be done without rebuilding the kernel. Testing and review is welcome. Right now I'm still moving, and I don't have access to my test environment, so I'm somewhat reluctant to making larger changes to this code; on the other hand I don't want to let it sit on Phab until my testing setup is back, because I want to get it into 11.1-RELEASE. Relnotes: yes Sponsored by: The FreeBSD Foundation Added: stable/11/share/man/man4/cfumass.4 - copied unchanged from r313959, head/share/man/man4/cfumass.4 stable/11/sys/dev/usb/storage/cfumass.c - copied unchanged from r313959, head/sys/dev/usb/storage/cfumass.c stable/11/sys/modules/usb/cfumass/ - copied from r313959, head/sys/modules/usb/cfumass/ Modified: stable/11/share/man/man4/Makefile stable/11/share/man/man4/ctl.4 stable/11/share/man/man4/umass.4 stable/11/share/man/man4/usb_template.4 stable/11/share/man/man4/usfs.4 stable/11/sys/conf/files stable/11/sys/modules/usb/Makefile Directory Properties: stable/11/ (props changed) Modified: stable/11/share/man/man4/Makefile ============================================================================== --- stable/11/share/man/man4/Makefile Sun Apr 9 18:31:33 2017 (r316659) +++ stable/11/share/man/man4/Makefile Sun Apr 9 19:30:49 2017 (r316660) @@ -102,6 +102,7 @@ MAN= aac.4 \ cd.4 \ cdce.4 \ cfi.4 \ + cfumass.4 \ ch.4 \ chromebook_platform.4 \ ciss.4 \ Copied: stable/11/share/man/man4/cfumass.4 (from r313959, head/share/man/man4/cfumass.4) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ stable/11/share/man/man4/cfumass.4 Sun Apr 9 19:30:49 2017 (r316660, copy of r313959, head/share/man/man4/cfumass.4) @@ -0,0 +1,137 @@ +.\" Copyright (c) 2017 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This software was developed by Edward Tomasz Napierala under sponsorship +.\" from the FreeBSD Foundation. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.Dd January 27, 2017 +.Dt CFUMASS 4 +.Os +.Sh NAME +.Nm cfumass +.Nd USB device side support for Mass Storage Class Transport +.Sh SYNOPSIS +This driver can be compiled into the kernel by placing these lines in +the kernel configuration file: +.Bd -ragged -offset indent +.Cd "device usb" +.Cd "device usb_template" +.Cd "device iscsi" +.Cd "device ctl" +.Cd "device cfumass" +.Ed +.Pp +The driver module can also be loaded at boot by adding this line to +.Xr loader.conf 5 : +.Bd -literal -offset indent +cfumass_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver provides device side support for emulating an USB mass storage +device compliant with the USB Mass Storage Class Bulk-Only (BBB) Transport +specification, implemented as a +.Xr ctl 4 +frontend driver. +.Pp +To use +.Nm : +.Bl -bullet +.It +.Xr cfumass 4 +must be loaded as a module or compiled into the kernel. +.It +The USB Mass Storage template must be chosen by setting the +.Va hw.usb.template +sysctl to 0. +.It +The USB OTG port must be working in USB device-side mode. +This happens automatically upon connection to a USB host. +.It +There must be a +.Xr ctl +LUN configured for the +.Pa cfumass +port. +.El +.Pp +Upon loading, the driver creates a +.Xr ctl 4 +port named +.Pa cfumass , +presenting the first LUN mapped for that port - usually LUN 0 - to +the USB host. +See +.Xr ctl.conf 5 +and +.Xr ctld 8 +for details on configuring the LUN. +.Sh SYSCTL VARIABLES +These variables are available as both +.Xr sysctl 8 +variables and +.Xr loader 8 +tunables: +.Bl -tag -width indent +.It Va hw.usb.cfumass.debug +Verbosity level for log messages from the +.Nm +driver. +Set to 0 to disable logging or 1 to warn about potential problems. +Larger values enable debugging output. +Defaults to 1. +.It Va hw.usb.cfumass.ignore_stop +Ignore START STOP UNIT SCSI commands with START and LOEJ bits cleared. +Some initiators send that command to stop the target when the user +attempts to gracefully eject the drive, but fail to start it when the +drive is reconnected. +Set to 0 to handle the command in a standards-compliant way, +1 to ignore it and log a warning, or 2 to ignore it silently. +Defaults to 1. +.It Va hw.usb.cfumass.max_lun +Max LUN number to report to the initiator (USB host). +Must be between 0 and 15. +Some initiators incorrectly handle values larger than 0. +Defaults to 0. +.El +.Sh SEE ALSO +.Xr ctl 4 , +.Xr umass 4 , +.Xr usb 4 , +.Xr usb_template 4 , +.Xr ctl.conf 5 , +.Xr ctld 8 +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 11.1 . +.Sh AUTHORS +The +.Nm +driver was developed by +.An Edward Tomasz Napierala Aq Mt trasz@FreeBSD.org +under sponsorship from the FreeBSD Foundation. Modified: stable/11/share/man/man4/ctl.4 ============================================================================== --- stable/11/share/man/man4/ctl.4 Sun Apr 9 18:31:33 2017 (r316659) +++ stable/11/share/man/man4/ctl.4 Sun Apr 9 19:30:49 2017 (r316660) @@ -212,6 +212,7 @@ Set to 0 to disable sending NOP-In PDUs. Defaults to 5. .El .Sh SEE ALSO +.Xr cfumass 4 , .Xr ctladm 8 , .Xr ctld 8 , .Xr ctlstat 8 Modified: stable/11/share/man/man4/umass.4 ============================================================================== --- stable/11/share/man/man4/umass.4 Sun Apr 9 18:31:33 2017 (r316659) +++ stable/11/share/man/man4/umass.4 Sun Apr 9 19:30:49 2017 (r316660) @@ -239,6 +239,7 @@ type as when using .Xr mount 8 . .Sh SEE ALSO +.Xr cfumass 4 , .Xr ehci 4 , .Xr ohci 4 , .Xr uhci 4 , Modified: stable/11/share/man/man4/usb_template.4 ============================================================================== --- stable/11/share/man/man4/usb_template.4 Sun Apr 9 18:31:33 2017 (r316659) +++ stable/11/share/man/man4/usb_template.4 Sun Apr 9 19:30:49 2017 (r316660) @@ -84,6 +84,7 @@ Available templates are: .El . .Sh SEE ALSO +.Xr cfumass 4 , .Xr usb 4 , .Xr usfs 4 .Sh STANDARDS Modified: stable/11/share/man/man4/usfs.4 ============================================================================== --- stable/11/share/man/man4/usfs.4 Sun Apr 9 18:31:33 2017 (r316659) +++ stable/11/share/man/man4/usfs.4 Sun Apr 9 19:30:49 2017 (r316660) @@ -58,6 +58,7 @@ sysctl is set to 0). .Pp Upon attach the driver creates a RAM disk which can be read and written. .Sh SEE ALSO +.Xr cfumass 4 , .Xr umass 4 , .Xr usb 4 , .Xr usb_template 4 Modified: stable/11/sys/conf/files ============================================================================== --- stable/11/sys/conf/files Sun Apr 9 18:31:33 2017 (r316659) +++ stable/11/sys/conf/files Sun Apr 9 19:30:49 2017 (r316660) @@ -2772,6 +2772,7 @@ dev/usb/controller/usb_controller.c opti # # USB storage drivers # +dev/usb/storage/cfumass.c optional cfumass ctl dev/usb/storage/umass.c optional umass dev/usb/storage/urio.c optional urio dev/usb/storage/ustorage_fs.c optional usfs Copied: stable/11/sys/dev/usb/storage/cfumass.c (from r313959, head/sys/dev/usb/storage/cfumass.c) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ stable/11/sys/dev/usb/storage/cfumass.c Sun Apr 9 19:30:49 2017 (r316660, copy of r313959, head/sys/dev/usb/storage/cfumass.c) @@ -0,0 +1,1075 @@ +/*- + * Copyright (c) 2016 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +/* + * USB Mass Storage Class Bulk-Only (BBB) Transport target. + * + * http://www.usb.org/developers/docs/devclass_docs/usbmassbulk_10.pdf + * + * This code implements the USB Mass Storage frontend driver for the CAM + * Target Layer (ctl(4)) subsystem. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "usbdevs.h" +#include "usb_if.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SYSCTL_NODE(_hw_usb, OID_AUTO, cfumass, CTLFLAG_RW, 0, + "CAM Target Layer USB Mass Storage Frontend"); +static int debug = 1; +SYSCTL_INT(_hw_usb_cfumass, OID_AUTO, debug, CTLFLAG_RWTUN, + &debug, 1, "Enable debug messages"); +static int max_lun = 0; +SYSCTL_INT(_hw_usb_cfumass, OID_AUTO, max_lun, CTLFLAG_RWTUN, + &max_lun, 1, "Maximum advertised LUN number"); +static int ignore_stop = 1; +SYSCTL_INT(_hw_usb_cfumass, OID_AUTO, ignore_stop, CTLFLAG_RWTUN, + &ignore_stop, 1, "Ignore START STOP UNIT with START and LOEJ bits cleared"); + +/* + * The driver uses a single, global CTL port. It could create its ports + * in cfumass_attach() instead, but that would make it impossible to specify + * "port cfumass0" in ctl.conf(5), as the port generally wouldn't exist + * at the time ctld(8) gets run. + */ +struct ctl_port cfumass_port; +bool cfumass_port_online; +volatile u_int cfumass_refcount; + +#ifndef CFUMASS_BULK_SIZE +#define CFUMASS_BULK_SIZE (1U << 17) /* bytes */ +#endif + +/* + * USB transfer definitions. + */ +#define CFUMASS_T_COMMAND 0 +#define CFUMASS_T_DATA_OUT 1 +#define CFUMASS_T_DATA_IN 2 +#define CFUMASS_T_STATUS 3 +#define CFUMASS_T_MAX 4 + +/* + * USB interface specific control requests. + */ +#define UR_RESET 0xff /* Bulk-Only Mass Storage Reset */ +#define UR_GET_MAX_LUN 0xfe /* Get Max LUN */ + +/* + * Command Block Wrapper. + */ +struct cfumass_cbw_t { + uDWord dCBWSignature; +#define CBWSIGNATURE 0x43425355 /* "USBC" */ + uDWord dCBWTag; + uDWord dCBWDataTransferLength; + uByte bCBWFlags; +#define CBWFLAGS_OUT 0x00 +#define CBWFLAGS_IN 0x80 + uByte bCBWLUN; + uByte bCDBLength; +#define CBWCBLENGTH 16 + uByte CBWCB[CBWCBLENGTH]; +} __packed; + +#define CFUMASS_CBW_SIZE 31 +CTASSERT(sizeof(struct cfumass_cbw_t) == CFUMASS_CBW_SIZE); + +/* + * Command Status Wrapper. + */ +struct cfumass_csw_t { + uDWord dCSWSignature; +#define CSWSIGNATURE 0x53425355 /* "USBS" */ + uDWord dCSWTag; + uDWord dCSWDataResidue; + uByte bCSWStatus; +#define CSWSTATUS_GOOD 0x0 +#define CSWSTATUS_FAILED 0x1 +#define CSWSTATUS_PHASE 0x2 +} __packed; + +#define CFUMASS_CSW_SIZE 13 +CTASSERT(sizeof(struct cfumass_csw_t) == CFUMASS_CSW_SIZE); + +struct cfumass_softc { + device_t sc_dev; + struct usb_device *sc_udev; + struct usb_xfer *sc_xfer[CFUMASS_T_MAX]; + + struct cfumass_cbw_t *sc_cbw; + struct cfumass_csw_t *sc_csw; + + struct mtx sc_mtx; + int sc_online; + int sc_ctl_initid; + + /* + * This is used to communicate between CTL callbacks + * and USB callbacks; basically, it holds the state + * for the current command ("the" command, since there + * is no queueing in USB Mass Storage). + */ + bool sc_current_stalled; + + /* + * The following are set upon receiving a SCSI command. + */ + int sc_current_tag; + int sc_current_transfer_length; + int sc_current_flags; + + /* + * The following are set in ctl_datamove(). + */ + int sc_current_residue; + union ctl_io *sc_ctl_io; + + /* + * The following is set in cfumass_done(). + */ + int sc_current_status; + + /* + * Number of requests queued to CTL. + */ + volatile u_int sc_queued; +}; + +/* + * USB interface. + */ +static device_probe_t cfumass_probe; +static device_attach_t cfumass_attach; +static device_detach_t cfumass_detach; +static device_suspend_t cfumass_suspend; +static device_resume_t cfumass_resume; +static usb_handle_request_t cfumass_handle_request; + +static usb_callback_t cfumass_t_command_callback; +static usb_callback_t cfumass_t_data_out_callback; +static usb_callback_t cfumass_t_data_in_callback; +static usb_callback_t cfumass_t_status_callback; + +static device_method_t cfumass_methods[] = { + + /* USB interface. */ + DEVMETHOD(usb_handle_request, cfumass_handle_request), + + /* Device interface. */ + DEVMETHOD(device_probe, cfumass_probe), + DEVMETHOD(device_attach, cfumass_attach), + DEVMETHOD(device_detach, cfumass_detach), + DEVMETHOD(device_suspend, cfumass_suspend), + DEVMETHOD(device_resume, cfumass_resume), + + DEVMETHOD_END +}; + +static driver_t cfumass_driver = { + .name = "cfumass", + .methods = cfumass_methods, + .size = sizeof(struct cfumass_softc), +}; + +static devclass_t cfumass_devclass; + +DRIVER_MODULE(cfumass, uhub, cfumass_driver, cfumass_devclass, NULL, 0); +MODULE_VERSION(cfumass, 0); +MODULE_DEPEND(cfumass, usb, 1, 1, 1); +MODULE_DEPEND(cfumass, usb_template, 1, 1, 1); + +static struct usb_config cfumass_config[CFUMASS_T_MAX] = { + + [CFUMASS_T_COMMAND] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = sizeof(struct cfumass_cbw_t), + .callback = &cfumass_t_command_callback, + .usb_mode = USB_MODE_DEVICE, + }, + + [CFUMASS_T_DATA_OUT] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = CFUMASS_BULK_SIZE, + .flags = {.proxy_buffer = 1, .short_xfer_ok = 1, + .ext_buffer = 1}, + .callback = &cfumass_t_data_out_callback, + .usb_mode = USB_MODE_DEVICE, + }, + + [CFUMASS_T_DATA_IN] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = CFUMASS_BULK_SIZE, + .flags = {.proxy_buffer = 1, .short_xfer_ok = 1, + .ext_buffer = 1}, + .callback = &cfumass_t_data_in_callback, + .usb_mode = USB_MODE_DEVICE, + }, + + [CFUMASS_T_STATUS] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = sizeof(struct cfumass_csw_t), + .flags = {.short_xfer_ok = 1}, + .callback = &cfumass_t_status_callback, + .usb_mode = USB_MODE_DEVICE, + }, +}; + +/* + * CTL frontend interface. + */ +static int cfumass_init(void); +static int cfumass_shutdown(void); +static void cfumass_online(void *arg); +static void cfumass_offline(void *arg); +static void cfumass_datamove(union ctl_io *io); +static void cfumass_done(union ctl_io *io); + +static struct ctl_frontend cfumass_frontend = { + .name = "umass", + .init = cfumass_init, + .shutdown = cfumass_shutdown, +}; +CTL_FRONTEND_DECLARE(ctlcfumass, cfumass_frontend); + +#define CFUMASS_DEBUG(S, X, ...) \ + do { \ + if (debug > 1) { \ + device_printf(S->sc_dev, "%s: " X "\n", \ + __func__, ## __VA_ARGS__); \ + } \ + } while (0) + +#define CFUMASS_WARN(S, X, ...) \ + do { \ + if (debug > 0) { \ + device_printf(S->sc_dev, "WARNING: %s: " X "\n",\ + __func__, ## __VA_ARGS__); \ + } \ + } while (0) + +#define CFUMASS_LOCK(X) mtx_lock(&X->sc_mtx) +#define CFUMASS_UNLOCK(X) mtx_unlock(&X->sc_mtx) + +static void cfumass_transfer_start(struct cfumass_softc *sc, + uint8_t xfer_index); +static void cfumass_terminate(struct cfumass_softc *sc); + +static int +cfumass_probe(device_t dev) +{ + struct usb_attach_arg *uaa; + struct usb_interface_descriptor *id; + + uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_DEVICE) + return (ENXIO); + + /* + * Check for a compliant device. + */ + id = usbd_get_interface_descriptor(uaa->iface); + if ((id == NULL) || + (id->bInterfaceClass != UICLASS_MASS) || + (id->bInterfaceSubClass != UISUBCLASS_SCSI) || + (id->bInterfaceProtocol != UIPROTO_MASS_BBB)) { + return (ENXIO); + } + + return (BUS_PROBE_GENERIC); +} + +static int +cfumass_attach(device_t dev) +{ + struct cfumass_softc *sc; + struct usb_attach_arg *uaa; + int error; + + sc = device_get_softc(dev); + uaa = device_get_ivars(dev); + + sc->sc_dev = dev; + sc->sc_udev = uaa->device; + + CFUMASS_DEBUG(sc, "go"); + + usbd_set_power_mode(uaa->device, USB_POWER_MODE_SAVE); + device_set_usb_desc(dev); + + mtx_init(&sc->sc_mtx, "cfumass", NULL, MTX_DEF); + refcount_acquire(&cfumass_refcount); + + error = usbd_transfer_setup(uaa->device, + &uaa->info.bIfaceIndex, sc->sc_xfer, cfumass_config, + CFUMASS_T_MAX, sc, &sc->sc_mtx); + if (error != 0) { + CFUMASS_WARN(sc, "usbd_transfer_setup() failed: %s", + usbd_errstr(error)); + refcount_release(&cfumass_refcount); + return (ENXIO); + } + + sc->sc_cbw = + usbd_xfer_get_frame_buffer(sc->sc_xfer[CFUMASS_T_COMMAND], 0); + sc->sc_csw = + usbd_xfer_get_frame_buffer(sc->sc_xfer[CFUMASS_T_STATUS], 0); + + sc->sc_ctl_initid = ctl_add_initiator(&cfumass_port, -1, 0, NULL); + if (sc->sc_ctl_initid < 0) { + CFUMASS_WARN(sc, "ctl_add_initiator() failed with error %d", + sc->sc_ctl_initid); + usbd_transfer_unsetup(sc->sc_xfer, CFUMASS_T_MAX); + refcount_release(&cfumass_refcount); + return (ENXIO); + } + + refcount_init(&sc->sc_queued, 0); + + CFUMASS_LOCK(sc); + cfumass_transfer_start(sc, CFUMASS_T_COMMAND); + CFUMASS_UNLOCK(sc); + + return (0); +} + +static int +cfumass_detach(device_t dev) +{ + struct cfumass_softc *sc; + int error; + + sc = device_get_softc(dev); + + CFUMASS_DEBUG(sc, "go"); + + CFUMASS_LOCK(sc); + cfumass_terminate(sc); + CFUMASS_UNLOCK(sc); + usbd_transfer_unsetup(sc->sc_xfer, CFUMASS_T_MAX); + + if (sc->sc_ctl_initid != -1) { + error = ctl_remove_initiator(&cfumass_port, sc->sc_ctl_initid); + if (error != 0) { + CFUMASS_WARN(sc, "ctl_remove_initiator() failed " + "with error %d", error); + } + sc->sc_ctl_initid = -1; + } + + mtx_destroy(&sc->sc_mtx); + refcount_release(&cfumass_refcount); + + return (0); +} + +static int +cfumass_suspend(device_t dev) +{ + struct cfumass_softc *sc; + + sc = device_get_softc(dev); + CFUMASS_DEBUG(sc, "go"); + + return (0); +} + +static int +cfumass_resume(device_t dev) +{ + struct cfumass_softc *sc; + + sc = device_get_softc(dev); + CFUMASS_DEBUG(sc, "go"); + + return (0); +} + +static void +cfumass_transfer_start(struct cfumass_softc *sc, uint8_t xfer_index) +{ + + usbd_transfer_start(sc->sc_xfer[xfer_index]); +} + +static void +cfumass_transfer_stop_and_drain(struct cfumass_softc *sc, uint8_t xfer_index) +{ + + usbd_transfer_stop(sc->sc_xfer[xfer_index]); + CFUMASS_UNLOCK(sc); + usbd_transfer_drain(sc->sc_xfer[xfer_index]); + CFUMASS_LOCK(sc); +} + +static void +cfumass_terminate(struct cfumass_softc *sc) +{ + int last; + + for (;;) { + cfumass_transfer_stop_and_drain(sc, CFUMASS_T_COMMAND); + cfumass_transfer_stop_and_drain(sc, CFUMASS_T_DATA_IN); + cfumass_transfer_stop_and_drain(sc, CFUMASS_T_DATA_OUT); + + if (sc->sc_ctl_io != NULL) { + CFUMASS_DEBUG(sc, "terminating CTL transfer"); + ctl_set_data_phase_error(&sc->sc_ctl_io->scsiio); + sc->sc_ctl_io->scsiio.be_move_done(sc->sc_ctl_io); + sc->sc_ctl_io = NULL; + } + + cfumass_transfer_stop_and_drain(sc, CFUMASS_T_STATUS); + + refcount_acquire(&sc->sc_queued); + last = refcount_release(&sc->sc_queued); + if (last != 0) + break; + + CFUMASS_DEBUG(sc, "%d CTL tasks pending", sc->sc_queued); + msleep(__DEVOLATILE(void *, &sc->sc_queued), &sc->sc_mtx, + 0, "cfumass_reset", hz / 100); + } +} + +static int +cfumass_handle_request(device_t dev, + const void *preq, void **pptr, uint16_t *plen, + uint16_t offset, uint8_t *pstate) +{ + static uint8_t max_lun_tmp; + struct cfumass_softc *sc; + const struct usb_device_request *req; + uint8_t is_complete; + + sc = device_get_softc(dev); + req = preq; + is_complete = *pstate; + + CFUMASS_DEBUG(sc, "go"); + + if (is_complete) + return (ENXIO); + + if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) && + (req->bRequest == UR_RESET)) { + CFUMASS_WARN(sc, "received Bulk-Only Mass Storage Reset"); + *plen = 0; + + CFUMASS_LOCK(sc); + cfumass_terminate(sc); + cfumass_transfer_start(sc, CFUMASS_T_COMMAND); + CFUMASS_UNLOCK(sc); + + CFUMASS_DEBUG(sc, "Bulk-Only Mass Storage Reset done"); + return (0); + } + + if ((req->bmRequestType == UT_READ_CLASS_INTERFACE) && + (req->bRequest == UR_GET_MAX_LUN)) { + CFUMASS_DEBUG(sc, "received Get Max LUN"); + if (offset == 0) { + *plen = 1; + /* + * The protocol doesn't support LUN numbers higher + * than 15. Also, some initiators (namely Windows XP + * SP3 Version 2002) can't properly query the number + * of LUNs, resulting in inaccessible "fake" ones - thus + * the default limit of one LUN. + */ + if (max_lun < 0 || max_lun > 15) { + CFUMASS_WARN(sc, + "invalid hw.usb.cfumass.max_lun, must be " + "between 0 and 15; defaulting to 0"); + max_lun_tmp = 0; + } else { + max_lun_tmp = max_lun; + } + *pptr = &max_lun_tmp; + } else { + *plen = 0; + } + return (0); + } + + return (ENXIO); +} + +static int +cfumass_quirk(struct cfumass_softc *sc, unsigned char *cdb, int cdb_len) +{ + struct scsi_start_stop_unit *sssu; + + switch (cdb[0]) { + case START_STOP_UNIT: + /* + * Some initiators - eg OSX, Darwin Kernel Version 15.6.0, + * root:xnu-3248.60.11~2/RELEASE_X86_64 - attempt to stop + * the unit on eject, but fail to start it when it's plugged + * back. Just ignore the command. + */ + + if (cdb_len < sizeof(*sssu)) { + CFUMASS_DEBUG(sc, "received START STOP UNIT with " + "bCDBLength %d, should be %zd", + cdb_len, sizeof(*sssu)); + break; + } + + sssu = (struct scsi_start_stop_unit *)cdb; + if ((sssu->how & SSS_PC_MASK) != 0) + break; + + if ((sssu->how & SSS_START) != 0) + break; + + if ((sssu->how & SSS_LOEJ) != 0) + break; + + if (ignore_stop == 0) { + break; + } else if (ignore_stop == 1) { + CFUMASS_WARN(sc, "ignoring START STOP UNIT request"); + } else { + CFUMASS_DEBUG(sc, "ignoring START STOP UNIT request"); + } + + sc->sc_current_status = 0; + cfumass_transfer_start(sc, CFUMASS_T_STATUS); + + return (1); + default: + break; + } + + return (0); +} + +static void +cfumass_t_command_callback(struct usb_xfer *xfer, usb_error_t usb_error) +{ + struct cfumass_softc *sc; + uint32_t signature; + union ctl_io *io; + int error = 0; + + sc = usbd_xfer_softc(xfer); + + KASSERT(sc->sc_ctl_io == NULL, + ("sc_ctl_io is %p, should be NULL", sc->sc_ctl_io)); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + CFUMASS_DEBUG(sc, "USB_ST_TRANSFERRED"); + + signature = UGETDW(sc->sc_cbw->dCBWSignature); + if (signature != CBWSIGNATURE) { + CFUMASS_WARN(sc, "wrong dCBWSignature 0x%08x, " + "should be 0x%08x", signature, CBWSIGNATURE); + break; + } + + if (sc->sc_cbw->bCDBLength <= 0 || + sc->sc_cbw->bCDBLength > sizeof(sc->sc_cbw->CBWCB)) { + CFUMASS_WARN(sc, "invalid bCDBLength %d, should be <= %zd", + sc->sc_cbw->bCDBLength, sizeof(sc->sc_cbw->CBWCB)); + break; + } + + sc->sc_current_stalled = false; + sc->sc_current_status = 0; + sc->sc_current_tag = UGETDW(sc->sc_cbw->dCBWTag); + sc->sc_current_transfer_length = + UGETDW(sc->sc_cbw->dCBWDataTransferLength); + sc->sc_current_flags = sc->sc_cbw->bCBWFlags; + + /* + * Make sure to report proper residue if the datamove wasn't + * required, or wasn't called due to SCSI error. + */ + sc->sc_current_residue = sc->sc_current_transfer_length; + + if (cfumass_quirk(sc, + sc->sc_cbw->CBWCB, sc->sc_cbw->bCDBLength) != 0) + break; + + if (!cfumass_port_online) { + CFUMASS_DEBUG(sc, "cfumass port is offline; stalling"); + usbd_xfer_set_stall(xfer); + break; + } + + /* + * Those CTL functions cannot be called with mutex held. + */ + CFUMASS_UNLOCK(sc); + io = ctl_alloc_io(cfumass_port.ctl_pool_ref); + ctl_zero_io(io); + io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = sc; + io->io_hdr.io_type = CTL_IO_SCSI; + io->io_hdr.nexus.initid = sc->sc_ctl_initid; + io->io_hdr.nexus.targ_port = cfumass_port.targ_port; + io->io_hdr.nexus.targ_lun = ctl_decode_lun(sc->sc_cbw->bCBWLUN); + io->scsiio.tag_num = UGETDW(sc->sc_cbw->dCBWTag); + io->scsiio.tag_type = CTL_TAG_UNTAGGED; + io->scsiio.cdb_len = sc->sc_cbw->bCDBLength; + memcpy(io->scsiio.cdb, sc->sc_cbw->CBWCB, sc->sc_cbw->bCDBLength); + refcount_acquire(&sc->sc_queued); + error = ctl_queue(io); + if (error != CTL_RETVAL_COMPLETE) { + CFUMASS_WARN(sc, + "ctl_queue() failed; error %d; stalling", error); + ctl_free_io(io); + refcount_release(&sc->sc_queued); + CFUMASS_LOCK(sc); + usbd_xfer_set_stall(xfer); + break; + } + + CFUMASS_LOCK(sc); + break; + + case USB_ST_SETUP: +tr_setup: + CFUMASS_DEBUG(sc, "USB_ST_SETUP"); + + usbd_xfer_set_frame_len(xfer, 0, sizeof(*sc->sc_cbw)); + usbd_transfer_submit(xfer); + break; + + default: + if (usb_error == USB_ERR_CANCELLED) { + CFUMASS_DEBUG(sc, "USB_ERR_CANCELLED"); + break; + } + + CFUMASS_DEBUG(sc, "USB_ST_ERROR: %s", usbd_errstr(usb_error)); + + goto tr_setup; + } +} + +static void +cfumass_t_data_out_callback(struct usb_xfer *xfer, usb_error_t usb_error) +{ + struct cfumass_softc *sc; + union ctl_io *io; + struct ctl_sg_entry ctl_sg_entry, *ctl_sglist; + int actlen, ctl_sg_count; + + sc = usbd_xfer_softc(xfer); + + CFUMASS_DEBUG(sc, "go"); + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + io = sc->sc_ctl_io; + + if (io->scsiio.kern_sg_entries > 0) { + ctl_sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr; + ctl_sg_count = io->scsiio.kern_sg_entries; + } else { + ctl_sglist = &ctl_sg_entry; + ctl_sglist->addr = io->scsiio.kern_data_ptr; + ctl_sglist->len = io->scsiio.kern_data_len; + ctl_sg_count = 1; + } + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + CFUMASS_DEBUG(sc, "USB_ST_TRANSFERRED"); + + /* + * If the host sent less data than required, zero-out + * the remaining buffer space, to prevent a malicious host + * to writing uninitialized kernel memory to the disk. + */ + if (actlen != ctl_sglist[0].len) { + KASSERT(actlen <= ctl_sglist[0].len, + ("actlen %d > ctl_sglist.len %zd", + actlen, ctl_sglist[0].len)); + + CFUMASS_DEBUG(sc, "host transferred %d bytes" + "instead of expected %zd bytes", + actlen, ctl_sglist[0].len); + + memset((char *)(ctl_sglist[0].addr) + actlen, 0, + ctl_sglist[0].len - actlen); + } + + sc->sc_current_residue = 0; + io->scsiio.be_move_done(io); + sc->sc_ctl_io = NULL; + break; + + case USB_ST_SETUP: +tr_setup: + CFUMASS_DEBUG(sc, "USB_ST_SETUP"); + + CFUMASS_DEBUG(sc, "requested size %d, CTL segment size %zd", + sc->sc_current_transfer_length, ctl_sglist[0].len); + + usbd_xfer_set_frame_data(xfer, 0, ctl_sglist[0].addr, ctl_sglist[0].len); + usbd_transfer_submit(xfer); + break; + + default: + if (usb_error == USB_ERR_CANCELLED) { + CFUMASS_DEBUG(sc, "USB_ERR_CANCELLED"); + break; + } + + CFUMASS_DEBUG(sc, "USB_ST_ERROR: %s", + usbd_errstr(usb_error)); + + goto tr_setup; + } +} + +static void +cfumass_t_data_in_callback(struct usb_xfer *xfer, usb_error_t usb_error) +{ + struct cfumass_softc *sc; + union ctl_io *io; + uint32_t max_bulk; + struct ctl_sg_entry ctl_sg_entry, *ctl_sglist; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***