11eba4c79SScott Long /*- 21eba4c79SScott Long * Copyright (c) 2007 Scott Long 31eba4c79SScott Long * All rights reserved. 41eba4c79SScott Long * 51eba4c79SScott Long * Redistribution and use in source and binary forms, with or without 61eba4c79SScott Long * modification, are permitted provided that the following conditions 71eba4c79SScott Long * are met: 81eba4c79SScott Long * 1. Redistributions of source code must retain the above copyright 91eba4c79SScott Long * notice, this list of conditions, and the following disclaimer, 101eba4c79SScott Long * without modification, immediately at the beginning of the file. 111eba4c79SScott Long * 2. The name of the author may not be used to endorse or promote products 121eba4c79SScott Long * derived from this software without specific prior written permission. 131eba4c79SScott Long * 141eba4c79SScott Long * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 151eba4c79SScott Long * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 161eba4c79SScott Long * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 171eba4c79SScott Long * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 181eba4c79SScott Long * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 191eba4c79SScott Long * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 201eba4c79SScott Long * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 211eba4c79SScott Long * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 221eba4c79SScott Long * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231eba4c79SScott Long * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241eba4c79SScott Long * SUCH DAMAGE. 251eba4c79SScott Long */ 261eba4c79SScott Long 271eba4c79SScott Long /* 281eba4c79SScott Long * scsi_sg peripheral driver. This driver is meant to implement the Linux 291eba4c79SScott Long * SG passthrough interface for SCSI. 301eba4c79SScott Long */ 311eba4c79SScott Long 321eba4c79SScott Long #include <sys/cdefs.h> 331eba4c79SScott Long __FBSDID("$FreeBSD$"); 341eba4c79SScott Long 351eba4c79SScott Long #include <sys/param.h> 361eba4c79SScott Long #include <sys/systm.h> 371eba4c79SScott Long #include <sys/kernel.h> 381eba4c79SScott Long #include <sys/types.h> 391eba4c79SScott Long #include <sys/bio.h> 401eba4c79SScott Long #include <sys/malloc.h> 411eba4c79SScott Long #include <sys/fcntl.h> 421eba4c79SScott Long #include <sys/ioccom.h> 431eba4c79SScott Long #include <sys/conf.h> 441eba4c79SScott Long #include <sys/errno.h> 451eba4c79SScott Long #include <sys/devicestat.h> 461eba4c79SScott Long #include <sys/proc.h> 471eba4c79SScott Long #include <sys/uio.h> 481eba4c79SScott Long 491eba4c79SScott Long #include <cam/cam.h> 501eba4c79SScott Long #include <cam/cam_ccb.h> 511eba4c79SScott Long #include <cam/cam_periph.h> 521eba4c79SScott Long #include <cam/cam_queue.h> 531eba4c79SScott Long #include <cam/cam_xpt_periph.h> 541eba4c79SScott Long #include <cam/cam_debug.h> 551eba4c79SScott Long #include <cam/cam_sim.h> 561eba4c79SScott Long 571eba4c79SScott Long #include <cam/scsi/scsi_all.h> 581eba4c79SScott Long #include <cam/scsi/scsi_message.h> 591eba4c79SScott Long #include <cam/scsi/scsi_sg.h> 601eba4c79SScott Long 611eba4c79SScott Long #include <compat/linux/linux_ioctl.h> 621eba4c79SScott Long 631eba4c79SScott Long typedef enum { 641eba4c79SScott Long SG_FLAG_OPEN = 0x01, 651eba4c79SScott Long SG_FLAG_LOCKED = 0x02, 661eba4c79SScott Long SG_FLAG_INVALID = 0x04 671eba4c79SScott Long } sg_flags; 681eba4c79SScott Long 691eba4c79SScott Long typedef enum { 701eba4c79SScott Long SG_STATE_NORMAL 711eba4c79SScott Long } sg_state; 721eba4c79SScott Long 731eba4c79SScott Long typedef enum { 74472cdbefSScott Long SG_RDWR_FREE, 751eba4c79SScott Long SG_RDWR_INPROG, 761eba4c79SScott Long SG_RDWR_DONE 771eba4c79SScott Long } sg_rdwr_state; 781eba4c79SScott Long 791eba4c79SScott Long typedef enum { 801eba4c79SScott Long SG_CCB_RDWR_IO, 811eba4c79SScott Long SG_CCB_WAITING 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; 1031eba4c79SScott Long uint8_t pd_type; 1041eba4c79SScott Long struct devstat *device_stats; 1051eba4c79SScott Long TAILQ_HEAD(, sg_rdwr) rdwr_done; 1061eba4c79SScott Long struct cdev *dev; 1071eba4c79SScott Long struct cdev *devalias; 1081eba4c79SScott Long union ccb saved_ccb; 1091eba4c79SScott Long }; 1101eba4c79SScott Long 1111eba4c79SScott Long static d_open_t sgopen; 1121eba4c79SScott Long static d_close_t sgclose; 1131eba4c79SScott Long static d_ioctl_t sgioctl; 1141eba4c79SScott Long static d_write_t sgwrite; 1151eba4c79SScott Long static d_read_t sgread; 1161eba4c79SScott Long 1171eba4c79SScott Long static periph_init_t sginit; 1181eba4c79SScott Long static periph_ctor_t sgregister; 1191eba4c79SScott Long static periph_oninv_t sgoninvalidate; 1201eba4c79SScott Long static periph_dtor_t sgcleanup; 1211eba4c79SScott Long static periph_start_t sgstart; 1221eba4c79SScott Long static void sgasync(void *callback_arg, uint32_t code, 1231eba4c79SScott Long struct cam_path *path, void *arg); 1241eba4c79SScott Long static void sgdone(struct cam_periph *periph, union ccb *done_ccb); 1251eba4c79SScott Long static int sgsendccb(struct cam_periph *periph, union ccb *ccb); 1261eba4c79SScott Long static int sgsendrdwr(struct cam_periph *periph, union ccb *ccb); 1271eba4c79SScott Long static int sgerror(union ccb *ccb, uint32_t cam_flags, 1281eba4c79SScott Long uint32_t sense_flags); 1291eba4c79SScott Long static void sg_scsiio_status(struct ccb_scsiio *csio, 1301eba4c79SScott Long u_short *hoststat, u_short *drvstat); 1311eba4c79SScott Long 1321eba4c79SScott Long static int scsi_group_len(u_char cmd); 1331eba4c79SScott Long 1341eba4c79SScott Long static struct periph_driver sgdriver = 1351eba4c79SScott Long { 1361eba4c79SScott Long sginit, "sg", 1371eba4c79SScott Long TAILQ_HEAD_INITIALIZER(sgdriver.units), /* gen */ 0 1381eba4c79SScott Long }; 1391eba4c79SScott Long PERIPHDRIVER_DECLARE(sg, sgdriver); 1401eba4c79SScott Long 1411eba4c79SScott Long static struct cdevsw sg_cdevsw = { 1421eba4c79SScott Long .d_version = D_VERSION, 1431eba4c79SScott Long .d_flags = D_NEEDGIANT, 1441eba4c79SScott Long .d_open = sgopen, 1451eba4c79SScott Long .d_close = sgclose, 1461eba4c79SScott Long .d_ioctl = sgioctl, 1471eba4c79SScott Long .d_write = sgwrite, 1481eba4c79SScott Long .d_read = sgread, 1491eba4c79SScott Long .d_name = "sg", 1501eba4c79SScott Long }; 1511eba4c79SScott Long 1521eba4c79SScott Long static int sg_version = 30125; 1531eba4c79SScott Long 1541eba4c79SScott Long static void 1551eba4c79SScott Long sginit(void) 1561eba4c79SScott Long { 1571eba4c79SScott Long cam_status status; 1581eba4c79SScott Long struct cam_path *path; 1591eba4c79SScott Long 1601eba4c79SScott Long /* 1611eba4c79SScott Long * Install a global async callback. This callback will receive aync 1621eba4c79SScott Long * callbacks like "new device found". 1631eba4c79SScott Long */ 1641eba4c79SScott Long status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID, 1651eba4c79SScott Long CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); 1661eba4c79SScott Long 1671eba4c79SScott Long if (status == CAM_REQ_CMP) { 1681eba4c79SScott Long struct ccb_setasync csa; 1691eba4c79SScott Long 1701eba4c79SScott Long xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5); 1711eba4c79SScott Long csa.ccb_h.func_code = XPT_SASYNC_CB; 1721eba4c79SScott Long csa.event_enable = AC_FOUND_DEVICE; 1731eba4c79SScott Long csa.callback = sgasync; 1741eba4c79SScott Long csa.callback_arg = NULL; 1751eba4c79SScott Long xpt_action((union ccb *)&csa); 1761eba4c79SScott Long status = csa.ccb_h.status; 1771eba4c79SScott Long xpt_free_path(path); 1781eba4c79SScott Long } 1791eba4c79SScott Long 1801eba4c79SScott Long if (status != CAM_REQ_CMP) { 1811eba4c79SScott Long printf("sg: Failed to attach master async callbac " 1821eba4c79SScott Long "due to status 0x%x!\n", status); 1831eba4c79SScott Long } 1841eba4c79SScott Long } 1851eba4c79SScott Long 1861eba4c79SScott Long static void 1871eba4c79SScott Long sgoninvalidate(struct cam_periph *periph) 1881eba4c79SScott Long { 1891eba4c79SScott Long struct sg_softc *softc; 1901eba4c79SScott Long struct ccb_setasync csa; 1911eba4c79SScott Long 1921eba4c79SScott Long softc = (struct sg_softc *)periph->softc; 1931eba4c79SScott Long 1941eba4c79SScott Long /* 1951eba4c79SScott Long * Deregister any async callbacks. 1961eba4c79SScott Long */ 1971eba4c79SScott Long xpt_setup_ccb(&csa.ccb_h, periph->path, /*priority*/5); 1981eba4c79SScott Long csa.ccb_h.func_code = XPT_SASYNC_CB; 1991eba4c79SScott Long csa.event_enable = 0; 2001eba4c79SScott Long csa.callback = sgasync; 2011eba4c79SScott Long csa.callback_arg = periph; 2021eba4c79SScott Long xpt_action((union ccb *)&csa); 2031eba4c79SScott Long 2041eba4c79SScott Long softc->flags |= SG_FLAG_INVALID; 2051eba4c79SScott Long 2061eba4c79SScott Long /* 2071eba4c79SScott Long * XXX Return all queued I/O with ENXIO. 2081eba4c79SScott Long * XXX Handle any transactions queued to the card 2091eba4c79SScott Long * with XPT_ABORT_CCB. 2101eba4c79SScott Long */ 2111eba4c79SScott Long 2121eba4c79SScott Long if (bootverbose) { 2131eba4c79SScott Long xpt_print(periph->path, "lost device\n"); 2141eba4c79SScott Long } 2151eba4c79SScott Long } 2161eba4c79SScott Long 2171eba4c79SScott Long static void 2181eba4c79SScott Long sgcleanup(struct cam_periph *periph) 2191eba4c79SScott Long { 2201eba4c79SScott Long struct sg_softc *softc; 2211eba4c79SScott Long 2221eba4c79SScott Long softc = (struct sg_softc *)periph->softc; 2231eba4c79SScott Long devstat_remove_entry(softc->device_stats); 2241eba4c79SScott Long destroy_dev(softc->dev); 2251eba4c79SScott Long destroy_dev(softc->devalias); 2261eba4c79SScott Long if (bootverbose) { 2271eba4c79SScott Long xpt_print(periph->path, "removing device entry\n"); 2281eba4c79SScott Long } 2291eba4c79SScott Long free(softc, M_DEVBUF); 2301eba4c79SScott Long } 2311eba4c79SScott Long 2321eba4c79SScott Long static void 2331eba4c79SScott Long sgasync(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) 2341eba4c79SScott Long { 2351eba4c79SScott Long struct cam_periph *periph; 2361eba4c79SScott Long 2371eba4c79SScott Long periph = (struct cam_periph *)callback_arg; 2381eba4c79SScott Long 2391eba4c79SScott Long switch (code) { 2401eba4c79SScott Long case AC_FOUND_DEVICE: 2411eba4c79SScott Long { 2421eba4c79SScott Long struct ccb_getdev *cgd; 2431eba4c79SScott Long cam_status status; 2441eba4c79SScott Long 2451eba4c79SScott Long cgd = (struct ccb_getdev *)arg; 2461eba4c79SScott Long if (cgd == NULL) 2471eba4c79SScott Long break; 2481eba4c79SScott Long 2491eba4c79SScott Long /* 2501eba4c79SScott Long * Allocate a peripheral instance for this device and 2511eba4c79SScott Long * start the probe process. 2521eba4c79SScott Long */ 2531eba4c79SScott Long status = cam_periph_alloc(sgregister, sgoninvalidate, 2541eba4c79SScott Long sgcleanup, sgstart, "sg", 2551eba4c79SScott Long CAM_PERIPH_BIO, cgd->ccb_h.path, 2561eba4c79SScott Long sgasync, AC_FOUND_DEVICE, cgd); 2571eba4c79SScott Long if ((status != CAM_REQ_CMP) && (status != CAM_REQ_INPROG)) { 2581eba4c79SScott Long const struct cam_status_entry *entry; 2591eba4c79SScott Long 2601eba4c79SScott Long entry = cam_fetch_status_entry(status); 2611eba4c79SScott Long printf("sgasync: Unable to attach new device " 2621eba4c79SScott Long "due to status %#x: %s\n", status, entry ? 2631eba4c79SScott Long entry->status_text : "Unknown"); 2641eba4c79SScott Long } 2651eba4c79SScott Long break; 2661eba4c79SScott Long } 2671eba4c79SScott Long default: 2681eba4c79SScott Long cam_periph_async(periph, code, path, arg); 2691eba4c79SScott Long break; 2701eba4c79SScott Long } 2711eba4c79SScott Long } 2721eba4c79SScott Long 2731eba4c79SScott Long static cam_status 2741eba4c79SScott Long sgregister(struct cam_periph *periph, void *arg) 2751eba4c79SScott Long { 2761eba4c79SScott Long struct sg_softc *softc; 2771eba4c79SScott Long struct ccb_setasync csa; 2781eba4c79SScott Long struct ccb_getdev *cgd; 2791eba4c79SScott Long int no_tags; 2801eba4c79SScott Long 2811eba4c79SScott Long cgd = (struct ccb_getdev *)arg; 2821eba4c79SScott Long if (periph == NULL) { 2831eba4c79SScott Long printf("sgregister: periph was NULL!!\n"); 2841eba4c79SScott Long return (CAM_REQ_CMP_ERR); 2851eba4c79SScott Long } 2861eba4c79SScott Long 2871eba4c79SScott Long if (cgd == NULL) { 2881eba4c79SScott Long printf("sgregister: no getdev CCB, can't register device\n"); 2891eba4c79SScott Long return (CAM_REQ_CMP_ERR); 2901eba4c79SScott Long } 2911eba4c79SScott Long 2924400b36dSScott Long softc = malloc(sizeof(*softc), M_DEVBUF, M_ZERO | M_NOWAIT); 2931eba4c79SScott Long if (softc == NULL) { 2941eba4c79SScott Long printf("sgregister: Unable to allocate softc\n"); 2951eba4c79SScott Long return (CAM_REQ_CMP_ERR); 2961eba4c79SScott Long } 2971eba4c79SScott Long 2981eba4c79SScott Long softc->state = SG_STATE_NORMAL; 2991eba4c79SScott Long softc->pd_type = SID_TYPE(&cgd->inq_data); 3001eba4c79SScott Long TAILQ_INIT(&softc->rdwr_done); 3011eba4c79SScott Long periph->softc = softc; 3021eba4c79SScott Long 3031eba4c79SScott Long /* 3041eba4c79SScott Long * We pass in 0 for all blocksize, since we don't know what the 3051eba4c79SScott Long * blocksize of the device is, if it even has a blocksize. 3061eba4c79SScott Long */ 3071eba4c79SScott Long no_tags = (cgd->inq_data.flags & SID_CmdQue) == 0; 3081eba4c79SScott Long softc->device_stats = devstat_new_entry("sg", 3091eba4c79SScott Long unit2minor(periph->unit_number), 0, 3101eba4c79SScott Long DEVSTAT_NO_BLOCKSIZE 3111eba4c79SScott Long | (no_tags ? DEVSTAT_NO_ORDERED_TAGS : 0), 3121eba4c79SScott Long softc->pd_type | 3131eba4c79SScott Long DEVSTAT_TYPE_IF_SCSI | 3141eba4c79SScott Long DEVSTAT_TYPE_PASS, 3151eba4c79SScott Long DEVSTAT_PRIORITY_PASS); 3161eba4c79SScott Long 3171eba4c79SScott Long /* Register the device */ 3181eba4c79SScott Long softc->dev = make_dev(&sg_cdevsw, unit2minor(periph->unit_number), 3191eba4c79SScott Long UID_ROOT, GID_OPERATOR, 0600, "%s%d", 3201eba4c79SScott Long periph->periph_name, periph->unit_number); 3211eba4c79SScott Long softc->devalias = make_dev_alias(softc->dev, "sg%c", 3221eba4c79SScott Long 'a' + periph->unit_number); 3231eba4c79SScott Long softc->dev->si_drv1 = periph; 3241eba4c79SScott Long 3251eba4c79SScott Long /* 3261eba4c79SScott Long * Add as async callback so that we get 3271eba4c79SScott Long * notified if this device goes away. 3281eba4c79SScott Long */ 3291eba4c79SScott Long xpt_setup_ccb(&csa.ccb_h, periph->path, /*priority*/5); 3301eba4c79SScott Long csa.ccb_h.func_code = XPT_SASYNC_CB; 3311eba4c79SScott Long csa.event_enable = AC_LOST_DEVICE; 3321eba4c79SScott Long csa.callback = sgasync; 3331eba4c79SScott Long csa.callback_arg = periph; 3341eba4c79SScott Long xpt_action((union ccb *)&csa); 3351eba4c79SScott Long 3361eba4c79SScott Long if (bootverbose) 3371eba4c79SScott Long xpt_announce_periph(periph, NULL); 3381eba4c79SScott Long 3391eba4c79SScott Long return (CAM_REQ_CMP); 3401eba4c79SScott Long } 3411eba4c79SScott Long 3421eba4c79SScott Long static void 3431eba4c79SScott Long sgstart(struct cam_periph *periph, union ccb *start_ccb) 3441eba4c79SScott Long { 3451eba4c79SScott Long struct sg_softc *softc; 3461eba4c79SScott Long 3471eba4c79SScott Long softc = (struct sg_softc *)periph->softc; 3481eba4c79SScott Long 3491eba4c79SScott Long switch (softc->state) { 3501eba4c79SScott Long case SG_STATE_NORMAL: 3511eba4c79SScott Long start_ccb->ccb_h.ccb_type = SG_CCB_WAITING; 3521eba4c79SScott Long SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h, 3531eba4c79SScott Long periph_links.sle); 3541eba4c79SScott Long periph->immediate_priority = CAM_PRIORITY_NONE; 3551eba4c79SScott Long wakeup(&periph->ccb_list); 3561eba4c79SScott Long break; 3571eba4c79SScott Long } 3581eba4c79SScott Long } 3591eba4c79SScott Long 3601eba4c79SScott Long static void 3611eba4c79SScott Long sgdone(struct cam_periph *periph, union ccb *done_ccb) 3621eba4c79SScott Long { 3631eba4c79SScott Long struct sg_softc *softc; 3641eba4c79SScott Long struct ccb_scsiio *csio; 3651eba4c79SScott Long 3661eba4c79SScott Long softc = (struct sg_softc *)periph->softc; 3671eba4c79SScott Long csio = &done_ccb->csio; 3681eba4c79SScott Long switch (csio->ccb_h.ccb_type) { 3691eba4c79SScott Long case SG_CCB_WAITING: 3701eba4c79SScott Long /* Caller will release the CCB */ 3711eba4c79SScott Long wakeup(&done_ccb->ccb_h.cbfcnp); 3721eba4c79SScott Long return; 3731eba4c79SScott Long case SG_CCB_RDWR_IO: 3741eba4c79SScott Long { 3751eba4c79SScott Long struct sg_rdwr *rdwr; 3761eba4c79SScott Long int state; 3771eba4c79SScott Long 3781eba4c79SScott Long devstat_end_transaction(softc->device_stats, 3791eba4c79SScott Long csio->dxfer_len, 3801eba4c79SScott Long csio->tag_action & 0xf, 3811eba4c79SScott Long ((csio->ccb_h.flags & CAM_DIR_MASK) == 3821eba4c79SScott Long CAM_DIR_NONE) ? DEVSTAT_NO_DATA : 3831eba4c79SScott Long (csio->ccb_h.flags & CAM_DIR_OUT) ? 3841eba4c79SScott Long DEVSTAT_WRITE : DEVSTAT_READ, 3851eba4c79SScott Long NULL, NULL); 3861eba4c79SScott Long 3871eba4c79SScott Long rdwr = done_ccb->ccb_h.ccb_rdwr; 3881eba4c79SScott Long state = rdwr->state; 3891eba4c79SScott Long rdwr->state = SG_RDWR_DONE; 3901eba4c79SScott Long wakeup(rdwr); 3911eba4c79SScott Long break; 3921eba4c79SScott Long } 3931eba4c79SScott Long default: 3941eba4c79SScott Long panic("unknown sg CCB type"); 3951eba4c79SScott Long } 3961eba4c79SScott Long } 3971eba4c79SScott Long 3981eba4c79SScott Long static int 3991eba4c79SScott Long sgopen(struct cdev *dev, int flags, int fmt, struct thread *td) 4001eba4c79SScott Long { 4011eba4c79SScott Long struct cam_periph *periph; 4021eba4c79SScott Long struct sg_softc *softc; 4031eba4c79SScott Long int error = 0; 4041eba4c79SScott Long 4051eba4c79SScott Long periph = (struct cam_periph *)dev->si_drv1; 4061eba4c79SScott Long if (periph == NULL) 4071eba4c79SScott Long return (ENXIO); 4081eba4c79SScott Long 4091eba4c79SScott Long softc = (struct sg_softc *)periph->softc; 4101eba4c79SScott Long if (softc->flags & SG_FLAG_INVALID) 4111eba4c79SScott Long return (ENXIO); 4121eba4c79SScott Long 4131eba4c79SScott Long /* 4141eba4c79SScott Long * Don't allow access when we're running at a high securelevel. 4151eba4c79SScott Long */ 4161eba4c79SScott Long error = securelevel_gt(td->td_ucred, 1); 4171eba4c79SScott Long if (error) 4181eba4c79SScott Long return (error); 4191eba4c79SScott Long 4201eba4c79SScott Long if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0) 4211eba4c79SScott Long return (error); 4221eba4c79SScott Long 4231eba4c79SScott Long if ((softc->flags & SG_FLAG_OPEN) == 0) { 4241eba4c79SScott Long if (cam_periph_acquire(periph) != CAM_REQ_CMP) 4251eba4c79SScott Long return (ENXIO); 4261eba4c79SScott Long softc->flags |= SG_FLAG_OPEN; 4271eba4c79SScott Long } 4281eba4c79SScott Long 4291eba4c79SScott Long cam_periph_unlock(periph); 4301eba4c79SScott Long 4311eba4c79SScott Long return (error); 4321eba4c79SScott Long } 4331eba4c79SScott Long 4341eba4c79SScott Long static int 4351eba4c79SScott Long sgclose(struct cdev *dev, int flag, int fmt, struct thread *td) 4361eba4c79SScott Long { 4371eba4c79SScott Long struct cam_periph *periph; 4381eba4c79SScott Long struct sg_softc *softc; 4391eba4c79SScott Long int error; 4401eba4c79SScott Long 4411eba4c79SScott Long periph = (struct cam_periph *)dev->si_drv1; 4421eba4c79SScott Long if (periph == NULL) 4431eba4c79SScott Long return (ENXIO); 4441eba4c79SScott Long 4451eba4c79SScott Long softc = (struct sg_softc *)periph->softc; 4461eba4c79SScott Long 4471eba4c79SScott Long if ((error = cam_periph_lock(periph, PRIBIO)) != 0) 4481eba4c79SScott Long return (error); 4491eba4c79SScott Long 4501eba4c79SScott Long softc->flags &= ~SG_FLAG_OPEN; 4511eba4c79SScott Long 4521eba4c79SScott Long cam_periph_unlock(periph); 4531eba4c79SScott Long cam_periph_release(periph); 4541eba4c79SScott Long 4551eba4c79SScott Long return (0); 4561eba4c79SScott Long } 4571eba4c79SScott Long 4581eba4c79SScott Long static int 4591eba4c79SScott Long sgioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) 4601eba4c79SScott Long { 4611eba4c79SScott Long union ccb *ccb; 4621eba4c79SScott Long struct ccb_scsiio *csio; 4631eba4c79SScott Long struct cam_periph *periph; 4641eba4c79SScott Long struct sg_softc *softc; 4651eba4c79SScott Long struct sg_io_hdr req; 4661eba4c79SScott Long int dir, error; 4671eba4c79SScott Long 4681eba4c79SScott Long periph = (struct cam_periph *)dev->si_drv1; 4691eba4c79SScott Long if (periph == NULL) 4701eba4c79SScott Long return (ENXIO); 4711eba4c79SScott Long 4721eba4c79SScott Long softc = (struct sg_softc *)periph->softc; 4731eba4c79SScott Long error = 0; 4741eba4c79SScott Long 4751eba4c79SScott Long switch (cmd) { 4761eba4c79SScott Long case LINUX_SCSI_GET_BUS_NUMBER: { 4771eba4c79SScott Long int busno; 4781eba4c79SScott Long 4791eba4c79SScott Long busno = xpt_path_path_id(periph->path); 4801eba4c79SScott Long error = copyout(&busno, arg, sizeof(busno)); 4811eba4c79SScott Long break; 4821eba4c79SScott Long } 4831eba4c79SScott Long case LINUX_SCSI_GET_IDLUN: { 4841eba4c79SScott Long struct scsi_idlun idlun; 4851eba4c79SScott Long struct cam_sim *sim; 4861eba4c79SScott Long 4871eba4c79SScott Long idlun.dev_id = xpt_path_target_id(periph->path); 4881eba4c79SScott Long sim = xpt_path_sim(periph->path); 4891eba4c79SScott Long idlun.host_unique_id = sim->unit_number; 4901eba4c79SScott Long error = copyout(&idlun, arg, sizeof(idlun)); 4911eba4c79SScott Long break; 4921eba4c79SScott Long } 4931eba4c79SScott Long case SG_GET_VERSION_NUM: 4941eba4c79SScott Long case LINUX_SG_GET_VERSION_NUM: 4951eba4c79SScott Long error = copyout(&sg_version, arg, sizeof(sg_version)); 4961eba4c79SScott Long break; 4971eba4c79SScott Long case SG_SET_TIMEOUT: 4981eba4c79SScott Long case LINUX_SG_SET_TIMEOUT: 4991eba4c79SScott Long break; 5001eba4c79SScott Long case SG_GET_TIMEOUT: 5011eba4c79SScott Long case LINUX_SG_GET_TIMEOUT: 5021eba4c79SScott Long /* 5031eba4c79SScott Long * XXX This ioctl is highly brain damaged because it requires 5041eba4c79SScott Long * that the value be returned in the syscall return value. 5051eba4c79SScott Long * The linuxolator seems to have a hard time with this, 5061eba4c79SScott Long * so just return 0 and hope that apps can cope. 5071eba4c79SScott Long */ 508472cdbefSScott Long td->td_retval[0] = 60*hz; 5091eba4c79SScott Long error = 0; 5101eba4c79SScott Long break; 5111eba4c79SScott Long case SG_IO: 5121eba4c79SScott Long case LINUX_SG_IO: 5131eba4c79SScott Long error = copyin(arg, &req, sizeof(req)); 5141eba4c79SScott Long if (error) 5151eba4c79SScott Long break; 5161eba4c79SScott Long 5171eba4c79SScott Long if (req.cmd_len > IOCDBLEN) { 5181eba4c79SScott Long error = EINVAL; 5191eba4c79SScott Long break; 5201eba4c79SScott Long } 5211eba4c79SScott Long 5221eba4c79SScott Long if (req.iovec_count != 0) { 5231eba4c79SScott Long error = EOPNOTSUPP; 5241eba4c79SScott Long break; 5251eba4c79SScott Long } 5261eba4c79SScott Long 5271eba4c79SScott Long ccb = cam_periph_getccb(periph, /*priority*/5); 5281eba4c79SScott Long csio = &ccb->csio; 5291eba4c79SScott Long 5301eba4c79SScott Long error = copyin(req.cmdp, &csio->cdb_io.cdb_bytes, 5311eba4c79SScott Long req.cmd_len); 5321eba4c79SScott Long if (error) { 5331eba4c79SScott Long xpt_release_ccb(ccb); 5341eba4c79SScott Long break; 5351eba4c79SScott Long } 5361eba4c79SScott Long 5371eba4c79SScott Long switch(req.dxfer_direction) { 5381eba4c79SScott Long case SG_DXFER_TO_DEV: 5391eba4c79SScott Long dir = CAM_DIR_OUT; 5401eba4c79SScott Long break; 5411eba4c79SScott Long case SG_DXFER_FROM_DEV: 5421eba4c79SScott Long dir = CAM_DIR_IN; 5431eba4c79SScott Long break; 5441eba4c79SScott Long case SG_DXFER_TO_FROM_DEV: 5451eba4c79SScott Long dir = CAM_DIR_IN | CAM_DIR_OUT; 5461eba4c79SScott Long break; 5471eba4c79SScott Long case SG_DXFER_NONE: 5481eba4c79SScott Long default: 5491eba4c79SScott Long dir = CAM_DIR_NONE; 5501eba4c79SScott Long break; 5511eba4c79SScott Long } 5521eba4c79SScott Long 5531eba4c79SScott Long cam_fill_csio(csio, 5541eba4c79SScott Long /*retries*/1, 5551eba4c79SScott Long sgdone, 5561eba4c79SScott Long dir|CAM_DEV_QFRZDIS, 5571eba4c79SScott Long MSG_SIMPLE_Q_TAG, 5581eba4c79SScott Long req.dxferp, 5591eba4c79SScott Long req.dxfer_len, 5601eba4c79SScott Long req.mx_sb_len, 5611eba4c79SScott Long req.cmd_len, 5621eba4c79SScott Long req.timeout); 5631eba4c79SScott Long 5641eba4c79SScott Long error = sgsendccb(periph, ccb); 5651eba4c79SScott Long if (error) { 5661eba4c79SScott Long req.host_status = DID_ERROR; 5671eba4c79SScott Long req.driver_status = DRIVER_INVALID; 5681eba4c79SScott Long xpt_release_ccb(ccb); 5691eba4c79SScott Long break; 5701eba4c79SScott Long } 5711eba4c79SScott Long 5721eba4c79SScott Long req.status = csio->scsi_status; 5731eba4c79SScott Long req.masked_status = (csio->scsi_status >> 1) & 0x7f; 5741eba4c79SScott Long sg_scsiio_status(csio, &req.host_status, &req.driver_status); 5751eba4c79SScott Long req.resid = csio->resid; 5761eba4c79SScott Long req.duration = csio->ccb_h.timeout; 5771eba4c79SScott Long req.info = 0; 5781eba4c79SScott Long 5791eba4c79SScott Long error = copyout(&req, arg, sizeof(req)); 5801eba4c79SScott Long if ((error == 0) && (csio->ccb_h.status & CAM_AUTOSNS_VALID) 5811eba4c79SScott Long && (req.sbp != NULL)) { 5821eba4c79SScott Long req.sb_len_wr = req.mx_sb_len - csio->sense_resid; 5831eba4c79SScott Long error = copyout(&csio->sense_data, req.sbp, 5841eba4c79SScott Long req.sb_len_wr); 5851eba4c79SScott Long } 5861eba4c79SScott Long 5871eba4c79SScott Long xpt_release_ccb(ccb); 5881eba4c79SScott Long break; 5891eba4c79SScott Long 5901eba4c79SScott Long case SG_GET_RESERVED_SIZE: 5911eba4c79SScott Long case LINUX_SG_GET_RESERVED_SIZE: { 5921eba4c79SScott Long int size = 32768; 5931eba4c79SScott Long 5941eba4c79SScott Long error = copyout(&size, arg, sizeof(size)); 5951eba4c79SScott Long break; 5961eba4c79SScott Long } 5971eba4c79SScott Long 5981eba4c79SScott Long case SG_GET_SCSI_ID: 5991eba4c79SScott Long case LINUX_SG_GET_SCSI_ID: 6001eba4c79SScott Long { 6011eba4c79SScott Long struct sg_scsi_id id; 6021eba4c79SScott Long 6031eba4c79SScott Long id.host_no = 0; /* XXX */ 6041eba4c79SScott Long id.channel = xpt_path_path_id(periph->path); 6051eba4c79SScott Long id.scsi_id = xpt_path_target_id(periph->path); 6061eba4c79SScott Long id.lun = xpt_path_lun_id(periph->path); 6071eba4c79SScott Long id.scsi_type = softc->pd_type; 6081eba4c79SScott Long id.h_cmd_per_lun = 1; 6091eba4c79SScott Long id.d_queue_depth = 1; 6101eba4c79SScott Long id.unused[0] = 0; 6111eba4c79SScott Long id.unused[1] = 0; 6121eba4c79SScott Long 6131eba4c79SScott Long error = copyout(&id, arg, sizeof(id)); 6141eba4c79SScott Long break; 6151eba4c79SScott Long } 6161eba4c79SScott Long 6171eba4c79SScott Long case SG_EMULATED_HOST: 6181eba4c79SScott Long case SG_SET_TRANSFORM: 6191eba4c79SScott Long case SG_GET_TRANSFORM: 6201eba4c79SScott Long case SG_GET_NUM_WAITING: 6211eba4c79SScott Long case SG_SCSI_RESET: 6221eba4c79SScott Long case SG_GET_REQUEST_TABLE: 6231eba4c79SScott Long case SG_SET_KEEP_ORPHAN: 6241eba4c79SScott Long case SG_GET_KEEP_ORPHAN: 6251eba4c79SScott Long case SG_GET_ACCESS_COUNT: 6261eba4c79SScott Long case SG_SET_FORCE_LOW_DMA: 6271eba4c79SScott Long case SG_GET_LOW_DMA: 6281eba4c79SScott Long case SG_GET_SG_TABLESIZE: 6291eba4c79SScott Long case SG_SET_FORCE_PACK_ID: 6301eba4c79SScott Long case SG_GET_PACK_ID: 6311eba4c79SScott Long case SG_SET_RESERVED_SIZE: 6321eba4c79SScott Long case SG_GET_COMMAND_Q: 6331eba4c79SScott Long case SG_SET_COMMAND_Q: 6341eba4c79SScott Long case SG_SET_DEBUG: 6351eba4c79SScott Long case SG_NEXT_CMD_LEN: 6361eba4c79SScott Long case LINUX_SG_EMULATED_HOST: 6371eba4c79SScott Long case LINUX_SG_SET_TRANSFORM: 6381eba4c79SScott Long case LINUX_SG_GET_TRANSFORM: 6391eba4c79SScott Long case LINUX_SG_GET_NUM_WAITING: 6401eba4c79SScott Long case LINUX_SG_SCSI_RESET: 6411eba4c79SScott Long case LINUX_SG_GET_REQUEST_TABLE: 6421eba4c79SScott Long case LINUX_SG_SET_KEEP_ORPHAN: 6431eba4c79SScott Long case LINUX_SG_GET_KEEP_ORPHAN: 6441eba4c79SScott Long case LINUX_SG_GET_ACCESS_COUNT: 6451eba4c79SScott Long case LINUX_SG_SET_FORCE_LOW_DMA: 6461eba4c79SScott Long case LINUX_SG_GET_LOW_DMA: 6471eba4c79SScott Long case LINUX_SG_GET_SG_TABLESIZE: 6481eba4c79SScott Long case LINUX_SG_SET_FORCE_PACK_ID: 6491eba4c79SScott Long case LINUX_SG_GET_PACK_ID: 6501eba4c79SScott Long case LINUX_SG_SET_RESERVED_SIZE: 6511eba4c79SScott Long case LINUX_SG_GET_COMMAND_Q: 6521eba4c79SScott Long case LINUX_SG_SET_COMMAND_Q: 6531eba4c79SScott Long case LINUX_SG_SET_DEBUG: 6541eba4c79SScott Long case LINUX_SG_NEXT_CMD_LEN: 6551eba4c79SScott Long default: 6561eba4c79SScott Long #ifdef CAMDEBUG 6571eba4c79SScott Long printf("sgioctl: rejecting cmd 0x%lx\n", cmd); 6581eba4c79SScott Long #endif 6591eba4c79SScott Long error = ENODEV; 6601eba4c79SScott Long break; 6611eba4c79SScott Long } 6621eba4c79SScott Long 6631eba4c79SScott Long return (error); 6641eba4c79SScott Long } 6651eba4c79SScott Long 6661eba4c79SScott Long static int 6671eba4c79SScott Long sgwrite(struct cdev *dev, struct uio *uio, int ioflag) 6681eba4c79SScott Long { 6691eba4c79SScott Long union ccb *ccb; 6701eba4c79SScott Long struct cam_periph *periph; 6711eba4c79SScott Long struct ccb_scsiio *csio; 6721eba4c79SScott Long struct sg_softc *sc; 6731eba4c79SScott Long struct sg_header *hdr; 6741eba4c79SScott Long struct sg_rdwr *rdwr; 6751eba4c79SScott Long u_char cdb_cmd; 6761eba4c79SScott Long char *buf; 6771eba4c79SScott Long int error = 0, cdb_len, buf_len, dir; 6781eba4c79SScott Long 6791eba4c79SScott Long periph = dev->si_drv1; 6801eba4c79SScott Long sc = periph->softc; 6814400b36dSScott Long rdwr = malloc(sizeof(*rdwr), M_DEVBUF, M_WAITOK | M_ZERO); 6821eba4c79SScott Long hdr = &rdwr->hdr.hdr; 6831eba4c79SScott Long 6841eba4c79SScott Long /* Copy in the header block and sanity check it */ 6851eba4c79SScott Long if (uio->uio_resid < sizeof(*hdr)) { 6861eba4c79SScott Long error = EINVAL; 6871eba4c79SScott Long goto out_hdr; 6881eba4c79SScott Long } 6891eba4c79SScott Long error = uiomove(hdr, sizeof(*hdr), uio); 6901eba4c79SScott Long if (error) 6911eba4c79SScott Long goto out_hdr; 6921eba4c79SScott Long 6931eba4c79SScott Long ccb = xpt_alloc_ccb(); 6941eba4c79SScott Long if (ccb == NULL) { 6951eba4c79SScott Long error = ENOMEM; 6961eba4c79SScott Long goto out_hdr; 6971eba4c79SScott Long } 6981eba4c79SScott Long xpt_setup_ccb(&ccb->ccb_h, periph->path, /*priority*/5); 6991eba4c79SScott Long csio = &ccb->csio; 7001eba4c79SScott Long 7011eba4c79SScott Long /* 7021eba4c79SScott Long * Copy in the CDB block. The designers of the interface didn't 7031eba4c79SScott Long * bother to provide a size for this in the header, so we have to 7041eba4c79SScott Long * figure it out ourselves. 7051eba4c79SScott Long */ 7061eba4c79SScott Long if (uio->uio_resid < 1) 7071eba4c79SScott Long goto out_ccb; 7081eba4c79SScott Long error = uiomove(&cdb_cmd, 1, uio); 7091eba4c79SScott Long if (error) 7101eba4c79SScott Long goto out_ccb; 7111eba4c79SScott Long if (hdr->twelve_byte) 7121eba4c79SScott Long cdb_len = 12; 7131eba4c79SScott Long else 7141eba4c79SScott Long cdb_len = scsi_group_len(cdb_cmd); 7151eba4c79SScott Long /* 7161eba4c79SScott Long * We've already read the first byte of the CDB and advanced the uio 7171eba4c79SScott Long * pointer. Just read the rest. 7181eba4c79SScott Long */ 7191eba4c79SScott Long csio->cdb_io.cdb_bytes[0] = cdb_cmd; 7201eba4c79SScott Long error = uiomove(&csio->cdb_io.cdb_bytes[1], cdb_len - 1, uio); 7211eba4c79SScott Long if (error) 7221eba4c79SScott Long goto out_ccb; 7231eba4c79SScott Long 7241eba4c79SScott Long /* 7251eba4c79SScott Long * Now set up the data block. Again, the designers didn't bother 7261eba4c79SScott Long * to make this reliable. 7271eba4c79SScott Long */ 7281eba4c79SScott Long buf_len = uio->uio_resid; 7291eba4c79SScott Long if (buf_len != 0) { 7304400b36dSScott Long buf = malloc(buf_len, M_DEVBUF, M_WAITOK | M_ZERO); 7311eba4c79SScott Long error = uiomove(buf, buf_len, uio); 7321eba4c79SScott Long if (error) 7331eba4c79SScott Long goto out_buf; 7341eba4c79SScott Long dir = CAM_DIR_OUT; 7351eba4c79SScott Long } else if (hdr->reply_len != 0) { 7364400b36dSScott Long buf = malloc(hdr->reply_len, M_DEVBUF, M_WAITOK | M_ZERO); 7371eba4c79SScott Long buf_len = hdr->reply_len; 7381eba4c79SScott Long dir = CAM_DIR_IN; 7391eba4c79SScott Long } else { 7401eba4c79SScott Long buf = NULL; 7411eba4c79SScott Long buf_len = 0; 7421eba4c79SScott Long dir = CAM_DIR_NONE; 7431eba4c79SScott Long } 7441eba4c79SScott Long 7451eba4c79SScott Long cam_fill_csio(csio, 7461eba4c79SScott Long /*retries*/1, 7471eba4c79SScott Long sgdone, 7481eba4c79SScott Long dir|CAM_DEV_QFRZDIS, 7491eba4c79SScott Long MSG_SIMPLE_Q_TAG, 7501eba4c79SScott Long buf, 7511eba4c79SScott Long buf_len, 7521eba4c79SScott Long SG_MAX_SENSE, 7531eba4c79SScott Long cdb_len, 7541eba4c79SScott Long 60*hz); 7551eba4c79SScott Long 7561eba4c79SScott Long /* 7571eba4c79SScott Long * Send off the command and hope that it works. This path does not 7581eba4c79SScott Long * go through sgstart because the I/O is supposed to be asynchronous. 7591eba4c79SScott Long */ 7601eba4c79SScott Long rdwr->buf = buf; 7611eba4c79SScott Long rdwr->buf_len = buf_len; 7621eba4c79SScott Long rdwr->tag = hdr->pack_id; 7631eba4c79SScott Long rdwr->ccb = ccb; 7641eba4c79SScott Long rdwr->state = SG_RDWR_INPROG; 7651eba4c79SScott Long ccb->ccb_h.ccb_rdwr = rdwr; 7661eba4c79SScott Long ccb->ccb_h.ccb_type = SG_CCB_RDWR_IO; 7671eba4c79SScott Long TAILQ_INSERT_TAIL(&sc->rdwr_done, rdwr, rdwr_link); 7681eba4c79SScott Long return (sgsendrdwr(periph, ccb)); 7691eba4c79SScott Long 7701eba4c79SScott Long out_buf: 7711eba4c79SScott Long free(buf, M_DEVBUF); 7721eba4c79SScott Long out_ccb: 7731eba4c79SScott Long xpt_free_ccb(ccb); 7741eba4c79SScott Long out_hdr: 7751eba4c79SScott Long free(rdwr, M_DEVBUF); 7761eba4c79SScott Long return (error); 7771eba4c79SScott Long } 7781eba4c79SScott Long 7791eba4c79SScott Long static int 7801eba4c79SScott Long sgread(struct cdev *dev, struct uio *uio, int ioflag) 7811eba4c79SScott Long { 7821eba4c79SScott Long struct ccb_scsiio *csio; 7831eba4c79SScott Long struct cam_periph *periph; 7841eba4c79SScott Long struct sg_softc *sc; 7851eba4c79SScott Long struct sg_header *hdr; 7861eba4c79SScott Long struct sg_rdwr *rdwr; 7871eba4c79SScott Long u_short hstat, dstat; 7881eba4c79SScott Long int error, pack_len, reply_len, pack_id; 7891eba4c79SScott Long 7901eba4c79SScott Long periph = dev->si_drv1; 7911eba4c79SScott Long sc = periph->softc; 7921eba4c79SScott Long 7931eba4c79SScott Long /* XXX The pack len field needs to be updated and written out instead 7941eba4c79SScott Long * of discarded. Not sure how to do that. 7951eba4c79SScott Long */ 7961eba4c79SScott Long uio->uio_rw = UIO_WRITE; 7971eba4c79SScott Long if ((error = uiomove(&pack_len, 4, uio)) != 0) 7981eba4c79SScott Long return (error); 7991eba4c79SScott Long if ((error = uiomove(&reply_len, 4, uio)) != 0) 8001eba4c79SScott Long return (error); 8011eba4c79SScott Long if ((error = uiomove(&pack_id, 4, uio)) != 0) 8021eba4c79SScott Long return (error); 8031eba4c79SScott Long uio->uio_rw = UIO_READ; 8041eba4c79SScott Long 8051eba4c79SScott Long search: 8061eba4c79SScott Long TAILQ_FOREACH(rdwr, &sc->rdwr_done, rdwr_link) { 8071eba4c79SScott Long if (rdwr->tag == pack_id) 8081eba4c79SScott Long break; 8091eba4c79SScott Long } 8101eba4c79SScott Long if ((rdwr == NULL) || (rdwr->state != SG_RDWR_DONE)) { 8111eba4c79SScott Long if (tsleep(rdwr, PCATCH, "sgread", 0) == ERESTART) 8121eba4c79SScott Long return (EAGAIN); 8131eba4c79SScott Long goto search; 8141eba4c79SScott Long } 8151eba4c79SScott Long TAILQ_REMOVE(&sc->rdwr_done, rdwr, rdwr_link); 8161eba4c79SScott Long 8171eba4c79SScott Long hdr = &rdwr->hdr.hdr; 8181eba4c79SScott Long csio = &rdwr->ccb->csio; 8191eba4c79SScott Long sg_scsiio_status(csio, &hstat, &dstat); 8201eba4c79SScott Long hdr->host_status = hstat; 8211eba4c79SScott Long hdr->driver_status = dstat; 8221eba4c79SScott Long hdr->target_status = csio->scsi_status >> 1; 8231eba4c79SScott Long 8241eba4c79SScott Long switch (hstat) { 8251eba4c79SScott Long case DID_OK: 8261eba4c79SScott Long case DID_PASSTHROUGH: 8271eba4c79SScott Long case DID_SOFT_ERROR: 8281eba4c79SScott Long hdr->result = 0; 8291eba4c79SScott Long break; 8301eba4c79SScott Long case DID_NO_CONNECT: 8311eba4c79SScott Long case DID_BUS_BUSY: 8321eba4c79SScott Long case DID_TIME_OUT: 8331eba4c79SScott Long hdr->result = EBUSY; 8341eba4c79SScott Long break; 8351eba4c79SScott Long case DID_BAD_TARGET: 8361eba4c79SScott Long case DID_ABORT: 8371eba4c79SScott Long case DID_PARITY: 8381eba4c79SScott Long case DID_RESET: 8391eba4c79SScott Long case DID_BAD_INTR: 8401eba4c79SScott Long case DID_ERROR: 8411eba4c79SScott Long default: 8421eba4c79SScott Long hdr->result = EIO; 8431eba4c79SScott Long break; 8441eba4c79SScott Long } 8451eba4c79SScott Long 8461eba4c79SScott Long if (dstat == DRIVER_SENSE) { 8471eba4c79SScott Long bcopy(&csio->sense_data, hdr->sense_buffer, 8481eba4c79SScott Long min(csio->sense_len, SG_MAX_SENSE)); 8491eba4c79SScott Long #ifdef CAMDEBUG 8501eba4c79SScott Long scsi_sense_print(csio); 8511eba4c79SScott Long #endif 8521eba4c79SScott Long } 8531eba4c79SScott Long 8541eba4c79SScott Long error = uiomove(&hdr->result, sizeof(*hdr) - 8551eba4c79SScott Long offsetof(struct sg_header, result), uio); 8561eba4c79SScott Long if ((error == 0) && (hdr->result == 0)) 8571eba4c79SScott Long error = uiomove(rdwr->buf, rdwr->buf_len, uio); 8581eba4c79SScott Long 8591eba4c79SScott Long xpt_free_ccb(rdwr->ccb); 8601eba4c79SScott Long free(rdwr->buf, M_DEVBUF); 8611eba4c79SScott Long free(rdwr, M_DEVBUF); 8621eba4c79SScott Long return (error); 8631eba4c79SScott Long } 8641eba4c79SScott Long 8651eba4c79SScott Long static int 8661eba4c79SScott Long sgsendccb(struct cam_periph *periph, union ccb *ccb) 8671eba4c79SScott Long { 8681eba4c79SScott Long struct sg_softc *softc; 8691eba4c79SScott Long struct cam_periph_map_info mapinfo; 8701eba4c79SScott Long int error, need_unmap = 0; 8711eba4c79SScott Long 8721eba4c79SScott Long softc = periph->softc; 8731eba4c79SScott Long if (((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) 8741eba4c79SScott Long && (ccb->csio.data_ptr != NULL)) { 8751eba4c79SScott Long bzero(&mapinfo, sizeof(mapinfo)); 8761eba4c79SScott Long error = cam_periph_mapmem(ccb, &mapinfo); 8771eba4c79SScott Long if (error) 8781eba4c79SScott Long return (error); 8791eba4c79SScott Long need_unmap = 1; 8801eba4c79SScott Long } 8811eba4c79SScott Long 8821eba4c79SScott Long error = cam_periph_runccb(ccb, 8831eba4c79SScott Long sgerror, 8841eba4c79SScott Long CAM_RETRY_SELTO, 8851eba4c79SScott Long SF_RETRY_UA, 8861eba4c79SScott Long softc->device_stats); 8871eba4c79SScott Long 8881eba4c79SScott Long if (need_unmap) 8891eba4c79SScott Long cam_periph_unmapmem(ccb, &mapinfo); 8901eba4c79SScott Long 8911eba4c79SScott Long return (error); 8921eba4c79SScott Long } 8931eba4c79SScott Long 8941eba4c79SScott Long static int 8951eba4c79SScott Long sgsendrdwr(struct cam_periph *periph, union ccb *ccb) 8961eba4c79SScott Long { 8971eba4c79SScott Long struct sg_softc *softc; 8981eba4c79SScott Long 8991eba4c79SScott Long softc = periph->softc; 9001eba4c79SScott Long devstat_start_transaction(softc->device_stats, NULL); 9011eba4c79SScott Long xpt_action(ccb); 9021eba4c79SScott Long return (0); 9031eba4c79SScott Long } 9041eba4c79SScott Long 9051eba4c79SScott Long static int 9061eba4c79SScott Long sgerror(union ccb *ccb, uint32_t cam_flags, uint32_t sense_flags) 9071eba4c79SScott Long { 9081eba4c79SScott Long struct cam_periph *periph; 9091eba4c79SScott Long struct sg_softc *softc; 9101eba4c79SScott Long 9111eba4c79SScott Long periph = xpt_path_periph(ccb->ccb_h.path); 9121eba4c79SScott Long softc = (struct sg_softc *)periph->softc; 9131eba4c79SScott Long 9141eba4c79SScott Long return (cam_periph_error(ccb, cam_flags, sense_flags, 9151eba4c79SScott Long &softc->saved_ccb)); 9161eba4c79SScott Long } 9171eba4c79SScott Long 9181eba4c79SScott Long static void 9191eba4c79SScott Long sg_scsiio_status(struct ccb_scsiio *csio, u_short *hoststat, u_short *drvstat) 9201eba4c79SScott Long { 9211eba4c79SScott Long int status; 9221eba4c79SScott Long 9231eba4c79SScott Long status = csio->ccb_h.status; 9241eba4c79SScott Long 9251eba4c79SScott Long switch (status & CAM_STATUS_MASK) { 9261eba4c79SScott Long case CAM_REQ_CMP: 9271eba4c79SScott Long *hoststat = DID_OK; 9281eba4c79SScott Long *drvstat = 0; 9291eba4c79SScott Long break; 9301eba4c79SScott Long case CAM_REQ_CMP_ERR: 9311eba4c79SScott Long *hoststat = DID_ERROR; 9321eba4c79SScott Long *drvstat = 0; 9331eba4c79SScott Long break; 9341eba4c79SScott Long case CAM_REQ_ABORTED: 9351eba4c79SScott Long *hoststat = DID_ABORT; 9361eba4c79SScott Long *drvstat = 0; 9371eba4c79SScott Long break; 9381eba4c79SScott Long case CAM_REQ_INVALID: 9391eba4c79SScott Long *hoststat = DID_ERROR; 9401eba4c79SScott Long *drvstat = DRIVER_INVALID; 9411eba4c79SScott Long break; 9421eba4c79SScott Long case CAM_DEV_NOT_THERE: 9431eba4c79SScott Long *hoststat = DID_BAD_TARGET; 9441eba4c79SScott Long *drvstat = 0; 9451eba4c79SScott Long case CAM_SEL_TIMEOUT: 9461eba4c79SScott Long *hoststat = DID_NO_CONNECT; 9471eba4c79SScott Long *drvstat = 0; 9481eba4c79SScott Long break; 9491eba4c79SScott Long case CAM_CMD_TIMEOUT: 9501eba4c79SScott Long *hoststat = DID_TIME_OUT; 9511eba4c79SScott Long *drvstat = 0; 9521eba4c79SScott Long break; 9531eba4c79SScott Long case CAM_SCSI_STATUS_ERROR: 9541eba4c79SScott Long *hoststat = DID_ERROR; 9551eba4c79SScott Long *drvstat = 0; 9561eba4c79SScott Long case CAM_SCSI_BUS_RESET: 9571eba4c79SScott Long *hoststat = DID_RESET; 9581eba4c79SScott Long *drvstat = 0; 9591eba4c79SScott Long break; 9601eba4c79SScott Long case CAM_UNCOR_PARITY: 9611eba4c79SScott Long *hoststat = DID_PARITY; 9621eba4c79SScott Long *drvstat = 0; 9631eba4c79SScott Long break; 9641eba4c79SScott Long case CAM_SCSI_BUSY: 9651eba4c79SScott Long *hoststat = DID_BUS_BUSY; 9661eba4c79SScott Long *drvstat = 0; 9671eba4c79SScott Long default: 9681eba4c79SScott Long *hoststat = DID_ERROR; 9691eba4c79SScott Long *drvstat = DRIVER_ERROR; 9701eba4c79SScott Long } 9711eba4c79SScott Long 9721eba4c79SScott Long if (status & CAM_AUTOSNS_VALID) 9731eba4c79SScott Long *drvstat = DRIVER_SENSE; 9741eba4c79SScott Long } 9751eba4c79SScott Long 9761eba4c79SScott Long static int 9771eba4c79SScott Long scsi_group_len(u_char cmd) 9781eba4c79SScott Long { 9791eba4c79SScott Long int len[] = {6, 10, 10, 12, 12, 12, 10, 10}; 9801eba4c79SScott Long int group; 9811eba4c79SScott Long 9821eba4c79SScott Long group = (cmd >> 5) & 0x7; 9831eba4c79SScott Long return (len[group]); 9841eba4c79SScott Long } 9851eba4c79SScott Long 986