1898b0535SWarner Losh /*- 23393f8daSKenneth D. Merry * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs. 32a888f93SKenneth D. Merry * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry. 476babe50SJustin T. Gibbs * All rights reserved. 576babe50SJustin T. Gibbs * 676babe50SJustin T. Gibbs * Redistribution and use in source and binary forms, with or without 776babe50SJustin T. Gibbs * modification, are permitted provided that the following conditions 876babe50SJustin T. Gibbs * are met: 976babe50SJustin T. Gibbs * 1. Redistributions of source code must retain the above copyright 1076babe50SJustin T. Gibbs * notice, this list of conditions, and the following disclaimer, 1176babe50SJustin T. Gibbs * without modification, immediately at the beginning of the file. 1276babe50SJustin T. Gibbs * 2. The name of the author may not be used to endorse or promote products 1376babe50SJustin T. Gibbs * derived from this software without specific prior written permission. 1476babe50SJustin T. Gibbs * 1576babe50SJustin T. Gibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1676babe50SJustin T. Gibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1776babe50SJustin T. Gibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1876babe50SJustin T. Gibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 1976babe50SJustin T. Gibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2076babe50SJustin T. Gibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2176babe50SJustin T. Gibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2276babe50SJustin T. Gibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2376babe50SJustin T. Gibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2476babe50SJustin T. Gibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2576babe50SJustin T. Gibbs * SUCH DAMAGE. 2676babe50SJustin T. Gibbs */ 2776babe50SJustin T. Gibbs 28ee709e70SDavid E. O'Brien #include <sys/cdefs.h> 29ee709e70SDavid E. O'Brien __FBSDID("$FreeBSD$"); 30ee709e70SDavid E. O'Brien 3176babe50SJustin T. Gibbs #include <sys/param.h> 3276babe50SJustin T. Gibbs #include <sys/systm.h> 3376babe50SJustin T. Gibbs #include <sys/kernel.h> 3476babe50SJustin T. Gibbs #include <sys/types.h> 359626b608SPoul-Henning Kamp #include <sys/bio.h> 3676babe50SJustin T. Gibbs #include <sys/malloc.h> 3776babe50SJustin T. Gibbs #include <sys/fcntl.h> 3876babe50SJustin T. Gibbs #include <sys/conf.h> 3976babe50SJustin T. Gibbs #include <sys/errno.h> 4076babe50SJustin T. Gibbs #include <sys/devicestat.h> 41f7312ca2SRobert Watson #include <sys/proc.h> 4276babe50SJustin T. Gibbs 4376babe50SJustin T. Gibbs #include <cam/cam.h> 4476babe50SJustin T. Gibbs #include <cam/cam_ccb.h> 4576babe50SJustin T. Gibbs #include <cam/cam_periph.h> 463393f8daSKenneth D. Merry #include <cam/cam_queue.h> 4776babe50SJustin T. Gibbs #include <cam/cam_xpt_periph.h> 4876babe50SJustin T. Gibbs #include <cam/cam_debug.h> 492b83592fSScott Long #include <cam/cam_sim.h> 5076babe50SJustin T. Gibbs 5176babe50SJustin T. Gibbs #include <cam/scsi/scsi_all.h> 5276babe50SJustin T. Gibbs #include <cam/scsi/scsi_pass.h> 5376babe50SJustin T. Gibbs 5476babe50SJustin T. Gibbs typedef enum { 5576babe50SJustin T. Gibbs PASS_FLAG_OPEN = 0x01, 5676babe50SJustin T. Gibbs PASS_FLAG_LOCKED = 0x02, 5776babe50SJustin T. Gibbs PASS_FLAG_INVALID = 0x04 5876babe50SJustin T. Gibbs } pass_flags; 5976babe50SJustin T. Gibbs 6076babe50SJustin T. Gibbs typedef enum { 6176babe50SJustin T. Gibbs PASS_STATE_NORMAL 6276babe50SJustin T. Gibbs } pass_state; 6376babe50SJustin T. Gibbs 6476babe50SJustin T. Gibbs typedef enum { 6576babe50SJustin T. Gibbs PASS_CCB_BUFFER_IO, 6676babe50SJustin T. Gibbs PASS_CCB_WAITING 6776babe50SJustin T. Gibbs } pass_ccb_types; 6876babe50SJustin T. Gibbs 6976babe50SJustin T. Gibbs #define ccb_type ppriv_field0 7076babe50SJustin T. Gibbs #define ccb_bp ppriv_ptr1 7176babe50SJustin T. Gibbs 7276babe50SJustin T. Gibbs struct pass_softc { 7376babe50SJustin T. Gibbs pass_state state; 7476babe50SJustin T. Gibbs pass_flags flags; 7576babe50SJustin T. Gibbs u_int8_t pd_type; 7676babe50SJustin T. Gibbs union ccb saved_ccb; 77a9d2245eSPoul-Henning Kamp struct devstat *device_stats; 7889c9c53dSPoul-Henning Kamp struct cdev *dev; 7976babe50SJustin T. Gibbs }; 8076babe50SJustin T. Gibbs 8176babe50SJustin T. Gibbs 8276babe50SJustin T. Gibbs static d_open_t passopen; 8376babe50SJustin T. Gibbs static d_close_t passclose; 8476babe50SJustin T. Gibbs static d_ioctl_t passioctl; 8576babe50SJustin T. Gibbs 8676babe50SJustin T. Gibbs static periph_init_t passinit; 8776babe50SJustin T. Gibbs static periph_ctor_t passregister; 88ee9c90c7SKenneth D. Merry static periph_oninv_t passoninvalidate; 8976babe50SJustin T. Gibbs static periph_dtor_t passcleanup; 9076babe50SJustin T. Gibbs static periph_start_t passstart; 9176babe50SJustin T. Gibbs static void passasync(void *callback_arg, u_int32_t code, 9276babe50SJustin T. Gibbs struct cam_path *path, void *arg); 9376babe50SJustin T. Gibbs static void passdone(struct cam_periph *periph, 9476babe50SJustin T. Gibbs union ccb *done_ccb); 9576babe50SJustin T. Gibbs static int passerror(union ccb *ccb, u_int32_t cam_flags, 9676babe50SJustin T. Gibbs u_int32_t sense_flags); 9776babe50SJustin T. Gibbs static int passsendccb(struct cam_periph *periph, union ccb *ccb, 9876babe50SJustin T. Gibbs union ccb *inccb); 9976babe50SJustin T. Gibbs 10076babe50SJustin T. Gibbs static struct periph_driver passdriver = 10176babe50SJustin T. Gibbs { 10276babe50SJustin T. Gibbs passinit, "pass", 10376babe50SJustin T. Gibbs TAILQ_HEAD_INITIALIZER(passdriver.units), /* generation */ 0 10476babe50SJustin T. Gibbs }; 10576babe50SJustin T. Gibbs 1060b7c27b9SPeter Wemm PERIPHDRIVER_DECLARE(pass, passdriver); 10776babe50SJustin T. Gibbs 1084e2f199eSPoul-Henning Kamp static struct cdevsw pass_cdevsw = { 109dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 1102b83592fSScott Long .d_flags = 0, 1117ac40f5fSPoul-Henning Kamp .d_open = passopen, 1127ac40f5fSPoul-Henning Kamp .d_close = passclose, 1137ac40f5fSPoul-Henning Kamp .d_ioctl = passioctl, 1147ac40f5fSPoul-Henning Kamp .d_name = "pass", 11576babe50SJustin T. Gibbs }; 11676babe50SJustin T. Gibbs 11776babe50SJustin T. Gibbs static void 11876babe50SJustin T. Gibbs passinit(void) 11976babe50SJustin T. Gibbs { 12076babe50SJustin T. Gibbs cam_status status; 12176babe50SJustin T. Gibbs 12276babe50SJustin T. Gibbs /* 12376babe50SJustin T. Gibbs * Install a global async callback. This callback will 12476babe50SJustin T. Gibbs * receive async callbacks like "new device found". 12576babe50SJustin T. Gibbs */ 12685d92640SScott Long status = xpt_register_async(AC_FOUND_DEVICE, passasync, NULL, NULL); 12776babe50SJustin T. Gibbs 12876babe50SJustin T. Gibbs if (status != CAM_REQ_CMP) { 12976babe50SJustin T. Gibbs printf("pass: Failed to attach master async callback " 13076babe50SJustin T. Gibbs "due to status 0x%x!\n", status); 13176babe50SJustin T. Gibbs } 13276babe50SJustin T. Gibbs 13376babe50SJustin T. Gibbs } 13476babe50SJustin T. Gibbs 13576babe50SJustin T. Gibbs static void 136ee9c90c7SKenneth D. Merry passoninvalidate(struct cam_periph *periph) 137ee9c90c7SKenneth D. Merry { 138ee9c90c7SKenneth D. Merry struct pass_softc *softc; 139ee9c90c7SKenneth D. Merry 140ee9c90c7SKenneth D. Merry softc = (struct pass_softc *)periph->softc; 141ee9c90c7SKenneth D. Merry 142ee9c90c7SKenneth D. Merry /* 143ee9c90c7SKenneth D. Merry * De-register any async callbacks. 144ee9c90c7SKenneth D. Merry */ 14585d92640SScott Long xpt_register_async(0, passasync, periph, periph->path); 146ee9c90c7SKenneth D. Merry 147ee9c90c7SKenneth D. Merry softc->flags |= PASS_FLAG_INVALID; 148ee9c90c7SKenneth D. Merry 149ee9c90c7SKenneth D. Merry /* 1503393f8daSKenneth D. Merry * XXX Return all queued I/O with ENXIO. 151ee9c90c7SKenneth D. Merry * XXX Handle any transactions queued to the card 152ee9c90c7SKenneth D. Merry * with XPT_ABORT_CCB. 153ee9c90c7SKenneth D. Merry */ 154ee9c90c7SKenneth D. Merry 155ee9c90c7SKenneth D. Merry if (bootverbose) { 156f0d9af51SMatt Jacob xpt_print(periph->path, "lost device\n"); 157ee9c90c7SKenneth D. Merry } 158ee9c90c7SKenneth D. Merry 159ee9c90c7SKenneth D. Merry } 160ee9c90c7SKenneth D. Merry 161ee9c90c7SKenneth D. Merry static void 16276babe50SJustin T. Gibbs passcleanup(struct cam_periph *periph) 16376babe50SJustin T. Gibbs { 164ee9c90c7SKenneth D. Merry struct pass_softc *softc; 165ee9c90c7SKenneth D. Merry 166ee9c90c7SKenneth D. Merry softc = (struct pass_softc *)periph->softc; 167ee9c90c7SKenneth D. Merry 1685f3fed85SEdward Tomasz Napierala if (bootverbose) 169f0d9af51SMatt Jacob xpt_print(periph->path, "removing device entry\n"); 1705f3fed85SEdward Tomasz Napierala devstat_remove_entry(softc->device_stats); 1715f3fed85SEdward Tomasz Napierala cam_periph_unlock(periph); 17281059816SMatthew D Fleming /* 17381059816SMatthew D Fleming * passcleanup() is indirectly a d_close method via passclose, 17481059816SMatthew D Fleming * so using destroy_dev(9) directly can result in deadlock. 17581059816SMatthew D Fleming */ 17681059816SMatthew D Fleming destroy_dev_sched(softc->dev); 1775f3fed85SEdward Tomasz Napierala cam_periph_lock(periph); 178ee9c90c7SKenneth D. Merry free(softc, M_DEVBUF); 17976babe50SJustin T. Gibbs } 18076babe50SJustin T. Gibbs 18176babe50SJustin T. Gibbs static void 18276babe50SJustin T. Gibbs passasync(void *callback_arg, u_int32_t code, 18376babe50SJustin T. Gibbs struct cam_path *path, void *arg) 18476babe50SJustin T. Gibbs { 18576babe50SJustin T. Gibbs struct cam_periph *periph; 18676babe50SJustin T. Gibbs 18776babe50SJustin T. Gibbs periph = (struct cam_periph *)callback_arg; 18876babe50SJustin T. Gibbs 18976babe50SJustin T. Gibbs switch (code) { 19076babe50SJustin T. Gibbs case AC_FOUND_DEVICE: 19176babe50SJustin T. Gibbs { 19276babe50SJustin T. Gibbs struct ccb_getdev *cgd; 19376babe50SJustin T. Gibbs cam_status status; 19476babe50SJustin T. Gibbs 19576babe50SJustin T. Gibbs cgd = (struct ccb_getdev *)arg; 196c5ff3b2fSMatt Jacob if (cgd == NULL) 197c5ff3b2fSMatt Jacob break; 19876babe50SJustin T. Gibbs 19976babe50SJustin T. Gibbs /* 20076babe50SJustin T. Gibbs * Allocate a peripheral instance for 20176babe50SJustin T. Gibbs * this device and start the probe 20276babe50SJustin T. Gibbs * process. 20376babe50SJustin T. Gibbs */ 204ee9c90c7SKenneth D. Merry status = cam_periph_alloc(passregister, passoninvalidate, 205ee9c90c7SKenneth D. Merry passcleanup, passstart, "pass", 206ee9c90c7SKenneth D. Merry CAM_PERIPH_BIO, cgd->ccb_h.path, 207ee9c90c7SKenneth D. Merry passasync, AC_FOUND_DEVICE, cgd); 20876babe50SJustin T. Gibbs 20976babe50SJustin T. Gibbs if (status != CAM_REQ_CMP 2103393f8daSKenneth D. Merry && status != CAM_REQ_INPROG) { 2113393f8daSKenneth D. Merry const struct cam_status_entry *entry; 2123393f8daSKenneth D. Merry 2133393f8daSKenneth D. Merry entry = cam_fetch_status_entry(status); 2143393f8daSKenneth D. Merry 21576babe50SJustin T. Gibbs printf("passasync: Unable to attach new device " 2163393f8daSKenneth D. Merry "due to status %#x: %s\n", status, entry ? 2173393f8daSKenneth D. Merry entry->status_text : "Unknown"); 2183393f8daSKenneth D. Merry } 21976babe50SJustin T. Gibbs 22076babe50SJustin T. Gibbs break; 22176babe50SJustin T. Gibbs } 22276babe50SJustin T. Gibbs default: 223516871c6SJustin T. Gibbs cam_periph_async(periph, code, path, arg); 22476babe50SJustin T. Gibbs break; 22576babe50SJustin T. Gibbs } 22676babe50SJustin T. Gibbs } 22776babe50SJustin T. Gibbs 22876babe50SJustin T. Gibbs static cam_status 22976babe50SJustin T. Gibbs passregister(struct cam_periph *periph, void *arg) 23076babe50SJustin T. Gibbs { 23176babe50SJustin T. Gibbs struct pass_softc *softc; 23276babe50SJustin T. Gibbs struct ccb_getdev *cgd; 233*b8b6b5d3SAlexander Motin struct ccb_pathinq cpi; 2343393f8daSKenneth D. Merry int no_tags; 23576babe50SJustin T. Gibbs 23676babe50SJustin T. Gibbs cgd = (struct ccb_getdev *)arg; 23776babe50SJustin T. Gibbs if (periph == NULL) { 23876babe50SJustin T. Gibbs printf("passregister: periph was NULL!!\n"); 23976babe50SJustin T. Gibbs return(CAM_REQ_CMP_ERR); 24076babe50SJustin T. Gibbs } 24176babe50SJustin T. Gibbs 24276babe50SJustin T. Gibbs if (cgd == NULL) { 24376babe50SJustin T. Gibbs printf("passregister: no getdev CCB, can't register device\n"); 24476babe50SJustin T. Gibbs return(CAM_REQ_CMP_ERR); 24576babe50SJustin T. Gibbs } 24676babe50SJustin T. Gibbs 24776babe50SJustin T. Gibbs softc = (struct pass_softc *)malloc(sizeof(*softc), 24876babe50SJustin T. Gibbs M_DEVBUF, M_NOWAIT); 24976babe50SJustin T. Gibbs 25076babe50SJustin T. Gibbs if (softc == NULL) { 25176babe50SJustin T. Gibbs printf("passregister: Unable to probe new device. " 25276babe50SJustin T. Gibbs "Unable to allocate softc\n"); 25376babe50SJustin T. Gibbs return(CAM_REQ_CMP_ERR); 25476babe50SJustin T. Gibbs } 25576babe50SJustin T. Gibbs 25676babe50SJustin T. Gibbs bzero(softc, sizeof(*softc)); 25776babe50SJustin T. Gibbs softc->state = PASS_STATE_NORMAL; 258*b8b6b5d3SAlexander Motin if (cgd->protocol == PROTO_SCSI || cgd->protocol == PROTO_ATAPI) 25910b6172aSMatt Jacob softc->pd_type = SID_TYPE(&cgd->inq_data); 260*b8b6b5d3SAlexander Motin else if (cgd->protocol == PROTO_SATAPM) 261*b8b6b5d3SAlexander Motin softc->pd_type = T_ENCLOSURE; 262*b8b6b5d3SAlexander Motin else 263*b8b6b5d3SAlexander Motin softc->pd_type = T_DIRECT; 26476babe50SJustin T. Gibbs 26576babe50SJustin T. Gibbs periph->softc = softc; 2663393f8daSKenneth D. Merry 267*b8b6b5d3SAlexander Motin bzero(&cpi, sizeof(cpi)); 268*b8b6b5d3SAlexander Motin xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NORMAL); 269*b8b6b5d3SAlexander Motin cpi.ccb_h.func_code = XPT_PATH_INQ; 270*b8b6b5d3SAlexander Motin xpt_action((union ccb *)&cpi); 271*b8b6b5d3SAlexander Motin 27276babe50SJustin T. Gibbs /* 27376babe50SJustin T. Gibbs * We pass in 0 for a blocksize, since we don't 27476babe50SJustin T. Gibbs * know what the blocksize of this device is, if 27576babe50SJustin T. Gibbs * it even has a blocksize. 27676babe50SJustin T. Gibbs */ 27785d92640SScott Long mtx_unlock(periph->sim->mtx); 2783393f8daSKenneth D. Merry no_tags = (cgd->inq_data.flags & SID_CmdQue) == 0; 279c81d2c74SMatt Jacob softc->device_stats = devstat_new_entry("pass", 280d3ce8327SEd Schouten periph->unit_number, 0, 2813393f8daSKenneth D. Merry DEVSTAT_NO_BLOCKSIZE 2823393f8daSKenneth D. Merry | (no_tags ? DEVSTAT_NO_ORDERED_TAGS : 0), 28310b6172aSMatt Jacob softc->pd_type | 284*b8b6b5d3SAlexander Motin XPORT_DEVSTAT_TYPE(cpi.transport) | 2852a888f93SKenneth D. Merry DEVSTAT_TYPE_PASS, 2862a888f93SKenneth D. Merry DEVSTAT_PRIORITY_PASS); 28773d26919SKenneth D. Merry 28873d26919SKenneth D. Merry /* Register the device */ 289d3ce8327SEd Schouten softc->dev = make_dev(&pass_cdevsw, periph->unit_number, 290c81d2c74SMatt Jacob UID_ROOT, GID_OPERATOR, 0600, "%s%d", 291c81d2c74SMatt Jacob periph->periph_name, periph->unit_number); 2922b83592fSScott Long mtx_lock(periph->sim->mtx); 293e2a5fdf9SNate Lawson softc->dev->si_drv1 = periph; 29473d26919SKenneth D. Merry 29576babe50SJustin T. Gibbs /* 29676babe50SJustin T. Gibbs * Add an async callback so that we get 29776babe50SJustin T. Gibbs * notified if this device goes away. 29876babe50SJustin T. Gibbs */ 29985d92640SScott Long xpt_register_async(AC_LOST_DEVICE, passasync, periph, periph->path); 30076babe50SJustin T. Gibbs 30176babe50SJustin T. Gibbs if (bootverbose) 30276babe50SJustin T. Gibbs xpt_announce_periph(periph, NULL); 30376babe50SJustin T. Gibbs 30476babe50SJustin T. Gibbs return(CAM_REQ_CMP); 30576babe50SJustin T. Gibbs } 30676babe50SJustin T. Gibbs 30776babe50SJustin T. Gibbs static int 30889c9c53dSPoul-Henning Kamp passopen(struct cdev *dev, int flags, int fmt, struct thread *td) 30976babe50SJustin T. Gibbs { 31076babe50SJustin T. Gibbs struct cam_periph *periph; 31176babe50SJustin T. Gibbs struct pass_softc *softc; 312e2a5fdf9SNate Lawson int error; 31376babe50SJustin T. Gibbs 314e2a5fdf9SNate Lawson periph = (struct cam_periph *)dev->si_drv1; 3152b83592fSScott Long if (cam_periph_acquire(periph) != CAM_REQ_CMP) 31676babe50SJustin T. Gibbs return (ENXIO); 31776babe50SJustin T. Gibbs 3182b83592fSScott Long cam_periph_lock(periph); 3192b83592fSScott Long 32076babe50SJustin T. Gibbs softc = (struct pass_softc *)periph->softc; 32176babe50SJustin T. Gibbs 322ee9c90c7SKenneth D. Merry if (softc->flags & PASS_FLAG_INVALID) { 3232b83592fSScott Long cam_periph_unlock(periph); 3242b83592fSScott Long cam_periph_release(periph); 32576babe50SJustin T. Gibbs return(ENXIO); 326ee9c90c7SKenneth D. Merry } 32722b9c86cSKenneth D. Merry 32822b9c86cSKenneth D. Merry /* 329f5ef42beSRobert Watson * Don't allow access when we're running at a high securelevel. 33022b9c86cSKenneth D. Merry */ 331a854ed98SJohn Baldwin error = securelevel_gt(td->td_ucred, 1); 332f7312ca2SRobert Watson if (error) { 3332b83592fSScott Long cam_periph_unlock(periph); 3342b83592fSScott Long cam_periph_release(periph); 335f7312ca2SRobert Watson return(error); 33622b9c86cSKenneth D. Merry } 33776babe50SJustin T. Gibbs 33876babe50SJustin T. Gibbs /* 33966a0780eSKenneth D. Merry * Only allow read-write access. 34066a0780eSKenneth D. Merry */ 34122b9c86cSKenneth D. Merry if (((flags & FWRITE) == 0) || ((flags & FREAD) == 0)) { 3422b83592fSScott Long cam_periph_unlock(periph); 3432b83592fSScott Long cam_periph_release(periph); 34466a0780eSKenneth D. Merry return(EPERM); 34522b9c86cSKenneth D. Merry } 34666a0780eSKenneth D. Merry 34766a0780eSKenneth D. Merry /* 34876babe50SJustin T. Gibbs * We don't allow nonblocking access. 34976babe50SJustin T. Gibbs */ 35076babe50SJustin T. Gibbs if ((flags & O_NONBLOCK) != 0) { 351f0d9af51SMatt Jacob xpt_print(periph->path, "can't do nonblocking access\n"); 3522b83592fSScott Long cam_periph_unlock(periph); 3532b83592fSScott Long cam_periph_release(periph); 35422b9c86cSKenneth D. Merry return(EINVAL); 35576babe50SJustin T. Gibbs } 35676babe50SJustin T. Gibbs 35776babe50SJustin T. Gibbs if ((softc->flags & PASS_FLAG_OPEN) == 0) { 35876babe50SJustin T. Gibbs softc->flags |= PASS_FLAG_OPEN; 359835187bfSScott Long cam_periph_unlock(periph); 3602b83592fSScott Long } else { 3612b83592fSScott Long /* Device closes aren't symmertical, so fix up the refcount */ 362835187bfSScott Long cam_periph_unlock(periph); 3632b83592fSScott Long cam_periph_release(periph); 36476babe50SJustin T. Gibbs } 36576babe50SJustin T. Gibbs 36676babe50SJustin T. Gibbs return (error); 36776babe50SJustin T. Gibbs } 36876babe50SJustin T. Gibbs 36976babe50SJustin T. Gibbs static int 37089c9c53dSPoul-Henning Kamp passclose(struct cdev *dev, int flag, int fmt, struct thread *td) 37176babe50SJustin T. Gibbs { 37276babe50SJustin T. Gibbs struct cam_periph *periph; 37376babe50SJustin T. Gibbs struct pass_softc *softc; 37476babe50SJustin T. Gibbs 375e2a5fdf9SNate Lawson periph = (struct cam_periph *)dev->si_drv1; 37676babe50SJustin T. Gibbs if (periph == NULL) 37776babe50SJustin T. Gibbs return (ENXIO); 37876babe50SJustin T. Gibbs 3792b83592fSScott Long cam_periph_lock(periph); 3802b83592fSScott Long 38176babe50SJustin T. Gibbs softc = (struct pass_softc *)periph->softc; 38276babe50SJustin T. Gibbs softc->flags &= ~PASS_FLAG_OPEN; 38376babe50SJustin T. Gibbs 38476babe50SJustin T. Gibbs cam_periph_unlock(periph); 38576babe50SJustin T. Gibbs cam_periph_release(periph); 38676babe50SJustin T. Gibbs 38776babe50SJustin T. Gibbs return (0); 38876babe50SJustin T. Gibbs } 38976babe50SJustin T. Gibbs 39076babe50SJustin T. Gibbs static void 39176babe50SJustin T. Gibbs passstart(struct cam_periph *periph, union ccb *start_ccb) 39276babe50SJustin T. Gibbs { 39376babe50SJustin T. Gibbs struct pass_softc *softc; 39476babe50SJustin T. Gibbs 39576babe50SJustin T. Gibbs softc = (struct pass_softc *)periph->softc; 39676babe50SJustin T. Gibbs 39776babe50SJustin T. Gibbs switch (softc->state) { 39876babe50SJustin T. Gibbs case PASS_STATE_NORMAL: 39976babe50SJustin T. Gibbs start_ccb->ccb_h.ccb_type = PASS_CCB_WAITING; 40076babe50SJustin T. Gibbs SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h, 40176babe50SJustin T. Gibbs periph_links.sle); 40276babe50SJustin T. Gibbs periph->immediate_priority = CAM_PRIORITY_NONE; 40376babe50SJustin T. Gibbs wakeup(&periph->ccb_list); 40476babe50SJustin T. Gibbs break; 40576babe50SJustin T. Gibbs } 40676babe50SJustin T. Gibbs } 4073393f8daSKenneth D. Merry 40876babe50SJustin T. Gibbs static void 40976babe50SJustin T. Gibbs passdone(struct cam_periph *periph, union ccb *done_ccb) 41076babe50SJustin T. Gibbs { 41176babe50SJustin T. Gibbs struct pass_softc *softc; 41276babe50SJustin T. Gibbs struct ccb_scsiio *csio; 41376babe50SJustin T. Gibbs 41476babe50SJustin T. Gibbs softc = (struct pass_softc *)periph->softc; 41576babe50SJustin T. Gibbs csio = &done_ccb->csio; 41676babe50SJustin T. Gibbs switch (csio->ccb_h.ccb_type) { 41776babe50SJustin T. Gibbs case PASS_CCB_WAITING: 41876babe50SJustin T. Gibbs /* Caller will release the CCB */ 41976babe50SJustin T. Gibbs wakeup(&done_ccb->ccb_h.cbfcnp); 42076babe50SJustin T. Gibbs return; 42176babe50SJustin T. Gibbs } 42276babe50SJustin T. Gibbs xpt_release_ccb(done_ccb); 42376babe50SJustin T. Gibbs } 42476babe50SJustin T. Gibbs 42576babe50SJustin T. Gibbs static int 42689c9c53dSPoul-Henning Kamp passioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) 42776babe50SJustin T. Gibbs { 42876babe50SJustin T. Gibbs struct cam_periph *periph; 42976babe50SJustin T. Gibbs struct pass_softc *softc; 43076babe50SJustin T. Gibbs int error; 43176babe50SJustin T. Gibbs 432e2a5fdf9SNate Lawson periph = (struct cam_periph *)dev->si_drv1; 43376babe50SJustin T. Gibbs if (periph == NULL) 43476babe50SJustin T. Gibbs return(ENXIO); 43576babe50SJustin T. Gibbs 4362b83592fSScott Long cam_periph_lock(periph); 43776babe50SJustin T. Gibbs softc = (struct pass_softc *)periph->softc; 43876babe50SJustin T. Gibbs 43976babe50SJustin T. Gibbs error = 0; 44076babe50SJustin T. Gibbs 44176babe50SJustin T. Gibbs switch (cmd) { 44276babe50SJustin T. Gibbs 44376babe50SJustin T. Gibbs case CAMIOCOMMAND: 44476babe50SJustin T. Gibbs { 44576babe50SJustin T. Gibbs union ccb *inccb; 44676babe50SJustin T. Gibbs union ccb *ccb; 4479deea857SKenneth D. Merry int ccb_malloced; 44876babe50SJustin T. Gibbs 44976babe50SJustin T. Gibbs inccb = (union ccb *)addr; 4509deea857SKenneth D. Merry 4519deea857SKenneth D. Merry /* 4529deea857SKenneth D. Merry * Some CCB types, like scan bus and scan lun can only go 4539deea857SKenneth D. Merry * through the transport layer device. 4549deea857SKenneth D. Merry */ 4559deea857SKenneth D. Merry if (inccb->ccb_h.func_code & XPT_FC_XPT_ONLY) { 456f0d9af51SMatt Jacob xpt_print(periph->path, "CCB function code %#x is " 457f0d9af51SMatt Jacob "restricted to the XPT device\n", 458f0d9af51SMatt Jacob inccb->ccb_h.func_code); 4599deea857SKenneth D. Merry error = ENODEV; 4609deea857SKenneth D. Merry break; 4619deea857SKenneth D. Merry } 4629deea857SKenneth D. Merry 4639deea857SKenneth D. Merry /* 4649deea857SKenneth D. Merry * Non-immediate CCBs need a CCB from the per-device pool 4659deea857SKenneth D. Merry * of CCBs, which is scheduled by the transport layer. 4669deea857SKenneth D. Merry * Immediate CCBs and user-supplied CCBs should just be 4679deea857SKenneth D. Merry * malloced. 4689deea857SKenneth D. Merry */ 4699deea857SKenneth D. Merry if ((inccb->ccb_h.func_code & XPT_FC_QUEUED) 4709deea857SKenneth D. Merry && ((inccb->ccb_h.func_code & XPT_FC_USER_CCB) == 0)) { 4719deea857SKenneth D. Merry ccb = cam_periph_getccb(periph, 4729deea857SKenneth D. Merry inccb->ccb_h.pinfo.priority); 4739deea857SKenneth D. Merry ccb_malloced = 0; 4749deea857SKenneth D. Merry } else { 4758008a935SScott Long ccb = xpt_alloc_ccb_nowait(); 4769deea857SKenneth D. Merry 4779deea857SKenneth D. Merry if (ccb != NULL) 4789deea857SKenneth D. Merry xpt_setup_ccb(&ccb->ccb_h, periph->path, 4799deea857SKenneth D. Merry inccb->ccb_h.pinfo.priority); 4809deea857SKenneth D. Merry ccb_malloced = 1; 4819deea857SKenneth D. Merry } 4829deea857SKenneth D. Merry 4839deea857SKenneth D. Merry if (ccb == NULL) { 484f0d9af51SMatt Jacob xpt_print(periph->path, "unable to allocate CCB\n"); 4859deea857SKenneth D. Merry error = ENOMEM; 4869deea857SKenneth D. Merry break; 4879deea857SKenneth D. Merry } 48876babe50SJustin T. Gibbs 48976babe50SJustin T. Gibbs error = passsendccb(periph, ccb, inccb); 49076babe50SJustin T. Gibbs 4919deea857SKenneth D. Merry if (ccb_malloced) 4929deea857SKenneth D. Merry xpt_free_ccb(ccb); 4939deea857SKenneth D. Merry else 49476babe50SJustin T. Gibbs xpt_release_ccb(ccb); 49576babe50SJustin T. Gibbs 49676babe50SJustin T. Gibbs break; 49776babe50SJustin T. Gibbs } 49876babe50SJustin T. Gibbs default: 49976babe50SJustin T. Gibbs error = cam_periph_ioctl(periph, cmd, addr, passerror); 50076babe50SJustin T. Gibbs break; 50176babe50SJustin T. Gibbs } 50276babe50SJustin T. Gibbs 5032b83592fSScott Long cam_periph_unlock(periph); 50476babe50SJustin T. Gibbs return(error); 50576babe50SJustin T. Gibbs } 50676babe50SJustin T. Gibbs 50776babe50SJustin T. Gibbs /* 50876babe50SJustin T. Gibbs * Generally, "ccb" should be the CCB supplied by the kernel. "inccb" 50976babe50SJustin T. Gibbs * should be the CCB that is copied in from the user. 51076babe50SJustin T. Gibbs */ 51176babe50SJustin T. Gibbs static int 51276babe50SJustin T. Gibbs passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb) 51376babe50SJustin T. Gibbs { 51476babe50SJustin T. Gibbs struct pass_softc *softc; 51576babe50SJustin T. Gibbs struct cam_periph_map_info mapinfo; 51676babe50SJustin T. Gibbs int error, need_unmap; 51776babe50SJustin T. Gibbs 51876babe50SJustin T. Gibbs softc = (struct pass_softc *)periph->softc; 51976babe50SJustin T. Gibbs 52076babe50SJustin T. Gibbs need_unmap = 0; 52176babe50SJustin T. Gibbs 52276babe50SJustin T. Gibbs /* 52376babe50SJustin T. Gibbs * There are some fields in the CCB header that need to be 52476babe50SJustin T. Gibbs * preserved, the rest we get from the user. 52576babe50SJustin T. Gibbs */ 52676babe50SJustin T. Gibbs xpt_merge_ccb(ccb, inccb); 52776babe50SJustin T. Gibbs 52876babe50SJustin T. Gibbs /* 52976babe50SJustin T. Gibbs * There's no way for the user to have a completion 53076babe50SJustin T. Gibbs * function, so we put our own completion function in here. 53176babe50SJustin T. Gibbs */ 53276babe50SJustin T. Gibbs ccb->ccb_h.cbfcnp = passdone; 53376babe50SJustin T. Gibbs 53476babe50SJustin T. Gibbs /* 53576babe50SJustin T. Gibbs * We only attempt to map the user memory into kernel space 53676babe50SJustin T. Gibbs * if they haven't passed in a physical memory pointer, 53776babe50SJustin T. Gibbs * and if there is actually an I/O operation to perform. 53806e79492SKenneth D. Merry * cam_periph_mapmem() supports SCSI, ATA, SMP, ADVINFO and device 5397c103ddeSKenneth D. Merry * match CCBs. For the SCSI, ATA and ADVINFO CCBs, we only pass the 5407c103ddeSKenneth D. Merry * CCB in if there's actually data to map. cam_periph_mapmem() will 5417c103ddeSKenneth D. Merry * do the right thing, even if there isn't data to map, but since CCBs 54276babe50SJustin T. Gibbs * without data are a reasonably common occurance (e.g. test unit 54376babe50SJustin T. Gibbs * ready), it will save a few cycles if we check for it here. 54476babe50SJustin T. Gibbs */ 54576babe50SJustin T. Gibbs if (((ccb->ccb_h.flags & CAM_DATA_PHYS) == 0) 54652c9ce25SScott Long && (((ccb->ccb_h.func_code == XPT_SCSI_IO || 54752c9ce25SScott Long ccb->ccb_h.func_code == XPT_ATA_IO) 54876babe50SJustin T. Gibbs && ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)) 54906e79492SKenneth D. Merry || (ccb->ccb_h.func_code == XPT_DEV_MATCH) 55006e79492SKenneth D. Merry || (ccb->ccb_h.func_code == XPT_SMP_IO) 5517c103ddeSKenneth D. Merry || ((ccb->ccb_h.func_code == XPT_GDEV_ADVINFO) 5527c103ddeSKenneth D. Merry && (ccb->cgdai.bufsiz > 0)))) { 55376babe50SJustin T. Gibbs 55476babe50SJustin T. Gibbs bzero(&mapinfo, sizeof(mapinfo)); 55576babe50SJustin T. Gibbs 5562b83592fSScott Long /* 5572b83592fSScott Long * cam_periph_mapmem calls into proc and vm functions that can 5582b83592fSScott Long * sleep as well as trigger I/O, so we can't hold the lock. 5592b83592fSScott Long * Dropping it here is reasonably safe. 5602b83592fSScott Long */ 5612b83592fSScott Long cam_periph_unlock(periph); 56276babe50SJustin T. Gibbs error = cam_periph_mapmem(ccb, &mapinfo); 5632b83592fSScott Long cam_periph_lock(periph); 56476babe50SJustin T. Gibbs 56576babe50SJustin T. Gibbs /* 56676babe50SJustin T. Gibbs * cam_periph_mapmem returned an error, we can't continue. 56776babe50SJustin T. Gibbs * Return the error to the user. 56876babe50SJustin T. Gibbs */ 56976babe50SJustin T. Gibbs if (error) 57076babe50SJustin T. Gibbs return(error); 57176babe50SJustin T. Gibbs 57276babe50SJustin T. Gibbs /* 57376babe50SJustin T. Gibbs * We successfully mapped the memory in, so we need to 57476babe50SJustin T. Gibbs * unmap it when the transaction is done. 57576babe50SJustin T. Gibbs */ 57676babe50SJustin T. Gibbs need_unmap = 1; 57776babe50SJustin T. Gibbs } 57876babe50SJustin T. Gibbs 57976babe50SJustin T. Gibbs /* 58076babe50SJustin T. Gibbs * If the user wants us to perform any error recovery, then honor 58176babe50SJustin T. Gibbs * that request. Otherwise, it's up to the user to perform any 58276babe50SJustin T. Gibbs * error recovery. 58376babe50SJustin T. Gibbs */ 58483c5d981SAlexander Motin cam_periph_runccb(ccb, 58583c5d981SAlexander Motin (ccb->ccb_h.flags & CAM_PASS_ERR_RECOVER) ? passerror : NULL, 58683c5d981SAlexander Motin /* cam_flags */ CAM_RETRY_SELTO, /* sense_flags */SF_RETRY_UA, 587a9d2245eSPoul-Henning Kamp softc->device_stats); 58876babe50SJustin T. Gibbs 58976babe50SJustin T. Gibbs if (need_unmap != 0) 59076babe50SJustin T. Gibbs cam_periph_unmapmem(ccb, &mapinfo); 59176babe50SJustin T. Gibbs 59276babe50SJustin T. Gibbs ccb->ccb_h.cbfcnp = NULL; 59376babe50SJustin T. Gibbs ccb->ccb_h.periph_priv = inccb->ccb_h.periph_priv; 59476babe50SJustin T. Gibbs bcopy(ccb, inccb, sizeof(union ccb)); 59576babe50SJustin T. Gibbs 59683c5d981SAlexander Motin return(0); 59776babe50SJustin T. Gibbs } 59876babe50SJustin T. Gibbs 59976babe50SJustin T. Gibbs static int 60076babe50SJustin T. Gibbs passerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) 60176babe50SJustin T. Gibbs { 60276babe50SJustin T. Gibbs struct cam_periph *periph; 60376babe50SJustin T. Gibbs struct pass_softc *softc; 60476babe50SJustin T. Gibbs 60576babe50SJustin T. Gibbs periph = xpt_path_periph(ccb->ccb_h.path); 60676babe50SJustin T. Gibbs softc = (struct pass_softc *)periph->softc; 60776babe50SJustin T. Gibbs 60876babe50SJustin T. Gibbs return(cam_periph_error(ccb, cam_flags, sense_flags, 60976babe50SJustin T. Gibbs &softc->saved_ccb)); 61076babe50SJustin T. Gibbs } 611