cfumass.c revision 317337
1313959Strasz/*-
2313959Strasz * Copyright (c) 2016 The FreeBSD Foundation
3313959Strasz * All rights reserved.
4313959Strasz *
5313959Strasz * This software was developed by Edward Tomasz Napierala under sponsorship
6313959Strasz * from the FreeBSD Foundation.
7313959Strasz *
8313959Strasz * Redistribution and use in source and binary forms, with or without
9313959Strasz * modification, are permitted provided that the following conditions
10313959Strasz * are met:
11313959Strasz * 1. Redistributions of source code must retain the above copyright
12313959Strasz *    notice, this list of conditions and the following disclaimer.
13313959Strasz * 2. Redistributions in binary form must reproduce the above copyright
14313959Strasz *    notice, this list of conditions and the following disclaimer in the
15313959Strasz *    documentation and/or other materials provided with the distribution.
16313959Strasz *
17313959Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18313959Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19313959Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20313959Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21313959Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22313959Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23313959Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24313959Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25313959Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26313959Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27313959Strasz * SUCH DAMAGE.
28313959Strasz *
29313959Strasz */
30313959Strasz/*
31313959Strasz * USB Mass Storage Class Bulk-Only (BBB) Transport target.
32313959Strasz *
33313959Strasz * http://www.usb.org/developers/docs/devclass_docs/usbmassbulk_10.pdf
34313959Strasz *
35313959Strasz * This code implements the USB Mass Storage frontend driver for the CAM
36313959Strasz * Target Layer (ctl(4)) subsystem.
37313959Strasz */
38313959Strasz
39313959Strasz#include <sys/cdefs.h>
40313959Strasz__FBSDID("$FreeBSD: stable/11/sys/dev/usb/storage/cfumass.c 317337 2017-04-23 09:01:01Z mav $");
41313959Strasz
42313959Strasz#include <sys/param.h>
43313959Strasz#include <sys/bus.h>
44313959Strasz#include <sys/kernel.h>
45313959Strasz#include <sys/lock.h>
46313959Strasz#include <sys/module.h>
47313959Strasz#include <sys/mutex.h>
48313959Strasz#include <sys/refcount.h>
49313959Strasz#include <sys/stdint.h>
50313959Strasz#include <sys/sysctl.h>
51313959Strasz#include <sys/systm.h>
52313959Strasz
53313959Strasz#include <dev/usb/usb.h>
54313959Strasz#include <dev/usb/usbdi.h>
55313959Strasz#include "usbdevs.h"
56313959Strasz#include "usb_if.h"
57313959Strasz
58313959Strasz#include <cam/scsi/scsi_all.h>
59313959Strasz#include <cam/scsi/scsi_da.h>
60313959Strasz#include <cam/ctl/ctl_io.h>
61313959Strasz#include <cam/ctl/ctl.h>
62313959Strasz#include <cam/ctl/ctl_backend.h>
63313959Strasz#include <cam/ctl/ctl_error.h>
64313959Strasz#include <cam/ctl/ctl_frontend.h>
65313959Strasz#include <cam/ctl/ctl_debug.h>
66313959Strasz#include <cam/ctl/ctl_ha.h>
67313959Strasz#include <cam/ctl/ctl_ioctl.h>
68313959Strasz#include <cam/ctl/ctl_private.h>
69313959Strasz
70313959StraszSYSCTL_NODE(_hw_usb, OID_AUTO, cfumass, CTLFLAG_RW, 0,
71313959Strasz    "CAM Target Layer USB Mass Storage Frontend");
72313959Straszstatic int debug = 1;
73313959StraszSYSCTL_INT(_hw_usb_cfumass, OID_AUTO, debug, CTLFLAG_RWTUN,
74313959Strasz    &debug, 1, "Enable debug messages");
75313959Straszstatic int max_lun = 0;
76313959StraszSYSCTL_INT(_hw_usb_cfumass, OID_AUTO, max_lun, CTLFLAG_RWTUN,
77313959Strasz    &max_lun, 1, "Maximum advertised LUN number");
78313959Straszstatic int ignore_stop = 1;
79313959StraszSYSCTL_INT(_hw_usb_cfumass, OID_AUTO, ignore_stop, CTLFLAG_RWTUN,
80313959Strasz    &ignore_stop, 1, "Ignore START STOP UNIT with START and LOEJ bits cleared");
81313959Strasz
82313959Strasz/*
83313959Strasz * The driver uses a single, global CTL port.  It could create its ports
84313959Strasz * in cfumass_attach() instead, but that would make it impossible to specify
85313959Strasz * "port cfumass0" in ctl.conf(5), as the port generally wouldn't exist
86313959Strasz * at the time ctld(8) gets run.
87313959Strasz */
88313959Straszstruct ctl_port	cfumass_port;
89313959Straszbool		cfumass_port_online;
90313959Straszvolatile u_int	cfumass_refcount;
91313959Strasz
92313959Strasz#ifndef CFUMASS_BULK_SIZE
93313959Strasz#define	CFUMASS_BULK_SIZE	(1U << 17)	/* bytes */
94313959Strasz#endif
95313959Strasz
96313959Strasz/*
97313959Strasz * USB transfer definitions.
98313959Strasz */
99313959Strasz#define	CFUMASS_T_COMMAND	0
100313959Strasz#define	CFUMASS_T_DATA_OUT	1
101313959Strasz#define	CFUMASS_T_DATA_IN	2
102313959Strasz#define	CFUMASS_T_STATUS	3
103313959Strasz#define	CFUMASS_T_MAX		4
104313959Strasz
105313959Strasz/*
106313959Strasz * USB interface specific control requests.
107313959Strasz */
108313959Strasz#define	UR_RESET	0xff	/* Bulk-Only Mass Storage Reset */
109313959Strasz#define	UR_GET_MAX_LUN	0xfe	/* Get Max LUN */
110313959Strasz
111313959Strasz/*
112313959Strasz * Command Block Wrapper.
113313959Strasz */
114313959Straszstruct cfumass_cbw_t {
115313959Strasz	uDWord	dCBWSignature;
116313959Strasz#define	CBWSIGNATURE		0x43425355 /* "USBC" */
117313959Strasz	uDWord	dCBWTag;
118313959Strasz	uDWord	dCBWDataTransferLength;
119313959Strasz	uByte	bCBWFlags;
120313959Strasz#define	CBWFLAGS_OUT		0x00
121313959Strasz#define	CBWFLAGS_IN		0x80
122313959Strasz	uByte	bCBWLUN;
123313959Strasz	uByte	bCDBLength;
124313959Strasz#define	CBWCBLENGTH		16
125313959Strasz	uByte	CBWCB[CBWCBLENGTH];
126313959Strasz} __packed;
127313959Strasz
128313959Strasz#define	CFUMASS_CBW_SIZE	31
129313959StraszCTASSERT(sizeof(struct cfumass_cbw_t) == CFUMASS_CBW_SIZE);
130313959Strasz
131313959Strasz/*
132313959Strasz * Command Status Wrapper.
133313959Strasz */
134313959Straszstruct cfumass_csw_t {
135313959Strasz	uDWord	dCSWSignature;
136313959Strasz#define	CSWSIGNATURE		0x53425355 /* "USBS" */
137313959Strasz	uDWord	dCSWTag;
138313959Strasz	uDWord	dCSWDataResidue;
139313959Strasz	uByte	bCSWStatus;
140313959Strasz#define	CSWSTATUS_GOOD		0x0
141313959Strasz#define	CSWSTATUS_FAILED	0x1
142313959Strasz#define	CSWSTATUS_PHASE		0x2
143313959Strasz} __packed;
144313959Strasz
145313959Strasz#define	CFUMASS_CSW_SIZE	13
146313959StraszCTASSERT(sizeof(struct cfumass_csw_t) == CFUMASS_CSW_SIZE);
147313959Strasz
148313959Straszstruct cfumass_softc {
149313959Strasz	device_t		sc_dev;
150313959Strasz	struct usb_device	*sc_udev;
151313959Strasz	struct usb_xfer		*sc_xfer[CFUMASS_T_MAX];
152313959Strasz
153313959Strasz	struct cfumass_cbw_t *sc_cbw;
154313959Strasz	struct cfumass_csw_t *sc_csw;
155313959Strasz
156313959Strasz	struct mtx	sc_mtx;
157313959Strasz	int		sc_online;
158313959Strasz	int		sc_ctl_initid;
159313959Strasz
160313959Strasz	/*
161313959Strasz	 * This is used to communicate between CTL callbacks
162313959Strasz	 * and USB callbacks; basically, it holds the state
163313959Strasz	 * for the current command ("the" command, since there
164313959Strasz	 * is no queueing in USB Mass Storage).
165313959Strasz	 */
166313959Strasz	bool		sc_current_stalled;
167313959Strasz
168313959Strasz	/*
169313959Strasz	 * The following are set upon receiving a SCSI command.
170313959Strasz	 */
171313959Strasz	int		sc_current_tag;
172313959Strasz	int		sc_current_transfer_length;
173313959Strasz	int		sc_current_flags;
174313959Strasz
175313959Strasz	/*
176313959Strasz	 * The following are set in ctl_datamove().
177313959Strasz	 */
178313959Strasz	int		sc_current_residue;
179313959Strasz	union ctl_io	*sc_ctl_io;
180313959Strasz
181313959Strasz	/*
182313959Strasz	 * The following is set in cfumass_done().
183313959Strasz	 */
184313959Strasz	int		sc_current_status;
185313959Strasz
186313959Strasz	/*
187313959Strasz	 * Number of requests queued to CTL.
188313959Strasz	 */
189313959Strasz	volatile u_int	sc_queued;
190313959Strasz};
191313959Strasz
192313959Strasz/*
193313959Strasz * USB interface.
194313959Strasz */
195313959Straszstatic device_probe_t		cfumass_probe;
196313959Straszstatic device_attach_t		cfumass_attach;
197313959Straszstatic device_detach_t		cfumass_detach;
198313959Straszstatic device_suspend_t		cfumass_suspend;
199313959Straszstatic device_resume_t		cfumass_resume;
200313959Straszstatic usb_handle_request_t	cfumass_handle_request;
201313959Strasz
202313959Straszstatic usb_callback_t		cfumass_t_command_callback;
203313959Straszstatic usb_callback_t		cfumass_t_data_out_callback;
204313959Straszstatic usb_callback_t		cfumass_t_data_in_callback;
205313959Straszstatic usb_callback_t		cfumass_t_status_callback;
206313959Strasz
207313959Straszstatic device_method_t cfumass_methods[] = {
208313959Strasz
209313959Strasz	/* USB interface. */
210313959Strasz	DEVMETHOD(usb_handle_request, cfumass_handle_request),
211313959Strasz
212313959Strasz	/* Device interface. */
213313959Strasz	DEVMETHOD(device_probe, cfumass_probe),
214313959Strasz	DEVMETHOD(device_attach, cfumass_attach),
215313959Strasz	DEVMETHOD(device_detach, cfumass_detach),
216313959Strasz	DEVMETHOD(device_suspend, cfumass_suspend),
217313959Strasz	DEVMETHOD(device_resume, cfumass_resume),
218313959Strasz
219313959Strasz	DEVMETHOD_END
220313959Strasz};
221313959Strasz
222313959Straszstatic driver_t cfumass_driver = {
223313959Strasz	.name = "cfumass",
224313959Strasz	.methods = cfumass_methods,
225313959Strasz	.size = sizeof(struct cfumass_softc),
226313959Strasz};
227313959Strasz
228313959Straszstatic devclass_t cfumass_devclass;
229313959Strasz
230313959StraszDRIVER_MODULE(cfumass, uhub, cfumass_driver, cfumass_devclass, NULL, 0);
231313959StraszMODULE_VERSION(cfumass, 0);
232313959StraszMODULE_DEPEND(cfumass, usb, 1, 1, 1);
233313959StraszMODULE_DEPEND(cfumass, usb_template, 1, 1, 1);
234313959Strasz
235313959Straszstatic struct usb_config cfumass_config[CFUMASS_T_MAX] = {
236313959Strasz
237313959Strasz	[CFUMASS_T_COMMAND] = {
238313959Strasz		.type = UE_BULK,
239313959Strasz		.endpoint = UE_ADDR_ANY,
240313959Strasz		.direction = UE_DIR_OUT,
241313959Strasz		.bufsize = sizeof(struct cfumass_cbw_t),
242313959Strasz		.callback = &cfumass_t_command_callback,
243313959Strasz		.usb_mode = USB_MODE_DEVICE,
244313959Strasz	},
245313959Strasz
246313959Strasz	[CFUMASS_T_DATA_OUT] = {
247313959Strasz		.type = UE_BULK,
248313959Strasz		.endpoint = UE_ADDR_ANY,
249313959Strasz		.direction = UE_DIR_OUT,
250313959Strasz		.bufsize = CFUMASS_BULK_SIZE,
251313959Strasz		.flags = {.proxy_buffer = 1, .short_xfer_ok = 1,
252313959Strasz		    .ext_buffer = 1},
253313959Strasz		.callback = &cfumass_t_data_out_callback,
254313959Strasz		.usb_mode = USB_MODE_DEVICE,
255313959Strasz	},
256313959Strasz
257313959Strasz	[CFUMASS_T_DATA_IN] = {
258313959Strasz		.type = UE_BULK,
259313959Strasz		.endpoint = UE_ADDR_ANY,
260313959Strasz		.direction = UE_DIR_IN,
261313959Strasz		.bufsize = CFUMASS_BULK_SIZE,
262313959Strasz		.flags = {.proxy_buffer = 1, .short_xfer_ok = 1,
263313959Strasz		    .ext_buffer = 1},
264313959Strasz		.callback = &cfumass_t_data_in_callback,
265313959Strasz		.usb_mode = USB_MODE_DEVICE,
266313959Strasz	},
267313959Strasz
268313959Strasz	[CFUMASS_T_STATUS] = {
269313959Strasz		.type = UE_BULK,
270313959Strasz		.endpoint = UE_ADDR_ANY,
271313959Strasz		.direction = UE_DIR_IN,
272313959Strasz		.bufsize = sizeof(struct cfumass_csw_t),
273313959Strasz		.flags = {.short_xfer_ok = 1},
274313959Strasz		.callback = &cfumass_t_status_callback,
275313959Strasz		.usb_mode = USB_MODE_DEVICE,
276313959Strasz	},
277313959Strasz};
278313959Strasz
279313959Strasz/*
280313959Strasz * CTL frontend interface.
281313959Strasz */
282313959Straszstatic int	cfumass_init(void);
283313959Straszstatic int	cfumass_shutdown(void);
284313959Straszstatic void	cfumass_online(void *arg);
285313959Straszstatic void	cfumass_offline(void *arg);
286313959Straszstatic void	cfumass_datamove(union ctl_io *io);
287313959Straszstatic void	cfumass_done(union ctl_io *io);
288313959Strasz
289313959Straszstatic struct ctl_frontend cfumass_frontend = {
290313959Strasz	.name = "umass",
291313959Strasz	.init = cfumass_init,
292313959Strasz	.shutdown = cfumass_shutdown,
293313959Strasz};
294313959StraszCTL_FRONTEND_DECLARE(ctlcfumass, cfumass_frontend);
295313959Strasz
296313959Strasz#define	CFUMASS_DEBUG(S, X, ...)					\
297313959Strasz	do {								\
298313959Strasz		if (debug > 1) {					\
299313959Strasz			device_printf(S->sc_dev, "%s: " X "\n",		\
300313959Strasz			    __func__, ## __VA_ARGS__);			\
301313959Strasz		}							\
302313959Strasz	} while (0)
303313959Strasz
304313959Strasz#define	CFUMASS_WARN(S, X, ...)						\
305313959Strasz	do {								\
306313959Strasz		if (debug > 0) {					\
307313959Strasz			device_printf(S->sc_dev, "WARNING: %s: " X "\n",\
308313959Strasz			    __func__, ## __VA_ARGS__);			\
309313959Strasz		}							\
310313959Strasz	} while (0)
311313959Strasz
312313959Strasz#define CFUMASS_LOCK(X)		mtx_lock(&X->sc_mtx)
313313959Strasz#define CFUMASS_UNLOCK(X)	mtx_unlock(&X->sc_mtx)
314313959Strasz
315313959Straszstatic void	cfumass_transfer_start(struct cfumass_softc *sc,
316313959Strasz		    uint8_t xfer_index);
317313959Straszstatic void	cfumass_terminate(struct cfumass_softc *sc);
318313959Strasz
319313959Straszstatic int
320313959Straszcfumass_probe(device_t dev)
321313959Strasz{
322313959Strasz	struct usb_attach_arg *uaa;
323313959Strasz	struct usb_interface_descriptor *id;
324313959Strasz
325313959Strasz	uaa = device_get_ivars(dev);
326313959Strasz
327313959Strasz	if (uaa->usb_mode != USB_MODE_DEVICE)
328313959Strasz		return (ENXIO);
329313959Strasz
330313959Strasz	/*
331313959Strasz	 * Check for a compliant device.
332313959Strasz	 */
333313959Strasz	id = usbd_get_interface_descriptor(uaa->iface);
334313959Strasz	if ((id == NULL) ||
335313959Strasz	    (id->bInterfaceClass != UICLASS_MASS) ||
336313959Strasz	    (id->bInterfaceSubClass != UISUBCLASS_SCSI) ||
337313959Strasz	    (id->bInterfaceProtocol != UIPROTO_MASS_BBB)) {
338313959Strasz		return (ENXIO);
339313959Strasz	}
340313959Strasz
341313959Strasz	return (BUS_PROBE_GENERIC);
342313959Strasz}
343313959Strasz
344313959Straszstatic int
345313959Straszcfumass_attach(device_t dev)
346313959Strasz{
347313959Strasz	struct cfumass_softc *sc;
348313959Strasz	struct usb_attach_arg *uaa;
349313959Strasz	int error;
350313959Strasz
351313959Strasz	sc = device_get_softc(dev);
352313959Strasz	uaa = device_get_ivars(dev);
353313959Strasz
354313959Strasz	sc->sc_dev = dev;
355313959Strasz	sc->sc_udev = uaa->device;
356313959Strasz
357313959Strasz	CFUMASS_DEBUG(sc, "go");
358313959Strasz
359313959Strasz	usbd_set_power_mode(uaa->device, USB_POWER_MODE_SAVE);
360313959Strasz	device_set_usb_desc(dev);
361313959Strasz
362313959Strasz	mtx_init(&sc->sc_mtx, "cfumass", NULL, MTX_DEF);
363313959Strasz	refcount_acquire(&cfumass_refcount);
364313959Strasz
365313959Strasz	error = usbd_transfer_setup(uaa->device,
366313959Strasz	    &uaa->info.bIfaceIndex, sc->sc_xfer, cfumass_config,
367313959Strasz	    CFUMASS_T_MAX, sc, &sc->sc_mtx);
368313959Strasz	if (error != 0) {
369313959Strasz		CFUMASS_WARN(sc, "usbd_transfer_setup() failed: %s",
370313959Strasz		    usbd_errstr(error));
371313959Strasz		refcount_release(&cfumass_refcount);
372313959Strasz		return (ENXIO);
373313959Strasz	}
374313959Strasz
375313959Strasz	sc->sc_cbw =
376313959Strasz	    usbd_xfer_get_frame_buffer(sc->sc_xfer[CFUMASS_T_COMMAND], 0);
377313959Strasz	sc->sc_csw =
378313959Strasz	    usbd_xfer_get_frame_buffer(sc->sc_xfer[CFUMASS_T_STATUS], 0);
379313959Strasz
380313959Strasz	sc->sc_ctl_initid = ctl_add_initiator(&cfumass_port, -1, 0, NULL);
381313959Strasz	if (sc->sc_ctl_initid < 0) {
382313959Strasz		CFUMASS_WARN(sc, "ctl_add_initiator() failed with error %d",
383313959Strasz		    sc->sc_ctl_initid);
384313959Strasz		usbd_transfer_unsetup(sc->sc_xfer, CFUMASS_T_MAX);
385313959Strasz		refcount_release(&cfumass_refcount);
386313959Strasz		return (ENXIO);
387313959Strasz	}
388313959Strasz
389313959Strasz	refcount_init(&sc->sc_queued, 0);
390313959Strasz
391313959Strasz	CFUMASS_LOCK(sc);
392313959Strasz	cfumass_transfer_start(sc, CFUMASS_T_COMMAND);
393313959Strasz	CFUMASS_UNLOCK(sc);
394313959Strasz
395313959Strasz	return (0);
396313959Strasz}
397313959Strasz
398313959Straszstatic int
399313959Straszcfumass_detach(device_t dev)
400313959Strasz{
401313959Strasz	struct cfumass_softc *sc;
402313959Strasz	int error;
403313959Strasz
404313959Strasz	sc = device_get_softc(dev);
405313959Strasz
406313959Strasz	CFUMASS_DEBUG(sc, "go");
407313959Strasz
408313959Strasz	CFUMASS_LOCK(sc);
409313959Strasz	cfumass_terminate(sc);
410313959Strasz	CFUMASS_UNLOCK(sc);
411313959Strasz	usbd_transfer_unsetup(sc->sc_xfer, CFUMASS_T_MAX);
412313959Strasz
413313959Strasz	if (sc->sc_ctl_initid != -1) {
414313959Strasz		error = ctl_remove_initiator(&cfumass_port, sc->sc_ctl_initid);
415313959Strasz		if (error != 0) {
416313959Strasz			CFUMASS_WARN(sc, "ctl_remove_initiator() failed "
417313959Strasz			    "with error %d", error);
418313959Strasz		}
419313959Strasz		sc->sc_ctl_initid = -1;
420313959Strasz	}
421313959Strasz
422313959Strasz	mtx_destroy(&sc->sc_mtx);
423313959Strasz	refcount_release(&cfumass_refcount);
424313959Strasz
425313959Strasz	return (0);
426313959Strasz}
427313959Strasz
428313959Straszstatic int
429313959Straszcfumass_suspend(device_t dev)
430313959Strasz{
431313959Strasz	struct cfumass_softc *sc;
432313959Strasz
433313959Strasz	sc = device_get_softc(dev);
434313959Strasz	CFUMASS_DEBUG(sc, "go");
435313959Strasz
436313959Strasz	return (0);
437313959Strasz}
438313959Strasz
439313959Straszstatic int
440313959Straszcfumass_resume(device_t dev)
441313959Strasz{
442313959Strasz	struct cfumass_softc *sc;
443313959Strasz
444313959Strasz	sc = device_get_softc(dev);
445313959Strasz	CFUMASS_DEBUG(sc, "go");
446313959Strasz
447313959Strasz	return (0);
448313959Strasz}
449313959Strasz
450313959Straszstatic void
451313959Straszcfumass_transfer_start(struct cfumass_softc *sc, uint8_t xfer_index)
452313959Strasz{
453313959Strasz
454313959Strasz	usbd_transfer_start(sc->sc_xfer[xfer_index]);
455313959Strasz}
456313959Strasz
457313959Straszstatic void
458313959Straszcfumass_transfer_stop_and_drain(struct cfumass_softc *sc, uint8_t xfer_index)
459313959Strasz{
460313959Strasz
461313959Strasz	usbd_transfer_stop(sc->sc_xfer[xfer_index]);
462313959Strasz	CFUMASS_UNLOCK(sc);
463313959Strasz	usbd_transfer_drain(sc->sc_xfer[xfer_index]);
464313959Strasz	CFUMASS_LOCK(sc);
465313959Strasz}
466313959Strasz
467313959Straszstatic void
468313959Straszcfumass_terminate(struct cfumass_softc *sc)
469313959Strasz{
470313959Strasz	int last;
471313959Strasz
472313959Strasz	for (;;) {
473313959Strasz		cfumass_transfer_stop_and_drain(sc, CFUMASS_T_COMMAND);
474313959Strasz		cfumass_transfer_stop_and_drain(sc, CFUMASS_T_DATA_IN);
475313959Strasz		cfumass_transfer_stop_and_drain(sc, CFUMASS_T_DATA_OUT);
476313959Strasz
477313959Strasz		if (sc->sc_ctl_io != NULL) {
478313959Strasz			CFUMASS_DEBUG(sc, "terminating CTL transfer");
479313959Strasz			ctl_set_data_phase_error(&sc->sc_ctl_io->scsiio);
480313959Strasz			sc->sc_ctl_io->scsiio.be_move_done(sc->sc_ctl_io);
481313959Strasz			sc->sc_ctl_io = NULL;
482313959Strasz		}
483313959Strasz
484313959Strasz		cfumass_transfer_stop_and_drain(sc, CFUMASS_T_STATUS);
485313959Strasz
486313959Strasz		refcount_acquire(&sc->sc_queued);
487313959Strasz		last = refcount_release(&sc->sc_queued);
488313959Strasz		if (last != 0)
489313959Strasz			break;
490313959Strasz
491313959Strasz		CFUMASS_DEBUG(sc, "%d CTL tasks pending", sc->sc_queued);
492313959Strasz		msleep(__DEVOLATILE(void *, &sc->sc_queued), &sc->sc_mtx,
493313959Strasz		    0, "cfumass_reset", hz / 100);
494313959Strasz	}
495313959Strasz}
496313959Strasz
497313959Straszstatic int
498313959Straszcfumass_handle_request(device_t dev,
499313959Strasz    const void *preq, void **pptr, uint16_t *plen,
500313959Strasz    uint16_t offset, uint8_t *pstate)
501313959Strasz{
502313959Strasz	static uint8_t max_lun_tmp;
503313959Strasz	struct cfumass_softc *sc;
504313959Strasz	const struct usb_device_request *req;
505313959Strasz	uint8_t is_complete;
506313959Strasz
507313959Strasz	sc = device_get_softc(dev);
508313959Strasz	req = preq;
509313959Strasz	is_complete = *pstate;
510313959Strasz
511313959Strasz	CFUMASS_DEBUG(sc, "go");
512313959Strasz
513313959Strasz	if (is_complete)
514313959Strasz		return (ENXIO);
515313959Strasz
516313959Strasz	if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
517313959Strasz	    (req->bRequest == UR_RESET)) {
518313959Strasz		CFUMASS_WARN(sc, "received Bulk-Only Mass Storage Reset");
519313959Strasz		*plen = 0;
520313959Strasz
521313959Strasz		CFUMASS_LOCK(sc);
522313959Strasz		cfumass_terminate(sc);
523313959Strasz		cfumass_transfer_start(sc, CFUMASS_T_COMMAND);
524313959Strasz		CFUMASS_UNLOCK(sc);
525313959Strasz
526313959Strasz		CFUMASS_DEBUG(sc, "Bulk-Only Mass Storage Reset done");
527313959Strasz		return (0);
528313959Strasz	}
529313959Strasz
530313959Strasz	if ((req->bmRequestType == UT_READ_CLASS_INTERFACE) &&
531313959Strasz	    (req->bRequest == UR_GET_MAX_LUN)) {
532313959Strasz		CFUMASS_DEBUG(sc, "received Get Max LUN");
533313959Strasz		if (offset == 0) {
534313959Strasz			*plen = 1;
535313959Strasz			/*
536313959Strasz			 * The protocol doesn't support LUN numbers higher
537313959Strasz			 * than 15.  Also, some initiators (namely Windows XP
538313959Strasz			 * SP3 Version 2002) can't properly query the number
539313959Strasz			 * of LUNs, resulting in inaccessible "fake" ones - thus
540313959Strasz			 * the default limit of one LUN.
541313959Strasz			 */
542313959Strasz			if (max_lun < 0 || max_lun > 15) {
543313959Strasz				CFUMASS_WARN(sc,
544313959Strasz				    "invalid hw.usb.cfumass.max_lun, must be "
545313959Strasz				    "between 0 and 15; defaulting to 0");
546313959Strasz				max_lun_tmp = 0;
547313959Strasz			} else {
548313959Strasz				max_lun_tmp = max_lun;
549313959Strasz			}
550313959Strasz			*pptr = &max_lun_tmp;
551313959Strasz		} else {
552313959Strasz			*plen = 0;
553313959Strasz		}
554313959Strasz		return (0);
555313959Strasz	}
556313959Strasz
557313959Strasz	return (ENXIO);
558313959Strasz}
559313959Strasz
560313959Straszstatic int
561313959Straszcfumass_quirk(struct cfumass_softc *sc, unsigned char *cdb, int cdb_len)
562313959Strasz{
563313959Strasz	struct scsi_start_stop_unit *sssu;
564313959Strasz
565313959Strasz	switch (cdb[0]) {
566313959Strasz	case START_STOP_UNIT:
567313959Strasz		/*
568313959Strasz		 * Some initiators - eg OSX, Darwin Kernel Version 15.6.0,
569313959Strasz		 * root:xnu-3248.60.11~2/RELEASE_X86_64 - attempt to stop
570313959Strasz		 * the unit on eject, but fail to start it when it's plugged
571313959Strasz		 * back.  Just ignore the command.
572313959Strasz		 */
573313959Strasz
574313959Strasz		if (cdb_len < sizeof(*sssu)) {
575313959Strasz			CFUMASS_DEBUG(sc, "received START STOP UNIT with "
576313959Strasz			    "bCDBLength %d, should be %zd",
577313959Strasz			    cdb_len, sizeof(*sssu));
578313959Strasz			break;
579313959Strasz		}
580313959Strasz
581313959Strasz		sssu = (struct scsi_start_stop_unit *)cdb;
582313959Strasz		if ((sssu->how & SSS_PC_MASK) != 0)
583313959Strasz			break;
584313959Strasz
585313959Strasz		if ((sssu->how & SSS_START) != 0)
586313959Strasz			break;
587313959Strasz
588313959Strasz		if ((sssu->how & SSS_LOEJ) != 0)
589313959Strasz			break;
590313959Strasz
591313959Strasz		if (ignore_stop == 0) {
592313959Strasz			break;
593313959Strasz		} else if (ignore_stop == 1) {
594313959Strasz			CFUMASS_WARN(sc, "ignoring START STOP UNIT request");
595313959Strasz		} else {
596313959Strasz			CFUMASS_DEBUG(sc, "ignoring START STOP UNIT request");
597313959Strasz		}
598313959Strasz
599313959Strasz		sc->sc_current_status = 0;
600313959Strasz		cfumass_transfer_start(sc, CFUMASS_T_STATUS);
601313959Strasz
602313959Strasz		return (1);
603313959Strasz	default:
604313959Strasz		break;
605313959Strasz	}
606313959Strasz
607313959Strasz	return (0);
608313959Strasz}
609313959Strasz
610313959Straszstatic void
611313959Straszcfumass_t_command_callback(struct usb_xfer *xfer, usb_error_t usb_error)
612313959Strasz{
613313959Strasz	struct cfumass_softc *sc;
614313959Strasz	uint32_t signature;
615313959Strasz	union ctl_io *io;
616313959Strasz	int error = 0;
617313959Strasz
618313959Strasz	sc = usbd_xfer_softc(xfer);
619313959Strasz
620313959Strasz	KASSERT(sc->sc_ctl_io == NULL,
621313959Strasz	    ("sc_ctl_io is %p, should be NULL", sc->sc_ctl_io));
622313959Strasz
623313959Strasz	switch (USB_GET_STATE(xfer)) {
624313959Strasz	case USB_ST_TRANSFERRED:
625313959Strasz		CFUMASS_DEBUG(sc, "USB_ST_TRANSFERRED");
626313959Strasz
627313959Strasz		signature = UGETDW(sc->sc_cbw->dCBWSignature);
628313959Strasz		if (signature != CBWSIGNATURE) {
629313959Strasz			CFUMASS_WARN(sc, "wrong dCBWSignature 0x%08x, "
630313959Strasz			    "should be 0x%08x", signature, CBWSIGNATURE);
631313959Strasz			break;
632313959Strasz		}
633313959Strasz
634313959Strasz		if (sc->sc_cbw->bCDBLength <= 0 ||
635313959Strasz		    sc->sc_cbw->bCDBLength > sizeof(sc->sc_cbw->CBWCB)) {
636313959Strasz			CFUMASS_WARN(sc, "invalid bCDBLength %d, should be <= %zd",
637313959Strasz			    sc->sc_cbw->bCDBLength, sizeof(sc->sc_cbw->CBWCB));
638313959Strasz			break;
639313959Strasz		}
640313959Strasz
641313959Strasz		sc->sc_current_stalled = false;
642313959Strasz		sc->sc_current_status = 0;
643313959Strasz		sc->sc_current_tag = UGETDW(sc->sc_cbw->dCBWTag);
644313959Strasz		sc->sc_current_transfer_length =
645313959Strasz		    UGETDW(sc->sc_cbw->dCBWDataTransferLength);
646313959Strasz		sc->sc_current_flags = sc->sc_cbw->bCBWFlags;
647313959Strasz
648313959Strasz		/*
649313959Strasz		 * Make sure to report proper residue if the datamove wasn't
650313959Strasz		 * required, or wasn't called due to SCSI error.
651313959Strasz		 */
652313959Strasz		sc->sc_current_residue = sc->sc_current_transfer_length;
653313959Strasz
654313959Strasz		if (cfumass_quirk(sc,
655313959Strasz		    sc->sc_cbw->CBWCB, sc->sc_cbw->bCDBLength) != 0)
656313959Strasz			break;
657313959Strasz
658313959Strasz		if (!cfumass_port_online) {
659313959Strasz			CFUMASS_DEBUG(sc, "cfumass port is offline; stalling");
660313959Strasz			usbd_xfer_set_stall(xfer);
661313959Strasz			break;
662313959Strasz		}
663313959Strasz
664313959Strasz		/*
665313959Strasz		 * Those CTL functions cannot be called with mutex held.
666313959Strasz		 */
667313959Strasz		CFUMASS_UNLOCK(sc);
668313959Strasz		io = ctl_alloc_io(cfumass_port.ctl_pool_ref);
669313959Strasz		ctl_zero_io(io);
670313959Strasz		io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = sc;
671313959Strasz		io->io_hdr.io_type = CTL_IO_SCSI;
672313959Strasz		io->io_hdr.nexus.initid = sc->sc_ctl_initid;
673313959Strasz		io->io_hdr.nexus.targ_port = cfumass_port.targ_port;
674313959Strasz		io->io_hdr.nexus.targ_lun = ctl_decode_lun(sc->sc_cbw->bCBWLUN);
675313959Strasz		io->scsiio.tag_num = UGETDW(sc->sc_cbw->dCBWTag);
676313959Strasz		io->scsiio.tag_type = CTL_TAG_UNTAGGED;
677313959Strasz		io->scsiio.cdb_len = sc->sc_cbw->bCDBLength;
678313959Strasz		memcpy(io->scsiio.cdb, sc->sc_cbw->CBWCB, sc->sc_cbw->bCDBLength);
679313959Strasz		refcount_acquire(&sc->sc_queued);
680313959Strasz		error = ctl_queue(io);
681313959Strasz		if (error != CTL_RETVAL_COMPLETE) {
682313959Strasz			CFUMASS_WARN(sc,
683313959Strasz			    "ctl_queue() failed; error %d; stalling", error);
684313959Strasz			ctl_free_io(io);
685313959Strasz			refcount_release(&sc->sc_queued);
686313959Strasz			CFUMASS_LOCK(sc);
687313959Strasz			usbd_xfer_set_stall(xfer);
688313959Strasz			break;
689313959Strasz		}
690313959Strasz
691313959Strasz		CFUMASS_LOCK(sc);
692313959Strasz		break;
693313959Strasz
694313959Strasz	case USB_ST_SETUP:
695313959Strasztr_setup:
696313959Strasz		CFUMASS_DEBUG(sc, "USB_ST_SETUP");
697313959Strasz
698313959Strasz		usbd_xfer_set_frame_len(xfer, 0, sizeof(*sc->sc_cbw));
699313959Strasz		usbd_transfer_submit(xfer);
700313959Strasz		break;
701313959Strasz
702313959Strasz	default:
703313959Strasz		if (usb_error == USB_ERR_CANCELLED) {
704313959Strasz			CFUMASS_DEBUG(sc, "USB_ERR_CANCELLED");
705313959Strasz			break;
706313959Strasz		}
707313959Strasz
708313959Strasz		CFUMASS_DEBUG(sc, "USB_ST_ERROR: %s", usbd_errstr(usb_error));
709313959Strasz
710313959Strasz		goto tr_setup;
711313959Strasz	}
712313959Strasz}
713313959Strasz
714313959Straszstatic void
715313959Straszcfumass_t_data_out_callback(struct usb_xfer *xfer, usb_error_t usb_error)
716313959Strasz{
717313959Strasz	struct cfumass_softc *sc;
718313959Strasz	union ctl_io *io;
719313959Strasz	struct ctl_sg_entry ctl_sg_entry, *ctl_sglist;
720313959Strasz	int actlen, ctl_sg_count;
721313959Strasz
722313959Strasz	sc = usbd_xfer_softc(xfer);
723313959Strasz	io = sc->sc_ctl_io;
724313959Strasz
725313959Strasz	if (io->scsiio.kern_sg_entries > 0) {
726313959Strasz		ctl_sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr;
727313959Strasz		ctl_sg_count = io->scsiio.kern_sg_entries;
728313959Strasz	} else {
729313959Strasz		ctl_sglist = &ctl_sg_entry;
730313959Strasz		ctl_sglist->addr = io->scsiio.kern_data_ptr;
731313959Strasz		ctl_sglist->len = io->scsiio.kern_data_len;
732313959Strasz		ctl_sg_count = 1;
733313959Strasz	}
734313959Strasz
735313959Strasz	switch (USB_GET_STATE(xfer)) {
736313959Strasz	case USB_ST_TRANSFERRED:
737313959Strasz		CFUMASS_DEBUG(sc, "USB_ST_TRANSFERRED");
738313959Strasz
739317336Smav		usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
740313959Strasz		if (actlen != ctl_sglist[0].len) {
741313959Strasz			KASSERT(actlen <= ctl_sglist[0].len,
742313959Strasz			    ("actlen %d > ctl_sglist.len %zd",
743313959Strasz			    actlen, ctl_sglist[0].len));
744313959Strasz			CFUMASS_DEBUG(sc, "host transferred %d bytes"
745313959Strasz			    "instead of expected %zd bytes",
746313959Strasz			    actlen, ctl_sglist[0].len);
747313959Strasz		}
748317336Smav		sc->sc_current_residue -= actlen;
749317336Smav		io->scsiio.kern_data_resid -= actlen;
750313959Strasz		io->scsiio.be_move_done(io);
751313959Strasz		sc->sc_ctl_io = NULL;
752313959Strasz		break;
753313959Strasz
754313959Strasz	case USB_ST_SETUP:
755313959Strasztr_setup:
756313959Strasz		CFUMASS_DEBUG(sc, "USB_ST_SETUP");
757313959Strasz
758313959Strasz		CFUMASS_DEBUG(sc, "requested size %d, CTL segment size %zd",
759313959Strasz		    sc->sc_current_transfer_length, ctl_sglist[0].len);
760313959Strasz
761313959Strasz		usbd_xfer_set_frame_data(xfer, 0, ctl_sglist[0].addr, ctl_sglist[0].len);
762313959Strasz		usbd_transfer_submit(xfer);
763313959Strasz		break;
764313959Strasz
765313959Strasz	default:
766313959Strasz		if (usb_error == USB_ERR_CANCELLED) {
767313959Strasz			CFUMASS_DEBUG(sc, "USB_ERR_CANCELLED");
768313959Strasz			break;
769313959Strasz		}
770313959Strasz
771313959Strasz		CFUMASS_DEBUG(sc, "USB_ST_ERROR: %s",
772313959Strasz		    usbd_errstr(usb_error));
773313959Strasz
774313959Strasz		goto tr_setup;
775313959Strasz	}
776313959Strasz}
777313959Strasz
778313959Straszstatic void
779313959Straszcfumass_t_data_in_callback(struct usb_xfer *xfer, usb_error_t usb_error)
780313959Strasz{
781313959Strasz	struct cfumass_softc *sc;
782313959Strasz	union ctl_io *io;
783313959Strasz	uint32_t max_bulk;
784313959Strasz	struct ctl_sg_entry ctl_sg_entry, *ctl_sglist;
785317336Smav	int actlen, ctl_sg_count;
786313959Strasz
787313959Strasz	sc = usbd_xfer_softc(xfer);
788313959Strasz	io = sc->sc_ctl_io;
789313959Strasz
790313959Strasz	switch (USB_GET_STATE(xfer)) {
791313959Strasz	case USB_ST_TRANSFERRED:
792313959Strasz		CFUMASS_DEBUG(sc, "USB_ST_TRANSFERRED");
793313959Strasz
794317336Smav		usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
795317336Smav		sc->sc_current_residue -= actlen;
796317336Smav		io->scsiio.kern_data_resid -= actlen;
797313959Strasz		io->scsiio.be_move_done(io);
798313959Strasz		sc->sc_ctl_io = NULL;
799313959Strasz		break;
800313959Strasz
801313959Strasz	case USB_ST_SETUP:
802313959Strasztr_setup:
803313959Strasz		CFUMASS_DEBUG(sc, "USB_ST_SETUP");
804313959Strasz
805313959Strasz		if (io->scsiio.kern_sg_entries > 0) {
806313959Strasz			ctl_sglist = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr;
807313959Strasz			ctl_sg_count = io->scsiio.kern_sg_entries;
808313959Strasz		} else {
809313959Strasz			ctl_sglist = &ctl_sg_entry;
810313959Strasz			ctl_sglist->addr = io->scsiio.kern_data_ptr;
811313959Strasz			ctl_sglist->len = io->scsiio.kern_data_len;
812313959Strasz			ctl_sg_count = 1;
813313959Strasz		}
814313959Strasz
815313959Strasz		if (sc->sc_current_transfer_length > io->scsiio.kern_total_len) {
816313959Strasz			CFUMASS_DEBUG(sc, "initiator requested %d bytes, "
817313959Strasz			    "we will send %ju and stall",
818313959Strasz			    sc->sc_current_transfer_length,
819313959Strasz			    (uintmax_t)io->scsiio.kern_total_len);
820313959Strasz		}
821313959Strasz
822317336Smav		max_bulk = usbd_xfer_max_len(xfer);
823313959Strasz		CFUMASS_DEBUG(sc, "max_bulk %d, requested size %d, "
824313959Strasz		    "CTL segment size %zd", max_bulk,
825313959Strasz		    sc->sc_current_transfer_length, ctl_sglist[0].len);
826313959Strasz
827313959Strasz		if (max_bulk >= ctl_sglist[0].len)
828313959Strasz			max_bulk = ctl_sglist[0].len;
829313959Strasz
830313959Strasz		usbd_xfer_set_frame_data(xfer, 0, ctl_sglist[0].addr, max_bulk);
831313959Strasz		usbd_transfer_submit(xfer);
832313959Strasz
833313959Strasz		break;
834313959Strasz
835313959Strasz	default:
836313959Strasz		if (usb_error == USB_ERR_CANCELLED) {
837313959Strasz			CFUMASS_DEBUG(sc, "USB_ERR_CANCELLED");
838313959Strasz			break;
839313959Strasz		}
840313959Strasz
841313959Strasz		CFUMASS_DEBUG(sc, "USB_ST_ERROR: %s", usbd_errstr(usb_error));
842313959Strasz
843313959Strasz		goto tr_setup;
844313959Strasz	}
845313959Strasz}
846313959Strasz
847313959Straszstatic void
848313959Straszcfumass_t_status_callback(struct usb_xfer *xfer, usb_error_t usb_error)
849313959Strasz{
850313959Strasz	struct cfumass_softc *sc;
851313959Strasz
852313959Strasz	sc = usbd_xfer_softc(xfer);
853313959Strasz
854313959Strasz	KASSERT(sc->sc_ctl_io == NULL,
855313959Strasz	    ("sc_ctl_io is %p, should be NULL", sc->sc_ctl_io));
856313959Strasz
857313959Strasz	switch (USB_GET_STATE(xfer)) {
858313959Strasz	case USB_ST_TRANSFERRED:
859313959Strasz		CFUMASS_DEBUG(sc, "USB_ST_TRANSFERRED");
860313959Strasz
861313959Strasz		cfumass_transfer_start(sc, CFUMASS_T_COMMAND);
862313959Strasz		break;
863313959Strasz
864313959Strasz	case USB_ST_SETUP:
865313959Strasztr_setup:
866313959Strasz		CFUMASS_DEBUG(sc, "USB_ST_SETUP");
867313959Strasz
868313959Strasz		if (sc->sc_current_residue > 0 && !sc->sc_current_stalled) {
869313959Strasz			CFUMASS_DEBUG(sc, "non-zero residue, stalling");
870313959Strasz			usbd_xfer_set_stall(xfer);
871313959Strasz			sc->sc_current_stalled = true;
872313959Strasz		}
873313959Strasz
874313959Strasz		USETDW(sc->sc_csw->dCSWSignature, CSWSIGNATURE);
875313959Strasz		USETDW(sc->sc_csw->dCSWTag, sc->sc_current_tag);
876313959Strasz		USETDW(sc->sc_csw->dCSWDataResidue, sc->sc_current_residue);
877313959Strasz		sc->sc_csw->bCSWStatus = sc->sc_current_status;
878313959Strasz
879313959Strasz		usbd_xfer_set_frame_len(xfer, 0, sizeof(*sc->sc_csw));
880313959Strasz		usbd_transfer_submit(xfer);
881313959Strasz		break;
882313959Strasz
883313959Strasz	default:
884313959Strasz		if (usb_error == USB_ERR_CANCELLED) {
885313959Strasz			CFUMASS_DEBUG(sc, "USB_ERR_CANCELLED");
886313959Strasz			break;
887313959Strasz		}
888313959Strasz
889313959Strasz		CFUMASS_DEBUG(sc, "USB_ST_ERROR: %s",
890313959Strasz		    usbd_errstr(usb_error));
891313959Strasz
892313959Strasz		goto tr_setup;
893313959Strasz	}
894313959Strasz}
895313959Strasz
896313959Straszstatic void
897313959Straszcfumass_online(void *arg __unused)
898313959Strasz{
899313959Strasz
900313959Strasz	cfumass_port_online = true;
901313959Strasz}
902313959Strasz
903313959Straszstatic void
904313959Straszcfumass_offline(void *arg __unused)
905313959Strasz{
906313959Strasz
907313959Strasz	cfumass_port_online = false;
908313959Strasz}
909313959Strasz
910313959Straszstatic void
911313959Straszcfumass_datamove(union ctl_io *io)
912313959Strasz{
913313959Strasz	struct cfumass_softc *sc;
914313959Strasz
915313959Strasz	sc = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
916313959Strasz
917313959Strasz	CFUMASS_DEBUG(sc, "go");
918313959Strasz
919313959Strasz	CFUMASS_LOCK(sc);
920313959Strasz
921313959Strasz	KASSERT(sc->sc_ctl_io == NULL,
922313959Strasz	    ("sc_ctl_io is %p, should be NULL", sc->sc_ctl_io));
923313959Strasz	sc->sc_ctl_io = io;
924313959Strasz
925313959Strasz	if ((io->io_hdr.flags & CTL_FLAG_DATA_MASK) == CTL_FLAG_DATA_IN) {
926313959Strasz		/*
927313959Strasz		 * Verify that CTL wants us to send the data in the direction
928313959Strasz		 * expected by the initiator.
929313959Strasz		 */
930313959Strasz		if (sc->sc_current_flags != CBWFLAGS_IN) {
931313959Strasz			CFUMASS_WARN(sc, "wrong bCBWFlags 0x%x, should be 0x%x",
932313959Strasz			    sc->sc_current_flags, CBWFLAGS_IN);
933313959Strasz			goto fail;
934313959Strasz		}
935313959Strasz
936313959Strasz		cfumass_transfer_start(sc, CFUMASS_T_DATA_IN);
937313959Strasz	} else {
938313959Strasz		if (sc->sc_current_flags != CBWFLAGS_OUT) {
939313959Strasz			CFUMASS_WARN(sc, "wrong bCBWFlags 0x%x, should be 0x%x",
940313959Strasz			    sc->sc_current_flags, CBWFLAGS_OUT);
941313959Strasz			goto fail;
942313959Strasz		}
943313959Strasz
944313959Strasz		cfumass_transfer_start(sc, CFUMASS_T_DATA_OUT);
945313959Strasz	}
946313959Strasz
947313959Strasz	CFUMASS_UNLOCK(sc);
948313959Strasz	return;
949313959Strasz
950313959Straszfail:
951313959Strasz	ctl_set_data_phase_error(&io->scsiio);
952313959Strasz	io->scsiio.be_move_done(io);
953313959Strasz	sc->sc_ctl_io = NULL;
954313959Strasz}
955313959Strasz
956313959Straszstatic void
957313959Straszcfumass_done(union ctl_io *io)
958313959Strasz{
959313959Strasz	struct cfumass_softc *sc;
960313959Strasz
961313959Strasz	sc = io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
962313959Strasz
963313959Strasz	CFUMASS_DEBUG(sc, "go");
964313959Strasz
965313959Strasz	KASSERT(((io->io_hdr.status & CTL_STATUS_MASK) != CTL_STATUS_NONE),
966313959Strasz	    ("invalid CTL status %#x", io->io_hdr.status));
967313959Strasz	KASSERT(sc->sc_ctl_io == NULL,
968313959Strasz	    ("sc_ctl_io is %p, should be NULL", sc->sc_ctl_io));
969313959Strasz
970313959Strasz	if (io->io_hdr.io_type == CTL_IO_TASK &&
971313959Strasz	    io->taskio.task_action == CTL_TASK_I_T_NEXUS_RESET) {
972313959Strasz		/*
973313959Strasz		 * Implicit task termination has just completed; nothing to do.
974313959Strasz		 */
975313959Strasz		ctl_free_io(io);
976313959Strasz		return;
977313959Strasz	}
978313959Strasz
979313959Strasz	/*
980313959Strasz	 * Do not return status for aborted commands.
981313959Strasz	 * There are exceptions, but none supported by CTL yet.
982313959Strasz	 */
983313959Strasz	if (((io->io_hdr.flags & CTL_FLAG_ABORT) &&
984313959Strasz	     (io->io_hdr.flags & CTL_FLAG_ABORT_STATUS) == 0) ||
985313959Strasz	    (io->io_hdr.flags & CTL_FLAG_STATUS_SENT)) {
986313959Strasz		ctl_free_io(io);
987313959Strasz		return;
988313959Strasz	}
989313959Strasz
990317337Smav	if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS)
991313959Strasz		sc->sc_current_status = 0;
992317337Smav	else
993313959Strasz		sc->sc_current_status = 1;
994313959Strasz
995317337Smav	/* XXX: How should we report BUSY, RESERVATION CONFLICT, etc? */
996317337Smav	if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SCSI_ERROR &&
997317337Smav	    io->scsiio.scsi_status == SCSI_STATUS_CHECK_COND)
998317337Smav		ctl_queue_sense(io);
999317337Smav	else
1000317337Smav		ctl_free_io(io);
1001317337Smav
1002313959Strasz	CFUMASS_LOCK(sc);
1003313959Strasz	cfumass_transfer_start(sc, CFUMASS_T_STATUS);
1004313959Strasz	CFUMASS_UNLOCK(sc);
1005313959Strasz
1006313959Strasz	refcount_release(&sc->sc_queued);
1007313959Strasz}
1008313959Strasz
1009313959Straszint
1010313959Straszcfumass_init(void)
1011313959Strasz{
1012313959Strasz	int error;
1013313959Strasz
1014313959Strasz	cfumass_port.frontend = &cfumass_frontend;
1015313959Strasz	cfumass_port.port_type = CTL_PORT_UMASS;
1016317336Smav	cfumass_port.num_requested_ctl_io = 1;
1017313959Strasz	cfumass_port.port_name = "cfumass";
1018313959Strasz	cfumass_port.physical_port = 0;
1019313959Strasz	cfumass_port.virtual_port = 0;
1020313959Strasz	cfumass_port.port_online = cfumass_online;
1021313959Strasz	cfumass_port.port_offline = cfumass_offline;
1022313959Strasz	cfumass_port.onoff_arg = NULL;
1023313959Strasz	cfumass_port.fe_datamove = cfumass_datamove;
1024313959Strasz	cfumass_port.fe_done = cfumass_done;
1025313959Strasz	cfumass_port.targ_port = -1;
1026313959Strasz
1027313959Strasz	error = ctl_port_register(&cfumass_port);
1028313959Strasz	if (error != 0) {
1029313959Strasz		printf("%s: ctl_port_register() failed "
1030313959Strasz		    "with error %d", __func__, error);
1031313959Strasz	}
1032313959Strasz
1033313959Strasz	cfumass_port_online = true;
1034313959Strasz	refcount_init(&cfumass_refcount, 0);
1035313959Strasz
1036313959Strasz	return (error);
1037313959Strasz}
1038313959Strasz
1039313959Straszint
1040313959Straszcfumass_shutdown(void)
1041313959Strasz{
1042313959Strasz	int error;
1043313959Strasz
1044313959Strasz	if (cfumass_refcount > 0) {
1045313959Strasz		if (debug > 1) {
1046313959Strasz			printf("%s: still have %u attachments; "
1047313959Strasz			    "returning EBUSY\n", __func__, cfumass_refcount);
1048313959Strasz		}
1049313959Strasz		return (EBUSY);
1050313959Strasz	}
1051313959Strasz
1052313959Strasz	error = ctl_port_deregister(&cfumass_port);
1053313959Strasz	if (error != 0) {
1054313959Strasz		printf("%s: ctl_port_deregister() failed "
1055313959Strasz		    "with error %d\n", __func__, error);
1056313959Strasz	}
1057313959Strasz
1058313959Strasz	return (error);
1059313959Strasz}
1060