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