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