11eba4c79SScott Long /*- 2bec9534dSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3bec9534dSPedro F. Giffuni * 41eba4c79SScott Long * Copyright (c) 2007 Scott Long 51eba4c79SScott Long * All rights reserved. 61eba4c79SScott Long * 71eba4c79SScott Long * Redistribution and use in source and binary forms, with or without 81eba4c79SScott Long * modification, are permitted provided that the following conditions 91eba4c79SScott Long * are met: 101eba4c79SScott Long * 1. Redistributions of source code must retain the above copyright 111eba4c79SScott Long * notice, this list of conditions, and the following disclaimer, 121eba4c79SScott Long * without modification, immediately at the beginning of the file. 131eba4c79SScott Long * 2. The name of the author may not be used to endorse or promote products 141eba4c79SScott Long * derived from this software without specific prior written permission. 151eba4c79SScott Long * 161eba4c79SScott Long * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 171eba4c79SScott Long * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 181eba4c79SScott Long * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 191eba4c79SScott Long * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 201eba4c79SScott Long * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 211eba4c79SScott Long * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 221eba4c79SScott Long * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 231eba4c79SScott Long * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 241eba4c79SScott Long * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 251eba4c79SScott Long * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 261eba4c79SScott Long * SUCH DAMAGE. 271eba4c79SScott Long */ 281eba4c79SScott Long 291eba4c79SScott Long /* 301eba4c79SScott Long * scsi_sg peripheral driver. This driver is meant to implement the Linux 311eba4c79SScott Long * SG passthrough interface for SCSI. 321eba4c79SScott Long */ 331eba4c79SScott Long 341eba4c79SScott Long #include <sys/cdefs.h> 351eba4c79SScott Long __FBSDID("$FreeBSD$"); 361eba4c79SScott Long 371eba4c79SScott Long #include <sys/param.h> 381eba4c79SScott Long #include <sys/systm.h> 391eba4c79SScott Long #include <sys/kernel.h> 401eba4c79SScott Long #include <sys/types.h> 411eba4c79SScott Long #include <sys/bio.h> 421eba4c79SScott Long #include <sys/malloc.h> 431eba4c79SScott Long #include <sys/fcntl.h> 441eba4c79SScott Long #include <sys/ioccom.h> 451eba4c79SScott Long #include <sys/conf.h> 461eba4c79SScott Long #include <sys/errno.h> 471eba4c79SScott Long #include <sys/devicestat.h> 481eba4c79SScott Long #include <sys/proc.h> 491eba4c79SScott Long #include <sys/uio.h> 501eba4c79SScott Long 511eba4c79SScott Long #include <cam/cam.h> 521eba4c79SScott Long #include <cam/cam_ccb.h> 531eba4c79SScott Long #include <cam/cam_periph.h> 541eba4c79SScott Long #include <cam/cam_queue.h> 551eba4c79SScott Long #include <cam/cam_xpt_periph.h> 561eba4c79SScott Long #include <cam/cam_debug.h> 571eba4c79SScott Long #include <cam/cam_sim.h> 581eba4c79SScott Long 591eba4c79SScott Long #include <cam/scsi/scsi_all.h> 601eba4c79SScott Long #include <cam/scsi/scsi_message.h> 611eba4c79SScott Long #include <cam/scsi/scsi_sg.h> 621eba4c79SScott Long 631eba4c79SScott Long #include <compat/linux/linux_ioctl.h> 641eba4c79SScott Long 651eba4c79SScott Long typedef enum { 66c552ebe1SKenneth D. Merry SG_FLAG_LOCKED = 0x01, 67c552ebe1SKenneth D. Merry SG_FLAG_INVALID = 0x02 681eba4c79SScott Long } sg_flags; 691eba4c79SScott Long 701eba4c79SScott Long typedef enum { 711eba4c79SScott Long SG_STATE_NORMAL 721eba4c79SScott Long } sg_state; 731eba4c79SScott Long 741eba4c79SScott Long typedef enum { 75472cdbefSScott Long SG_RDWR_FREE, 761eba4c79SScott Long SG_RDWR_INPROG, 771eba4c79SScott Long SG_RDWR_DONE 781eba4c79SScott Long } sg_rdwr_state; 791eba4c79SScott Long 801eba4c79SScott Long typedef enum { 81227d67aaSAlexander Motin SG_CCB_RDWR_IO 821eba4c79SScott Long } sg_ccb_types; 831eba4c79SScott Long 841eba4c79SScott Long #define ccb_type ppriv_field0 851eba4c79SScott Long #define ccb_rdwr ppriv_ptr1 861eba4c79SScott Long 871eba4c79SScott Long struct sg_rdwr { 881eba4c79SScott Long TAILQ_ENTRY(sg_rdwr) rdwr_link; 891eba4c79SScott Long int tag; 901eba4c79SScott Long int state; 911eba4c79SScott Long int buf_len; 921eba4c79SScott Long char *buf; 931eba4c79SScott Long union ccb *ccb; 941eba4c79SScott Long union { 951eba4c79SScott Long struct sg_header hdr; 961eba4c79SScott Long struct sg_io_hdr io_hdr; 971eba4c79SScott Long } hdr; 981eba4c79SScott Long }; 991eba4c79SScott Long 1001eba4c79SScott Long struct sg_softc { 1011eba4c79SScott Long sg_state state; 1021eba4c79SScott Long sg_flags flags; 10386d45c7fSKenneth D. Merry int open_count; 104de239312SAlexander Motin u_int maxio; 1051eba4c79SScott Long struct devstat *device_stats; 1061eba4c79SScott Long TAILQ_HEAD(, sg_rdwr) rdwr_done; 1071eba4c79SScott Long struct cdev *dev; 108715ab212SScott Long int sg_timeout; 109715ab212SScott Long int sg_user_timeout; 110715ab212SScott Long uint8_t pd_type; 1111eba4c79SScott Long union ccb saved_ccb; 1121eba4c79SScott Long }; 1131eba4c79SScott Long 1141eba4c79SScott Long static d_open_t sgopen; 1151eba4c79SScott Long static d_close_t sgclose; 1161eba4c79SScott Long static d_ioctl_t sgioctl; 1171eba4c79SScott Long static d_write_t sgwrite; 1181eba4c79SScott Long static d_read_t sgread; 1191eba4c79SScott Long 1201eba4c79SScott Long static periph_init_t sginit; 1211eba4c79SScott Long static periph_ctor_t sgregister; 1221eba4c79SScott Long static periph_oninv_t sgoninvalidate; 1231eba4c79SScott Long static periph_dtor_t sgcleanup; 1241eba4c79SScott Long static void sgasync(void *callback_arg, uint32_t code, 1251eba4c79SScott Long struct cam_path *path, void *arg); 1261eba4c79SScott Long static void sgdone(struct cam_periph *periph, union ccb *done_ccb); 1271eba4c79SScott Long static int sgsendccb(struct cam_periph *periph, union ccb *ccb); 1281eba4c79SScott Long static int sgsendrdwr(struct cam_periph *periph, union ccb *ccb); 1291eba4c79SScott Long static int sgerror(union ccb *ccb, uint32_t cam_flags, 1301eba4c79SScott Long uint32_t sense_flags); 1311eba4c79SScott Long static void sg_scsiio_status(struct ccb_scsiio *csio, 1321eba4c79SScott Long u_short *hoststat, u_short *drvstat); 1331eba4c79SScott Long 1341eba4c79SScott Long static int scsi_group_len(u_char cmd); 1351eba4c79SScott Long 1361eba4c79SScott Long static struct periph_driver sgdriver = 1371eba4c79SScott Long { 1381eba4c79SScott Long sginit, "sg", 1391eba4c79SScott Long TAILQ_HEAD_INITIALIZER(sgdriver.units), /* gen */ 0 1401eba4c79SScott Long }; 1411eba4c79SScott Long PERIPHDRIVER_DECLARE(sg, sgdriver); 1421eba4c79SScott Long 1431eba4c79SScott Long static struct cdevsw sg_cdevsw = { 1441eba4c79SScott Long .d_version = D_VERSION, 145f0d6f577SScott Long .d_flags = D_TRACKCLOSE, 1461eba4c79SScott Long .d_open = sgopen, 1471eba4c79SScott Long .d_close = sgclose, 1481eba4c79SScott Long .d_ioctl = sgioctl, 1491eba4c79SScott Long .d_write = sgwrite, 1501eba4c79SScott Long .d_read = sgread, 1511eba4c79SScott Long .d_name = "sg", 1521eba4c79SScott Long }; 1531eba4c79SScott Long 1541eba4c79SScott Long static int sg_version = 30125; 1551eba4c79SScott Long 1561eba4c79SScott Long static void 1571eba4c79SScott Long sginit(void) 1581eba4c79SScott Long { 1591eba4c79SScott Long cam_status status; 1601eba4c79SScott Long 1611eba4c79SScott Long /* 1621eba4c79SScott Long * Install a global async callback. This callback will receive aync 1631eba4c79SScott Long * callbacks like "new device found". 1641eba4c79SScott Long */ 16585d92640SScott Long status = xpt_register_async(AC_FOUND_DEVICE, sgasync, NULL, NULL); 1661eba4c79SScott Long 1671eba4c79SScott Long if (status != CAM_REQ_CMP) { 1681eba4c79SScott Long printf("sg: Failed to attach master async callbac " 1691eba4c79SScott Long "due to status 0x%x!\n", status); 1701eba4c79SScott Long } 1711eba4c79SScott Long } 1721eba4c79SScott Long 1731eba4c79SScott Long static void 17486d45c7fSKenneth D. Merry sgdevgonecb(void *arg) 17586d45c7fSKenneth D. Merry { 17686d45c7fSKenneth D. Merry struct cam_periph *periph; 17786d45c7fSKenneth D. Merry struct sg_softc *softc; 178227d67aaSAlexander Motin struct mtx *mtx; 17986d45c7fSKenneth D. Merry int i; 18086d45c7fSKenneth D. Merry 18186d45c7fSKenneth D. Merry periph = (struct cam_periph *)arg; 182227d67aaSAlexander Motin mtx = cam_periph_mtx(periph); 183227d67aaSAlexander Motin mtx_lock(mtx); 18486d45c7fSKenneth D. Merry 185227d67aaSAlexander Motin softc = (struct sg_softc *)periph->softc; 18686d45c7fSKenneth D. Merry KASSERT(softc->open_count >= 0, ("Negative open count %d", 18786d45c7fSKenneth D. Merry softc->open_count)); 18886d45c7fSKenneth D. Merry 18986d45c7fSKenneth D. Merry /* 19086d45c7fSKenneth D. Merry * When we get this callback, we will get no more close calls from 19186d45c7fSKenneth D. Merry * devfs. So if we have any dangling opens, we need to release the 19286d45c7fSKenneth D. Merry * reference held for that particular context. 19386d45c7fSKenneth D. Merry */ 19486d45c7fSKenneth D. Merry for (i = 0; i < softc->open_count; i++) 19586d45c7fSKenneth D. Merry cam_periph_release_locked(periph); 19686d45c7fSKenneth D. Merry 19786d45c7fSKenneth D. Merry softc->open_count = 0; 19886d45c7fSKenneth D. Merry 19986d45c7fSKenneth D. Merry /* 20086d45c7fSKenneth D. Merry * Release the reference held for the device node, it is gone now. 20186d45c7fSKenneth D. Merry */ 20286d45c7fSKenneth D. Merry cam_periph_release_locked(periph); 20386d45c7fSKenneth D. Merry 20486d45c7fSKenneth D. Merry /* 205227d67aaSAlexander Motin * We reference the lock directly here, instead of using 20686d45c7fSKenneth D. Merry * cam_periph_unlock(). The reason is that the final call to 20786d45c7fSKenneth D. Merry * cam_periph_release_locked() above could result in the periph 20886d45c7fSKenneth D. Merry * getting freed. If that is the case, dereferencing the periph 20986d45c7fSKenneth D. Merry * with a cam_periph_unlock() call would cause a page fault. 21086d45c7fSKenneth D. Merry */ 211227d67aaSAlexander Motin mtx_unlock(mtx); 21286d45c7fSKenneth D. Merry } 21386d45c7fSKenneth D. Merry 21486d45c7fSKenneth D. Merry 21586d45c7fSKenneth D. Merry static void 2161eba4c79SScott Long sgoninvalidate(struct cam_periph *periph) 2171eba4c79SScott Long { 2181eba4c79SScott Long struct sg_softc *softc; 2191eba4c79SScott Long 2201eba4c79SScott Long softc = (struct sg_softc *)periph->softc; 2211eba4c79SScott Long 2221eba4c79SScott Long /* 2231eba4c79SScott Long * Deregister any async callbacks. 2241eba4c79SScott Long */ 22585d92640SScott Long xpt_register_async(0, sgasync, periph, periph->path); 2261eba4c79SScott Long 2271eba4c79SScott Long softc->flags |= SG_FLAG_INVALID; 2281eba4c79SScott Long 2291eba4c79SScott Long /* 23086d45c7fSKenneth D. Merry * Tell devfs this device has gone away, and ask for a callback 23186d45c7fSKenneth D. Merry * when it has cleaned up its state. 23286d45c7fSKenneth D. Merry */ 23386d45c7fSKenneth D. Merry destroy_dev_sched_cb(softc->dev, sgdevgonecb, periph); 23486d45c7fSKenneth D. Merry 23586d45c7fSKenneth D. Merry /* 2361eba4c79SScott Long * XXX Return all queued I/O with ENXIO. 2371eba4c79SScott Long * XXX Handle any transactions queued to the card 2381eba4c79SScott Long * with XPT_ABORT_CCB. 2391eba4c79SScott Long */ 2401eba4c79SScott Long 2411eba4c79SScott Long } 2421eba4c79SScott Long 2431eba4c79SScott Long static void 2441eba4c79SScott Long sgcleanup(struct cam_periph *periph) 2451eba4c79SScott Long { 2461eba4c79SScott Long struct sg_softc *softc; 2471eba4c79SScott Long 2481eba4c79SScott Long softc = (struct sg_softc *)periph->softc; 24986d45c7fSKenneth D. Merry 2505f3fed85SEdward Tomasz Napierala devstat_remove_entry(softc->device_stats); 25186d45c7fSKenneth D. Merry 2521eba4c79SScott Long free(softc, M_DEVBUF); 2531eba4c79SScott Long } 2541eba4c79SScott Long 2551eba4c79SScott Long static void 2561eba4c79SScott Long sgasync(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) 2571eba4c79SScott Long { 2581eba4c79SScott Long struct cam_periph *periph; 2591eba4c79SScott Long 2601eba4c79SScott Long periph = (struct cam_periph *)callback_arg; 2611eba4c79SScott Long 2621eba4c79SScott Long switch (code) { 2631eba4c79SScott Long case AC_FOUND_DEVICE: 2641eba4c79SScott Long { 2651eba4c79SScott Long struct ccb_getdev *cgd; 2661eba4c79SScott Long cam_status status; 2671eba4c79SScott Long 2681eba4c79SScott Long cgd = (struct ccb_getdev *)arg; 2691eba4c79SScott Long if (cgd == NULL) 2701eba4c79SScott Long break; 2711eba4c79SScott Long 27252c9ce25SScott Long if (cgd->protocol != PROTO_SCSI) 27352c9ce25SScott Long break; 27452c9ce25SScott Long 2751eba4c79SScott Long /* 2761eba4c79SScott Long * Allocate a peripheral instance for this device and 2771eba4c79SScott Long * start the probe process. 2781eba4c79SScott Long */ 2791eba4c79SScott Long status = cam_periph_alloc(sgregister, sgoninvalidate, 280227d67aaSAlexander Motin sgcleanup, NULL, "sg", 281227d67aaSAlexander Motin CAM_PERIPH_BIO, path, 2821eba4c79SScott Long sgasync, AC_FOUND_DEVICE, cgd); 2831eba4c79SScott Long if ((status != CAM_REQ_CMP) && (status != CAM_REQ_INPROG)) { 2841eba4c79SScott Long const struct cam_status_entry *entry; 2851eba4c79SScott Long 2861eba4c79SScott Long entry = cam_fetch_status_entry(status); 2871eba4c79SScott Long printf("sgasync: Unable to attach new device " 2881eba4c79SScott Long "due to status %#x: %s\n", status, entry ? 2891eba4c79SScott Long entry->status_text : "Unknown"); 2901eba4c79SScott Long } 2911eba4c79SScott Long break; 2921eba4c79SScott Long } 2931eba4c79SScott Long default: 2941eba4c79SScott Long cam_periph_async(periph, code, path, arg); 2951eba4c79SScott Long break; 2961eba4c79SScott Long } 2971eba4c79SScott Long } 2981eba4c79SScott Long 2991eba4c79SScott Long static cam_status 3001eba4c79SScott Long sgregister(struct cam_periph *periph, void *arg) 3011eba4c79SScott Long { 3021eba4c79SScott Long struct sg_softc *softc; 3031eba4c79SScott Long struct ccb_getdev *cgd; 304b8b6b5d3SAlexander Motin struct ccb_pathinq cpi; 305ee198893SKonstantin Belousov struct make_dev_args args; 306ee198893SKonstantin Belousov int no_tags, error; 3071eba4c79SScott Long 3081eba4c79SScott Long cgd = (struct ccb_getdev *)arg; 3091eba4c79SScott Long if (cgd == NULL) { 3101eba4c79SScott Long printf("sgregister: no getdev CCB, can't register device\n"); 3111eba4c79SScott Long return (CAM_REQ_CMP_ERR); 3121eba4c79SScott Long } 3131eba4c79SScott Long 3144400b36dSScott Long softc = malloc(sizeof(*softc), M_DEVBUF, M_ZERO | M_NOWAIT); 3151eba4c79SScott Long if (softc == NULL) { 3161eba4c79SScott Long printf("sgregister: Unable to allocate softc\n"); 3171eba4c79SScott Long return (CAM_REQ_CMP_ERR); 3181eba4c79SScott Long } 3191eba4c79SScott Long 3201eba4c79SScott Long softc->state = SG_STATE_NORMAL; 3211eba4c79SScott Long softc->pd_type = SID_TYPE(&cgd->inq_data); 322715ab212SScott Long softc->sg_timeout = SG_DEFAULT_TIMEOUT / SG_DEFAULT_HZ * hz; 323715ab212SScott Long softc->sg_user_timeout = SG_DEFAULT_TIMEOUT; 3241eba4c79SScott Long TAILQ_INIT(&softc->rdwr_done); 3251eba4c79SScott Long periph->softc = softc; 3261eba4c79SScott Long 327762a7f4fSWarner Losh xpt_path_inq(&cpi, periph->path); 328b8b6b5d3SAlexander Motin 329de239312SAlexander Motin if (cpi.maxio == 0) 330de239312SAlexander Motin softc->maxio = DFLTPHYS; /* traditional default */ 331de239312SAlexander Motin else if (cpi.maxio > MAXPHYS) 332de239312SAlexander Motin softc->maxio = MAXPHYS; /* for safety */ 333de239312SAlexander Motin else 334de239312SAlexander Motin softc->maxio = cpi.maxio; /* real value */ 335de239312SAlexander Motin 3361eba4c79SScott Long /* 3371eba4c79SScott Long * We pass in 0 for all blocksize, since we don't know what the 3381eba4c79SScott Long * blocksize of the device is, if it even has a blocksize. 3391eba4c79SScott Long */ 34085d92640SScott Long cam_periph_unlock(periph); 3411eba4c79SScott Long no_tags = (cgd->inq_data.flags & SID_CmdQue) == 0; 3421eba4c79SScott Long softc->device_stats = devstat_new_entry("sg", 343d3ce8327SEd Schouten periph->unit_number, 0, 3441eba4c79SScott Long DEVSTAT_NO_BLOCKSIZE 3451eba4c79SScott Long | (no_tags ? DEVSTAT_NO_ORDERED_TAGS : 0), 3461eba4c79SScott Long softc->pd_type | 347b8b6b5d3SAlexander Motin XPORT_DEVSTAT_TYPE(cpi.transport) | 3481eba4c79SScott Long DEVSTAT_TYPE_PASS, 3491eba4c79SScott Long DEVSTAT_PRIORITY_PASS); 3501eba4c79SScott Long 35186d45c7fSKenneth D. Merry /* 35286d45c7fSKenneth D. Merry * Acquire a reference to the periph before we create the devfs 35386d45c7fSKenneth D. Merry * instance for it. We'll release this reference once the devfs 35486d45c7fSKenneth D. Merry * instance has been freed. 35586d45c7fSKenneth D. Merry */ 35699e7a4adSScott Long if (cam_periph_acquire(periph) != 0) { 35786d45c7fSKenneth D. Merry xpt_print(periph->path, "%s: lost periph during " 35886d45c7fSKenneth D. Merry "registration!\n", __func__); 35986d45c7fSKenneth D. Merry cam_periph_lock(periph); 36086d45c7fSKenneth D. Merry return (CAM_REQ_CMP_ERR); 36186d45c7fSKenneth D. Merry } 36286d45c7fSKenneth D. Merry 3631eba4c79SScott Long /* Register the device */ 364ee198893SKonstantin Belousov make_dev_args_init(&args); 365ee198893SKonstantin Belousov args.mda_devsw = &sg_cdevsw; 366ee198893SKonstantin Belousov args.mda_unit = periph->unit_number; 367ee198893SKonstantin Belousov args.mda_uid = UID_ROOT; 368ee198893SKonstantin Belousov args.mda_gid = GID_OPERATOR; 369ee198893SKonstantin Belousov args.mda_mode = 0600; 370ee198893SKonstantin Belousov args.mda_si_drv1 = periph; 371ee198893SKonstantin Belousov error = make_dev_s(&args, &softc->dev, "%s%d", 3721eba4c79SScott Long periph->periph_name, periph->unit_number); 373ee198893SKonstantin Belousov if (error != 0) { 374ee198893SKonstantin Belousov cam_periph_lock(periph); 375ee198893SKonstantin Belousov cam_periph_release_locked(periph); 376ee198893SKonstantin Belousov return (CAM_REQ_CMP_ERR); 377ee198893SKonstantin Belousov } 378cf454e30SMatt Jacob if (periph->unit_number < 26) { 379c59b4dcdSMatt Jacob (void)make_dev_alias(softc->dev, "sg%c", 380c59b4dcdSMatt Jacob periph->unit_number + 'a'); 381cf454e30SMatt Jacob } else { 382cf454e30SMatt Jacob (void)make_dev_alias(softc->dev, "sg%c%c", 383c59b4dcdSMatt Jacob ((periph->unit_number / 26) - 1) + 'a', 384c59b4dcdSMatt Jacob (periph->unit_number % 26) + 'a'); 385cf454e30SMatt Jacob } 3862b83592fSScott Long cam_periph_lock(periph); 3871eba4c79SScott Long 3881eba4c79SScott Long /* 3891eba4c79SScott Long * Add as async callback so that we get 3901eba4c79SScott Long * notified if this device goes away. 3911eba4c79SScott Long */ 39285d92640SScott Long xpt_register_async(AC_LOST_DEVICE, sgasync, periph, periph->path); 3931eba4c79SScott Long 3941eba4c79SScott Long if (bootverbose) 3951eba4c79SScott Long xpt_announce_periph(periph, NULL); 3961eba4c79SScott Long 3971eba4c79SScott Long return (CAM_REQ_CMP); 3981eba4c79SScott Long } 3991eba4c79SScott Long 4001eba4c79SScott Long static void 4011eba4c79SScott Long sgdone(struct cam_periph *periph, union ccb *done_ccb) 4021eba4c79SScott Long { 4031eba4c79SScott Long struct sg_softc *softc; 4041eba4c79SScott Long struct ccb_scsiio *csio; 4051eba4c79SScott Long 4061eba4c79SScott Long softc = (struct sg_softc *)periph->softc; 4071eba4c79SScott Long csio = &done_ccb->csio; 4081eba4c79SScott Long switch (csio->ccb_h.ccb_type) { 4091eba4c79SScott Long case SG_CCB_RDWR_IO: 4101eba4c79SScott Long { 4111eba4c79SScott Long struct sg_rdwr *rdwr; 4121eba4c79SScott Long int state; 4131eba4c79SScott Long 4141eba4c79SScott Long devstat_end_transaction(softc->device_stats, 4151eba4c79SScott Long csio->dxfer_len, 4161eba4c79SScott Long csio->tag_action & 0xf, 4171eba4c79SScott Long ((csio->ccb_h.flags & CAM_DIR_MASK) == 4181eba4c79SScott Long CAM_DIR_NONE) ? DEVSTAT_NO_DATA : 4191eba4c79SScott Long (csio->ccb_h.flags & CAM_DIR_OUT) ? 4201eba4c79SScott Long DEVSTAT_WRITE : DEVSTAT_READ, 4211eba4c79SScott Long NULL, NULL); 4221eba4c79SScott Long 4231eba4c79SScott Long rdwr = done_ccb->ccb_h.ccb_rdwr; 4241eba4c79SScott Long state = rdwr->state; 4251eba4c79SScott Long rdwr->state = SG_RDWR_DONE; 4261eba4c79SScott Long wakeup(rdwr); 4271eba4c79SScott Long break; 4281eba4c79SScott Long } 4291eba4c79SScott Long default: 4301eba4c79SScott Long panic("unknown sg CCB type"); 4311eba4c79SScott Long } 4321eba4c79SScott Long } 4331eba4c79SScott Long 4341eba4c79SScott Long static int 4351eba4c79SScott Long sgopen(struct cdev *dev, int flags, int fmt, struct thread *td) 4361eba4c79SScott Long { 4371eba4c79SScott Long struct cam_periph *periph; 4381eba4c79SScott Long struct sg_softc *softc; 4391eba4c79SScott Long int error = 0; 4401eba4c79SScott Long 4411eba4c79SScott Long periph = (struct cam_periph *)dev->si_drv1; 44299e7a4adSScott Long if (cam_periph_acquire(periph) != 0) 4438900f4b8SKenneth D. Merry return (ENXIO); 4448900f4b8SKenneth D. Merry 4451eba4c79SScott Long /* 4461eba4c79SScott Long * Don't allow access when we're running at a high securelevel. 4471eba4c79SScott Long */ 4481eba4c79SScott Long error = securelevel_gt(td->td_ucred, 1); 4498900f4b8SKenneth D. Merry if (error) { 4508900f4b8SKenneth D. Merry cam_periph_release(periph); 4511eba4c79SScott Long return (error); 4528900f4b8SKenneth D. Merry } 4531eba4c79SScott Long 4542b83592fSScott Long cam_periph_lock(periph); 4552b83592fSScott Long 4562b83592fSScott Long softc = (struct sg_softc *)periph->softc; 4572b83592fSScott Long if (softc->flags & SG_FLAG_INVALID) { 458c552ebe1SKenneth D. Merry cam_periph_release_locked(periph); 4592b83592fSScott Long cam_periph_unlock(periph); 4602b83592fSScott Long return (ENXIO); 4612b83592fSScott Long } 4621eba4c79SScott Long 46386d45c7fSKenneth D. Merry softc->open_count++; 46486d45c7fSKenneth D. Merry 465835187bfSScott Long cam_periph_unlock(periph); 4661eba4c79SScott Long 4671eba4c79SScott Long return (error); 4681eba4c79SScott Long } 4691eba4c79SScott Long 4701eba4c79SScott Long static int 4711eba4c79SScott Long sgclose(struct cdev *dev, int flag, int fmt, struct thread *td) 4721eba4c79SScott Long { 4731eba4c79SScott Long struct cam_periph *periph; 47486d45c7fSKenneth D. Merry struct sg_softc *softc; 475227d67aaSAlexander Motin struct mtx *mtx; 4761eba4c79SScott Long 4771eba4c79SScott Long periph = (struct cam_periph *)dev->si_drv1; 478227d67aaSAlexander Motin mtx = cam_periph_mtx(periph); 479227d67aaSAlexander Motin mtx_lock(mtx); 4801eba4c79SScott Long 48186d45c7fSKenneth D. Merry softc = periph->softc; 48286d45c7fSKenneth D. Merry softc->open_count--; 48386d45c7fSKenneth D. Merry 48486d45c7fSKenneth D. Merry cam_periph_release_locked(periph); 48586d45c7fSKenneth D. Merry 48686d45c7fSKenneth D. Merry /* 487227d67aaSAlexander Motin * We reference the lock directly here, instead of using 48886d45c7fSKenneth D. Merry * cam_periph_unlock(). The reason is that the call to 48986d45c7fSKenneth D. Merry * cam_periph_release_locked() above could result in the periph 49086d45c7fSKenneth D. Merry * getting freed. If that is the case, dereferencing the periph 49186d45c7fSKenneth D. Merry * with a cam_periph_unlock() call would cause a page fault. 49286d45c7fSKenneth D. Merry * 49386d45c7fSKenneth D. Merry * cam_periph_release() avoids this problem using the same method, 49486d45c7fSKenneth D. Merry * but we're manually acquiring and dropping the lock here to 49586d45c7fSKenneth D. Merry * protect the open count and avoid another lock acquisition and 49686d45c7fSKenneth D. Merry * release. 49786d45c7fSKenneth D. Merry */ 498227d67aaSAlexander Motin mtx_unlock(mtx); 4991eba4c79SScott Long 5001eba4c79SScott Long return (0); 5011eba4c79SScott Long } 5021eba4c79SScott Long 5031eba4c79SScott Long static int 5041eba4c79SScott Long sgioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) 5051eba4c79SScott Long { 5061eba4c79SScott Long union ccb *ccb; 5071eba4c79SScott Long struct ccb_scsiio *csio; 5081eba4c79SScott Long struct cam_periph *periph; 5091eba4c79SScott Long struct sg_softc *softc; 510fcaf473cSAlexander Motin struct sg_io_hdr *req; 511*81b62a76SJohn Baldwin void *data_ptr; 5121eba4c79SScott Long int dir, error; 5131eba4c79SScott Long 5141eba4c79SScott Long periph = (struct cam_periph *)dev->si_drv1; 5152b83592fSScott Long cam_periph_lock(periph); 5162b83592fSScott Long 5171eba4c79SScott Long softc = (struct sg_softc *)periph->softc; 5181eba4c79SScott Long error = 0; 5191eba4c79SScott Long 5201eba4c79SScott Long switch (cmd) { 5211eba4c79SScott Long case SG_GET_VERSION_NUM: 522fcaf473cSAlexander Motin { 523fcaf473cSAlexander Motin int *version = (int *)arg; 524715ab212SScott Long 525fcaf473cSAlexander Motin *version = sg_version; 526fcaf473cSAlexander Motin break; 527fcaf473cSAlexander Motin } 528fcaf473cSAlexander Motin case SG_SET_TIMEOUT: 529fcaf473cSAlexander Motin { 530fcaf473cSAlexander Motin u_int user_timeout = *(u_int *)arg; 531fcaf473cSAlexander Motin 532715ab212SScott Long softc->sg_user_timeout = user_timeout; 533715ab212SScott Long softc->sg_timeout = user_timeout / SG_DEFAULT_HZ * hz; 5341eba4c79SScott Long break; 535715ab212SScott Long } 5361eba4c79SScott Long case SG_GET_TIMEOUT: 5371eba4c79SScott Long /* 538715ab212SScott Long * The value is returned directly to the syscall. 5391eba4c79SScott Long */ 540715ab212SScott Long td->td_retval[0] = softc->sg_user_timeout; 5411eba4c79SScott Long error = 0; 5421eba4c79SScott Long break; 5431eba4c79SScott Long case SG_IO: 544fcaf473cSAlexander Motin req = (struct sg_io_hdr *)arg; 5451eba4c79SScott Long 546fcaf473cSAlexander Motin if (req->cmd_len > IOCDBLEN) { 5471eba4c79SScott Long error = EINVAL; 5481eba4c79SScott Long break; 5491eba4c79SScott Long } 5501eba4c79SScott Long 551fcaf473cSAlexander Motin if (req->iovec_count != 0) { 5521eba4c79SScott Long error = EOPNOTSUPP; 5531eba4c79SScott Long break; 5541eba4c79SScott Long } 5551eba4c79SScott Long 556*81b62a76SJohn Baldwin if (req->dxfer_len > MAXPHYS) { 557*81b62a76SJohn Baldwin error = EINVAL; 558*81b62a76SJohn Baldwin break; 559*81b62a76SJohn Baldwin } 560*81b62a76SJohn Baldwin 561*81b62a76SJohn Baldwin data_ptr = malloc(req->dxfer_len, M_DEVBUF, M_WAITOK); 562*81b62a76SJohn Baldwin 5631e637ba6SAlexander Motin ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); 5641eba4c79SScott Long csio = &ccb->csio; 5651eba4c79SScott Long 566fcaf473cSAlexander Motin error = copyin(req->cmdp, &csio->cdb_io.cdb_bytes, 567fcaf473cSAlexander Motin req->cmd_len); 5681eba4c79SScott Long if (error) { 569*81b62a76SJohn Baldwin free(data_ptr, M_DEVBUF); 5701eba4c79SScott Long xpt_release_ccb(ccb); 5711eba4c79SScott Long break; 5721eba4c79SScott Long } 5731eba4c79SScott Long 574fcaf473cSAlexander Motin switch(req->dxfer_direction) { 5751eba4c79SScott Long case SG_DXFER_TO_DEV: 5761eba4c79SScott Long dir = CAM_DIR_OUT; 5771eba4c79SScott Long break; 5781eba4c79SScott Long case SG_DXFER_FROM_DEV: 5791eba4c79SScott Long dir = CAM_DIR_IN; 5801eba4c79SScott Long break; 5811eba4c79SScott Long case SG_DXFER_TO_FROM_DEV: 582*81b62a76SJohn Baldwin dir = CAM_DIR_BOTH; 5831eba4c79SScott Long break; 5841eba4c79SScott Long case SG_DXFER_NONE: 5851eba4c79SScott Long default: 5861eba4c79SScott Long dir = CAM_DIR_NONE; 5871eba4c79SScott Long break; 5881eba4c79SScott Long } 5891eba4c79SScott Long 590*81b62a76SJohn Baldwin if (dir == CAM_DIR_IN || dir == CAM_DIR_BOTH) { 591*81b62a76SJohn Baldwin error = copyin(req->dxferp, data_ptr, req->dxfer_len); 592*81b62a76SJohn Baldwin if (error) { 593*81b62a76SJohn Baldwin free(data_ptr, M_DEVBUF); 594*81b62a76SJohn Baldwin xpt_release_ccb(ccb); 595*81b62a76SJohn Baldwin break; 596*81b62a76SJohn Baldwin } 597*81b62a76SJohn Baldwin } 598*81b62a76SJohn Baldwin 5991eba4c79SScott Long cam_fill_csio(csio, 6001eba4c79SScott Long /*retries*/1, 601eed99e75SScott Long /*cbfcnp*/NULL, 6021eba4c79SScott Long dir|CAM_DEV_QFRZDIS, 6031eba4c79SScott Long MSG_SIMPLE_Q_TAG, 604*81b62a76SJohn Baldwin data_ptr, 605fcaf473cSAlexander Motin req->dxfer_len, 606fcaf473cSAlexander Motin req->mx_sb_len, 607fcaf473cSAlexander Motin req->cmd_len, 608fcaf473cSAlexander Motin req->timeout); 6091eba4c79SScott Long 6101eba4c79SScott Long error = sgsendccb(periph, ccb); 6111eba4c79SScott Long if (error) { 612fcaf473cSAlexander Motin req->host_status = DID_ERROR; 613fcaf473cSAlexander Motin req->driver_status = DRIVER_INVALID; 614*81b62a76SJohn Baldwin free(data_ptr, M_DEVBUF); 6151eba4c79SScott Long xpt_release_ccb(ccb); 6161eba4c79SScott Long break; 6171eba4c79SScott Long } 6181eba4c79SScott Long 619fcaf473cSAlexander Motin req->status = csio->scsi_status; 620fcaf473cSAlexander Motin req->masked_status = (csio->scsi_status >> 1) & 0x7f; 621fcaf473cSAlexander Motin sg_scsiio_status(csio, &req->host_status, &req->driver_status); 622fcaf473cSAlexander Motin req->resid = csio->resid; 623fcaf473cSAlexander Motin req->duration = csio->ccb_h.timeout; 624fcaf473cSAlexander Motin req->info = 0; 6251eba4c79SScott Long 626fcaf473cSAlexander Motin if ((csio->ccb_h.status & CAM_AUTOSNS_VALID) 627fcaf473cSAlexander Motin && (req->sbp != NULL)) { 628fcaf473cSAlexander Motin req->sb_len_wr = req->mx_sb_len - csio->sense_resid; 629fcaf473cSAlexander Motin error = copyout(&csio->sense_data, req->sbp, 630fcaf473cSAlexander Motin req->sb_len_wr); 6311eba4c79SScott Long } 6321eba4c79SScott Long 633*81b62a76SJohn Baldwin if ((dir == CAM_DIR_OUT || dir == CAM_DIR_BOTH) && error == 0) 634*81b62a76SJohn Baldwin error = copyout(data_ptr, req->dxferp, req->dxfer_len); 635*81b62a76SJohn Baldwin 636*81b62a76SJohn Baldwin free(data_ptr, M_DEVBUF); 6371eba4c79SScott Long xpt_release_ccb(ccb); 6381eba4c79SScott Long break; 6391eba4c79SScott Long 6401eba4c79SScott Long case SG_GET_RESERVED_SIZE: 641fcaf473cSAlexander Motin { 642fcaf473cSAlexander Motin int *size = (int *)arg; 643fcaf473cSAlexander Motin *size = DFLTPHYS; 6441eba4c79SScott Long break; 6451eba4c79SScott Long } 6461eba4c79SScott Long 6471eba4c79SScott Long case SG_GET_SCSI_ID: 6481eba4c79SScott Long { 649fcaf473cSAlexander Motin struct sg_scsi_id *id = (struct sg_scsi_id *)arg; 6501eba4c79SScott Long 651fcaf473cSAlexander Motin id->host_no = cam_sim_path(xpt_path_sim(periph->path)); 652fcaf473cSAlexander Motin id->channel = xpt_path_path_id(periph->path); 653fcaf473cSAlexander Motin id->scsi_id = xpt_path_target_id(periph->path); 654fcaf473cSAlexander Motin id->lun = xpt_path_lun_id(periph->path); 655fcaf473cSAlexander Motin id->scsi_type = softc->pd_type; 656fcaf473cSAlexander Motin id->h_cmd_per_lun = 1; 657fcaf473cSAlexander Motin id->d_queue_depth = 1; 658fcaf473cSAlexander Motin id->unused[0] = 0; 659fcaf473cSAlexander Motin id->unused[1] = 0; 6601eba4c79SScott Long break; 6611eba4c79SScott Long } 6621eba4c79SScott Long 66394fe9f95SAlexander Motin case SG_GET_SG_TABLESIZE: 66494fe9f95SAlexander Motin { 66594fe9f95SAlexander Motin int *size = (int *)arg; 66694fe9f95SAlexander Motin *size = 0; 66794fe9f95SAlexander Motin break; 66894fe9f95SAlexander Motin } 66994fe9f95SAlexander Motin 6701eba4c79SScott Long case SG_EMULATED_HOST: 6711eba4c79SScott Long case SG_SET_TRANSFORM: 6721eba4c79SScott Long case SG_GET_TRANSFORM: 6731eba4c79SScott Long case SG_GET_NUM_WAITING: 6741eba4c79SScott Long case SG_SCSI_RESET: 6751eba4c79SScott Long case SG_GET_REQUEST_TABLE: 6761eba4c79SScott Long case SG_SET_KEEP_ORPHAN: 6771eba4c79SScott Long case SG_GET_KEEP_ORPHAN: 6781eba4c79SScott Long case SG_GET_ACCESS_COUNT: 6791eba4c79SScott Long case SG_SET_FORCE_LOW_DMA: 6801eba4c79SScott Long case SG_GET_LOW_DMA: 6811eba4c79SScott Long case SG_SET_FORCE_PACK_ID: 6821eba4c79SScott Long case SG_GET_PACK_ID: 6831eba4c79SScott Long case SG_SET_RESERVED_SIZE: 6841eba4c79SScott Long case SG_GET_COMMAND_Q: 6851eba4c79SScott Long case SG_SET_COMMAND_Q: 6861eba4c79SScott Long case SG_SET_DEBUG: 6871eba4c79SScott Long case SG_NEXT_CMD_LEN: 6881eba4c79SScott Long default: 6891eba4c79SScott Long #ifdef CAMDEBUG 6901eba4c79SScott Long printf("sgioctl: rejecting cmd 0x%lx\n", cmd); 6911eba4c79SScott Long #endif 6921eba4c79SScott Long error = ENODEV; 6931eba4c79SScott Long break; 6941eba4c79SScott Long } 6951eba4c79SScott Long 6962b83592fSScott Long cam_periph_unlock(periph); 6971eba4c79SScott Long return (error); 6981eba4c79SScott Long } 6991eba4c79SScott Long 7001eba4c79SScott Long static int 7011eba4c79SScott Long sgwrite(struct cdev *dev, struct uio *uio, int ioflag) 7021eba4c79SScott Long { 7031eba4c79SScott Long union ccb *ccb; 7041eba4c79SScott Long struct cam_periph *periph; 7051eba4c79SScott Long struct ccb_scsiio *csio; 7061eba4c79SScott Long struct sg_softc *sc; 7071eba4c79SScott Long struct sg_header *hdr; 7081eba4c79SScott Long struct sg_rdwr *rdwr; 7091eba4c79SScott Long u_char cdb_cmd; 7101eba4c79SScott Long char *buf; 7111eba4c79SScott Long int error = 0, cdb_len, buf_len, dir; 7121eba4c79SScott Long 7131eba4c79SScott Long periph = dev->si_drv1; 7144400b36dSScott Long rdwr = malloc(sizeof(*rdwr), M_DEVBUF, M_WAITOK | M_ZERO); 7151eba4c79SScott Long hdr = &rdwr->hdr.hdr; 7161eba4c79SScott Long 7171eba4c79SScott Long /* Copy in the header block and sanity check it */ 7181eba4c79SScott Long if (uio->uio_resid < sizeof(*hdr)) { 7191eba4c79SScott Long error = EINVAL; 7201eba4c79SScott Long goto out_hdr; 7211eba4c79SScott Long } 7221eba4c79SScott Long error = uiomove(hdr, sizeof(*hdr), uio); 7231eba4c79SScott Long if (error) 7241eba4c79SScott Long goto out_hdr; 7251eba4c79SScott Long 72694fe9f95SAlexander Motin /* XXX: We don't support SG 3.x read/write API. */ 72794fe9f95SAlexander Motin if (hdr->reply_len < 0) { 72894fe9f95SAlexander Motin error = ENODEV; 72994fe9f95SAlexander Motin goto out_hdr; 73094fe9f95SAlexander Motin } 73194fe9f95SAlexander Motin 7328008a935SScott Long ccb = xpt_alloc_ccb(); 7331eba4c79SScott Long if (ccb == NULL) { 7341eba4c79SScott Long error = ENOMEM; 7351eba4c79SScott Long goto out_hdr; 7361eba4c79SScott Long } 7371eba4c79SScott Long csio = &ccb->csio; 7381eba4c79SScott Long 7391eba4c79SScott Long /* 7401eba4c79SScott Long * Copy in the CDB block. The designers of the interface didn't 7411eba4c79SScott Long * bother to provide a size for this in the header, so we have to 7421eba4c79SScott Long * figure it out ourselves. 7431eba4c79SScott Long */ 7441eba4c79SScott Long if (uio->uio_resid < 1) 7451eba4c79SScott Long goto out_ccb; 7461eba4c79SScott Long error = uiomove(&cdb_cmd, 1, uio); 7471eba4c79SScott Long if (error) 7481eba4c79SScott Long goto out_ccb; 7491eba4c79SScott Long if (hdr->twelve_byte) 7501eba4c79SScott Long cdb_len = 12; 7511eba4c79SScott Long else 7521eba4c79SScott Long cdb_len = scsi_group_len(cdb_cmd); 7531eba4c79SScott Long /* 7541eba4c79SScott Long * We've already read the first byte of the CDB and advanced the uio 7551eba4c79SScott Long * pointer. Just read the rest. 7561eba4c79SScott Long */ 7571eba4c79SScott Long csio->cdb_io.cdb_bytes[0] = cdb_cmd; 7581eba4c79SScott Long error = uiomove(&csio->cdb_io.cdb_bytes[1], cdb_len - 1, uio); 7591eba4c79SScott Long if (error) 7601eba4c79SScott Long goto out_ccb; 7611eba4c79SScott Long 7621eba4c79SScott Long /* 7631eba4c79SScott Long * Now set up the data block. Again, the designers didn't bother 7641eba4c79SScott Long * to make this reliable. 7651eba4c79SScott Long */ 7661eba4c79SScott Long buf_len = uio->uio_resid; 7671eba4c79SScott Long if (buf_len != 0) { 7684400b36dSScott Long buf = malloc(buf_len, M_DEVBUF, M_WAITOK | M_ZERO); 7691eba4c79SScott Long error = uiomove(buf, buf_len, uio); 7701eba4c79SScott Long if (error) 7711eba4c79SScott Long goto out_buf; 7721eba4c79SScott Long dir = CAM_DIR_OUT; 7731eba4c79SScott Long } else if (hdr->reply_len != 0) { 7744400b36dSScott Long buf = malloc(hdr->reply_len, M_DEVBUF, M_WAITOK | M_ZERO); 7751eba4c79SScott Long buf_len = hdr->reply_len; 7761eba4c79SScott Long dir = CAM_DIR_IN; 7771eba4c79SScott Long } else { 7781eba4c79SScott Long buf = NULL; 7791eba4c79SScott Long buf_len = 0; 7801eba4c79SScott Long dir = CAM_DIR_NONE; 7811eba4c79SScott Long } 7821eba4c79SScott Long 7832b83592fSScott Long cam_periph_lock(periph); 7842b83592fSScott Long sc = periph->softc; 7851e637ba6SAlexander Motin xpt_setup_ccb(&ccb->ccb_h, periph->path, CAM_PRIORITY_NORMAL); 7861eba4c79SScott Long cam_fill_csio(csio, 7871eba4c79SScott Long /*retries*/1, 7881eba4c79SScott Long sgdone, 7891eba4c79SScott Long dir|CAM_DEV_QFRZDIS, 7901eba4c79SScott Long MSG_SIMPLE_Q_TAG, 7911eba4c79SScott Long buf, 7921eba4c79SScott Long buf_len, 7931eba4c79SScott Long SG_MAX_SENSE, 7941eba4c79SScott Long cdb_len, 795715ab212SScott Long sc->sg_timeout); 7961eba4c79SScott Long 7971eba4c79SScott Long /* 7981eba4c79SScott Long * Send off the command and hope that it works. This path does not 7991eba4c79SScott Long * go through sgstart because the I/O is supposed to be asynchronous. 8001eba4c79SScott Long */ 8011eba4c79SScott Long rdwr->buf = buf; 8021eba4c79SScott Long rdwr->buf_len = buf_len; 8031eba4c79SScott Long rdwr->tag = hdr->pack_id; 8041eba4c79SScott Long rdwr->ccb = ccb; 8051eba4c79SScott Long rdwr->state = SG_RDWR_INPROG; 8061eba4c79SScott Long ccb->ccb_h.ccb_rdwr = rdwr; 8071eba4c79SScott Long ccb->ccb_h.ccb_type = SG_CCB_RDWR_IO; 8081eba4c79SScott Long TAILQ_INSERT_TAIL(&sc->rdwr_done, rdwr, rdwr_link); 8092b83592fSScott Long error = sgsendrdwr(periph, ccb); 8102b83592fSScott Long cam_periph_unlock(periph); 8112b83592fSScott Long return (error); 8121eba4c79SScott Long 8131eba4c79SScott Long out_buf: 8141eba4c79SScott Long free(buf, M_DEVBUF); 8151eba4c79SScott Long out_ccb: 8161eba4c79SScott Long xpt_free_ccb(ccb); 8171eba4c79SScott Long out_hdr: 8181eba4c79SScott Long free(rdwr, M_DEVBUF); 8191eba4c79SScott Long return (error); 8201eba4c79SScott Long } 8211eba4c79SScott Long 8221eba4c79SScott Long static int 8231eba4c79SScott Long sgread(struct cdev *dev, struct uio *uio, int ioflag) 8241eba4c79SScott Long { 8251eba4c79SScott Long struct ccb_scsiio *csio; 8261eba4c79SScott Long struct cam_periph *periph; 8271eba4c79SScott Long struct sg_softc *sc; 8281eba4c79SScott Long struct sg_header *hdr; 8291eba4c79SScott Long struct sg_rdwr *rdwr; 8301eba4c79SScott Long u_short hstat, dstat; 8311eba4c79SScott Long int error, pack_len, reply_len, pack_id; 8321eba4c79SScott Long 8331eba4c79SScott Long periph = dev->si_drv1; 8341eba4c79SScott Long 8351eba4c79SScott Long /* XXX The pack len field needs to be updated and written out instead 8361eba4c79SScott Long * of discarded. Not sure how to do that. 8371eba4c79SScott Long */ 8381eba4c79SScott Long uio->uio_rw = UIO_WRITE; 8391eba4c79SScott Long if ((error = uiomove(&pack_len, 4, uio)) != 0) 8401eba4c79SScott Long return (error); 8411eba4c79SScott Long if ((error = uiomove(&reply_len, 4, uio)) != 0) 8421eba4c79SScott Long return (error); 8431eba4c79SScott Long if ((error = uiomove(&pack_id, 4, uio)) != 0) 8441eba4c79SScott Long return (error); 8451eba4c79SScott Long uio->uio_rw = UIO_READ; 8461eba4c79SScott Long 8472b83592fSScott Long cam_periph_lock(periph); 8482b83592fSScott Long sc = periph->softc; 8491eba4c79SScott Long search: 8501eba4c79SScott Long TAILQ_FOREACH(rdwr, &sc->rdwr_done, rdwr_link) { 8511eba4c79SScott Long if (rdwr->tag == pack_id) 8521eba4c79SScott Long break; 8531eba4c79SScott Long } 8541eba4c79SScott Long if ((rdwr == NULL) || (rdwr->state != SG_RDWR_DONE)) { 855227d67aaSAlexander Motin if (cam_periph_sleep(periph, rdwr, PCATCH, "sgread", 0) == ERESTART) 8561eba4c79SScott Long return (EAGAIN); 8571eba4c79SScott Long goto search; 8581eba4c79SScott Long } 8591eba4c79SScott Long TAILQ_REMOVE(&sc->rdwr_done, rdwr, rdwr_link); 8602b83592fSScott Long cam_periph_unlock(periph); 8611eba4c79SScott Long 8621eba4c79SScott Long hdr = &rdwr->hdr.hdr; 8631eba4c79SScott Long csio = &rdwr->ccb->csio; 8641eba4c79SScott Long sg_scsiio_status(csio, &hstat, &dstat); 8651eba4c79SScott Long hdr->host_status = hstat; 8661eba4c79SScott Long hdr->driver_status = dstat; 8671eba4c79SScott Long hdr->target_status = csio->scsi_status >> 1; 8681eba4c79SScott Long 8691eba4c79SScott Long switch (hstat) { 8701eba4c79SScott Long case DID_OK: 8711eba4c79SScott Long case DID_PASSTHROUGH: 8721eba4c79SScott Long case DID_SOFT_ERROR: 8731eba4c79SScott Long hdr->result = 0; 8741eba4c79SScott Long break; 8751eba4c79SScott Long case DID_NO_CONNECT: 8761eba4c79SScott Long case DID_BUS_BUSY: 8771eba4c79SScott Long case DID_TIME_OUT: 8781eba4c79SScott Long hdr->result = EBUSY; 8791eba4c79SScott Long break; 8801eba4c79SScott Long case DID_BAD_TARGET: 8811eba4c79SScott Long case DID_ABORT: 8821eba4c79SScott Long case DID_PARITY: 8831eba4c79SScott Long case DID_RESET: 8841eba4c79SScott Long case DID_BAD_INTR: 8851eba4c79SScott Long case DID_ERROR: 8861eba4c79SScott Long default: 8871eba4c79SScott Long hdr->result = EIO; 8881eba4c79SScott Long break; 8891eba4c79SScott Long } 8901eba4c79SScott Long 8911eba4c79SScott Long if (dstat == DRIVER_SENSE) { 8921eba4c79SScott Long bcopy(&csio->sense_data, hdr->sense_buffer, 8931eba4c79SScott Long min(csio->sense_len, SG_MAX_SENSE)); 8941eba4c79SScott Long #ifdef CAMDEBUG 8951eba4c79SScott Long scsi_sense_print(csio); 8961eba4c79SScott Long #endif 8971eba4c79SScott Long } 8981eba4c79SScott Long 8991eba4c79SScott Long error = uiomove(&hdr->result, sizeof(*hdr) - 9001eba4c79SScott Long offsetof(struct sg_header, result), uio); 9011eba4c79SScott Long if ((error == 0) && (hdr->result == 0)) 9021eba4c79SScott Long error = uiomove(rdwr->buf, rdwr->buf_len, uio); 9031eba4c79SScott Long 9042b83592fSScott Long cam_periph_lock(periph); 9051eba4c79SScott Long xpt_free_ccb(rdwr->ccb); 9062b83592fSScott Long cam_periph_unlock(periph); 9071eba4c79SScott Long free(rdwr->buf, M_DEVBUF); 9081eba4c79SScott Long free(rdwr, M_DEVBUF); 9091eba4c79SScott Long return (error); 9101eba4c79SScott Long } 9111eba4c79SScott Long 9121eba4c79SScott Long static int 9131eba4c79SScott Long sgsendccb(struct cam_periph *periph, union ccb *ccb) 9141eba4c79SScott Long { 9151eba4c79SScott Long struct sg_softc *softc; 9161eba4c79SScott Long struct cam_periph_map_info mapinfo; 91795fbded6SScott Long int error; 9181eba4c79SScott Long 9191eba4c79SScott Long softc = periph->softc; 9201eba4c79SScott Long bzero(&mapinfo, sizeof(mapinfo)); 9212b83592fSScott Long 9222b83592fSScott Long /* 9232b83592fSScott Long * cam_periph_mapmem calls into proc and vm functions that can 9242b83592fSScott Long * sleep as well as trigger I/O, so we can't hold the lock. 9252b83592fSScott Long * Dropping it here is reasonably safe. 92695fbded6SScott Long * The only CCB opcode that is possible here is XPT_SCSI_IO, no 92795fbded6SScott Long * need for additional checks. 9282b83592fSScott Long */ 9292b83592fSScott Long cam_periph_unlock(periph); 930de239312SAlexander Motin error = cam_periph_mapmem(ccb, &mapinfo, softc->maxio); 9312b83592fSScott Long cam_periph_lock(periph); 9321eba4c79SScott Long if (error) 9331eba4c79SScott Long return (error); 9341eba4c79SScott Long 9351eba4c79SScott Long error = cam_periph_runccb(ccb, 9361eba4c79SScott Long sgerror, 9371eba4c79SScott Long CAM_RETRY_SELTO, 9381eba4c79SScott Long SF_RETRY_UA, 9391eba4c79SScott Long softc->device_stats); 9401eba4c79SScott Long 9418cb46437SAlexander Motin cam_periph_unlock(periph); 9421eba4c79SScott Long cam_periph_unmapmem(ccb, &mapinfo); 9438cb46437SAlexander Motin cam_periph_lock(periph); 9441eba4c79SScott Long 9451eba4c79SScott Long return (error); 9461eba4c79SScott Long } 9471eba4c79SScott Long 9481eba4c79SScott Long static int 9491eba4c79SScott Long sgsendrdwr(struct cam_periph *periph, union ccb *ccb) 9501eba4c79SScott Long { 9511eba4c79SScott Long struct sg_softc *softc; 9521eba4c79SScott Long 9531eba4c79SScott Long softc = periph->softc; 9541eba4c79SScott Long devstat_start_transaction(softc->device_stats, NULL); 9551eba4c79SScott Long xpt_action(ccb); 9561eba4c79SScott Long return (0); 9571eba4c79SScott Long } 9581eba4c79SScott Long 9591eba4c79SScott Long static int 9601eba4c79SScott Long sgerror(union ccb *ccb, uint32_t cam_flags, uint32_t sense_flags) 9611eba4c79SScott Long { 9621eba4c79SScott Long struct cam_periph *periph; 9631eba4c79SScott Long struct sg_softc *softc; 9641eba4c79SScott Long 9651eba4c79SScott Long periph = xpt_path_periph(ccb->ccb_h.path); 9661eba4c79SScott Long softc = (struct sg_softc *)periph->softc; 9671eba4c79SScott Long 968553484aeSWarner Losh return (cam_periph_error(ccb, cam_flags, sense_flags)); 9691eba4c79SScott Long } 9701eba4c79SScott Long 9711eba4c79SScott Long static void 9721eba4c79SScott Long sg_scsiio_status(struct ccb_scsiio *csio, u_short *hoststat, u_short *drvstat) 9731eba4c79SScott Long { 9741eba4c79SScott Long int status; 9751eba4c79SScott Long 9761eba4c79SScott Long status = csio->ccb_h.status; 9771eba4c79SScott Long 9781eba4c79SScott Long switch (status & CAM_STATUS_MASK) { 9791eba4c79SScott Long case CAM_REQ_CMP: 9801eba4c79SScott Long *hoststat = DID_OK; 9811eba4c79SScott Long *drvstat = 0; 9821eba4c79SScott Long break; 9831eba4c79SScott Long case CAM_REQ_CMP_ERR: 9841eba4c79SScott Long *hoststat = DID_ERROR; 9851eba4c79SScott Long *drvstat = 0; 9861eba4c79SScott Long break; 9871eba4c79SScott Long case CAM_REQ_ABORTED: 9881eba4c79SScott Long *hoststat = DID_ABORT; 9891eba4c79SScott Long *drvstat = 0; 9901eba4c79SScott Long break; 9911eba4c79SScott Long case CAM_REQ_INVALID: 9921eba4c79SScott Long *hoststat = DID_ERROR; 9931eba4c79SScott Long *drvstat = DRIVER_INVALID; 9941eba4c79SScott Long break; 9951eba4c79SScott Long case CAM_DEV_NOT_THERE: 9961eba4c79SScott Long *hoststat = DID_BAD_TARGET; 9971eba4c79SScott Long *drvstat = 0; 9984fee613eSEdward Tomasz Napierala break; 9991eba4c79SScott Long case CAM_SEL_TIMEOUT: 10001eba4c79SScott Long *hoststat = DID_NO_CONNECT; 10011eba4c79SScott Long *drvstat = 0; 10021eba4c79SScott Long break; 10031eba4c79SScott Long case CAM_CMD_TIMEOUT: 10041eba4c79SScott Long *hoststat = DID_TIME_OUT; 10051eba4c79SScott Long *drvstat = 0; 10061eba4c79SScott Long break; 10071eba4c79SScott Long case CAM_SCSI_STATUS_ERROR: 10081eba4c79SScott Long *hoststat = DID_ERROR; 10091eba4c79SScott Long *drvstat = 0; 10100c70e307SEdward Tomasz Napierala break; 10111eba4c79SScott Long case CAM_SCSI_BUS_RESET: 10121eba4c79SScott Long *hoststat = DID_RESET; 10131eba4c79SScott Long *drvstat = 0; 10141eba4c79SScott Long break; 10151eba4c79SScott Long case CAM_UNCOR_PARITY: 10161eba4c79SScott Long *hoststat = DID_PARITY; 10171eba4c79SScott Long *drvstat = 0; 10181eba4c79SScott Long break; 10191eba4c79SScott Long case CAM_SCSI_BUSY: 10201eba4c79SScott Long *hoststat = DID_BUS_BUSY; 10211eba4c79SScott Long *drvstat = 0; 10220c70e307SEdward Tomasz Napierala break; 10231eba4c79SScott Long default: 10241eba4c79SScott Long *hoststat = DID_ERROR; 10251eba4c79SScott Long *drvstat = DRIVER_ERROR; 10261eba4c79SScott Long } 10271eba4c79SScott Long 10281eba4c79SScott Long if (status & CAM_AUTOSNS_VALID) 10291eba4c79SScott Long *drvstat = DRIVER_SENSE; 10301eba4c79SScott Long } 10311eba4c79SScott Long 10321eba4c79SScott Long static int 10331eba4c79SScott Long scsi_group_len(u_char cmd) 10341eba4c79SScott Long { 10351eba4c79SScott Long int len[] = {6, 10, 10, 12, 12, 12, 10, 10}; 10361eba4c79SScott Long int group; 10371eba4c79SScott Long 10381eba4c79SScott Long group = (cmd >> 5) & 0x7; 10391eba4c79SScott Long return (len[group]); 10401eba4c79SScott Long } 10411eba4c79SScott Long 1042