Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 19 Feb 2017 17:47:43 +0000 (UTC)
From:      Edward Tomasz Napierala <trasz@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r313959 - in head: share/man/man4 sys/conf sys/dev/usb/storage sys/modules/usb sys/modules/usb/cfumass
Message-ID:  <201702191747.v1JHlh5F010337@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: trasz
Date: Sun Feb 19 17:47:43 2017
New Revision: 313959
URL: https://svnweb.freebsd.org/changeset/base/313959

Log:
  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.
  
  Reviewed by:	emaste (cursory), wblock (man page)
  MFC after:	2 weeks
  Relnotes:	yes
  Sponsored by:	The FreeBSD Foundation
  Differential Revision:	https://reviews.freebsd.org/D8787

Added:
  head/share/man/man4/cfumass.4   (contents, props changed)
  head/sys/dev/usb/storage/cfumass.c   (contents, props changed)
  head/sys/modules/usb/cfumass/
  head/sys/modules/usb/cfumass/Makefile   (contents, props changed)
Modified:
  head/share/man/man4/Makefile
  head/share/man/man4/ctl.4
  head/share/man/man4/umass.4
  head/share/man/man4/usb_template.4
  head/share/man/man4/usfs.4
  head/sys/conf/files
  head/sys/modules/usb/Makefile

Modified: head/share/man/man4/Makefile
==============================================================================
--- head/share/man/man4/Makefile	Sun Feb 19 17:46:37 2017	(r313958)
+++ head/share/man/man4/Makefile	Sun Feb 19 17:47:43 2017	(r313959)
@@ -102,6 +102,7 @@ MAN=	aac.4 \
 	cd.4 \
 	cdce.4 \
 	cfi.4 \
+	cfumass.4 \
 	ch.4 \
 	ciss.4 \
 	cloudabi.4 \

Added: head/share/man/man4/cfumass.4
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/share/man/man4/cfumass.4	Sun Feb 19 17:47:43 2017	(r313959)
@@ -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: head/share/man/man4/ctl.4
==============================================================================
--- head/share/man/man4/ctl.4	Sun Feb 19 17:46:37 2017	(r313958)
+++ head/share/man/man4/ctl.4	Sun Feb 19 17:47:43 2017	(r313959)
@@ -172,6 +172,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: head/share/man/man4/umass.4
==============================================================================
--- head/share/man/man4/umass.4	Sun Feb 19 17:46:37 2017	(r313958)
+++ head/share/man/man4/umass.4	Sun Feb 19 17:47:43 2017	(r313959)
@@ -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: head/share/man/man4/usb_template.4
==============================================================================
--- head/share/man/man4/usb_template.4	Sun Feb 19 17:46:37 2017	(r313958)
+++ head/share/man/man4/usb_template.4	Sun Feb 19 17:47:43 2017	(r313959)
@@ -84,6 +84,7 @@ Available templates are:
 .El
 .
 .Sh SEE ALSO
+.Xr cfumass 4 ,
 .Xr usb 4 ,
 .Xr usfs 4
 .Sh STANDARDS

Modified: head/share/man/man4/usfs.4
==============================================================================
--- head/share/man/man4/usfs.4	Sun Feb 19 17:46:37 2017	(r313958)
+++ head/share/man/man4/usfs.4	Sun Feb 19 17:47:43 2017	(r313959)
@@ -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: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Sun Feb 19 17:46:37 2017	(r313958)
+++ head/sys/conf/files	Sun Feb 19 17:47:43 2017	(r313959)
@@ -3037,6 +3037,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

Added: head/sys/dev/usb/storage/cfumass.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/usb/storage/cfumass.c	Sun Feb 19 17:47:43 2017	(r313959)
@@ -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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/refcount.h>
+#include <sys/stdint.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include "usbdevs.h"
+#include "usb_if.h"
+
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_da.h>
+#include <cam/ctl/ctl_io.h>
+#include <cam/ctl/ctl.h>
+#include <cam/ctl/ctl_backend.h>
+#include <cam/ctl/ctl_error.h>
+#include <cam/ctl/ctl_frontend.h>
+#include <cam/ctl/ctl_debug.h>
+#include <cam/ctl/ctl_ha.h>
+#include <cam/ctl/ctl_ioctl.h>
+#include <cam/ctl/ctl_private.h>
+
+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 ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201702191747.v1JHlh5F010337>