Kernel - Add scsi/scsi_sg linux-compatible SCSI pass through device
authorMatthew Dillon <dillon@apollo.backplane.com>
Wed, 26 Aug 2009 01:40:34 +0000 (18:40 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Wed, 26 Aug 2009 01:40:34 +0000 (18:40 -0700)
Taken-from: FreeBSD

sys/bus/cam/scsi/scsi_sg.c [new file with mode: 0644]
sys/bus/cam/scsi/scsi_sg.h [new file with mode: 0644]
sys/conf/files
sys/config/GENERIC
sys/emulation/linux/linux_ioctl.h

diff --git a/sys/bus/cam/scsi/scsi_sg.c b/sys/bus/cam/scsi/scsi_sg.c
new file mode 100644 (file)
index 0000000..054e747
--- /dev/null
@@ -0,0 +1,1006 @@
+/*-
+ * Copyright (c) 2007 Scott Long
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * scsi_sg peripheral driver.  This driver is meant to implement the Linux
+ * SG passthrough interface for SCSI.
+ */
+
+#include <sys/conf.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+#include <sys/bio.h>
+#include <sys/malloc.h>
+#include <sys/fcntl.h>
+#include <sys/ioccom.h>
+#include <sys/errno.h>
+#include <sys/devicestat.h>
+#include <sys/proc.h>
+#include <sys/uio.h>
+#include <sys/device.h>
+#include <sys/sysmsg.h>
+
+#include "../cam.h"
+#include "../cam_ccb.h"
+#include "../cam_periph.h"
+#include "../cam_queue.h"
+#include "../cam_xpt_periph.h"
+#include "../cam_debug.h"
+#include "../cam_sim.h"
+
+#include <emulation/linux/linux_ioctl.h>
+
+#include "scsi_all.h"
+#include "scsi_message.h"
+#include "scsi_sg.h"
+
+typedef enum {
+       SG_FLAG_OPEN            = 0x01,
+       SG_FLAG_LOCKED          = 0x02,
+       SG_FLAG_INVALID         = 0x04
+} sg_flags;
+
+typedef enum {
+       SG_STATE_NORMAL
+} sg_state;
+
+typedef enum {
+       SG_RDWR_FREE,
+       SG_RDWR_INPROG,
+       SG_RDWR_DONE
+} sg_rdwr_state;
+
+typedef enum {
+       SG_CCB_RDWR_IO,
+       SG_CCB_WAITING
+} sg_ccb_types;
+
+#define ccb_type       ppriv_field0
+#define ccb_rdwr       ppriv_ptr1
+
+struct sg_rdwr {
+       TAILQ_ENTRY(sg_rdwr)    rdwr_link;
+       int                     tag;
+       int                     state;
+       int                     buf_len;
+       char                    *buf;
+       union ccb               *ccb;
+       union {
+               struct sg_header hdr;
+               struct sg_io_hdr io_hdr;
+       } hdr;
+};
+
+struct sg_softc {
+       sg_state                state;
+       sg_flags                flags;
+       struct devstat          device_stats;
+       TAILQ_HEAD(, sg_rdwr)   rdwr_done;
+       cdev_t                  dev;
+       int                     sg_timeout;
+       int                     sg_user_timeout;
+       uint8_t                 pd_type;
+       union ccb               saved_ccb;
+};
+
+static d_open_t                sgopen;
+static d_close_t       sgclose;
+static d_ioctl_t       sgioctl;
+static d_write_t       sgwrite;
+static d_read_t                sgread;
+
+static periph_init_t   sginit;
+static periph_ctor_t   sgregister;
+static periph_oninv_t  sgoninvalidate;
+static periph_dtor_t   sgcleanup;
+static periph_start_t  sgstart;
+static void            sgasync(void *callback_arg, uint32_t code,
+                               struct cam_path *path, void *arg);
+static void            sgdone(struct cam_periph *periph, union ccb *done_ccb);
+static int             sgsendccb(struct cam_periph *periph, union ccb *ccb);
+static int             sgsendrdwr(struct cam_periph *periph, union ccb *ccb);
+static int             sgerror(union ccb *ccb, uint32_t cam_flags,
+                               uint32_t sense_flags);
+static void            sg_scsiio_status(struct ccb_scsiio *csio,
+                                        u_short *hoststat, u_short *drvstat);
+
+static int             scsi_group_len(u_char cmd);
+
+static struct periph_driver sgdriver =
+{
+       sginit, "sg",
+       TAILQ_HEAD_INITIALIZER(sgdriver.units), /* gen */ 0
+};
+PERIPHDRIVER_DECLARE(sg, sgdriver);
+
+static struct dev_ops sg_ops = {
+       { "sg", 0, D_DISK },
+       .d_open =       sgopen,
+       .d_close =      sgclose,
+       .d_read =       sgread,
+       .d_write =      sgwrite,
+       .d_ioctl =      sgioctl
+};
+
+static int sg_version = 30125;
+
+static void
+sginit(void)
+{
+       cam_status status;
+
+       /*
+        * Install a global async callback.  This callback will receive aync
+        * callbacks like "new device found".
+        */
+       status = xpt_register_async(AC_FOUND_DEVICE, sgasync, NULL, NULL);
+
+       if (status != CAM_REQ_CMP) {
+               kprintf("sg: Failed to attach master async callbac "
+                       "due to status 0x%x!\n", status);
+       }
+}
+
+static void
+sgoninvalidate(struct cam_periph *periph)
+{
+       struct sg_softc *softc;
+
+       softc = (struct sg_softc *)periph->softc;
+
+       /*
+        * Deregister any async callbacks.
+        */
+       xpt_register_async(0, sgasync, periph, periph->path);
+
+       softc->flags |= SG_FLAG_INVALID;
+
+       /*
+        * XXX Return all queued I/O with ENXIO.
+        * XXX Handle any transactions queued to the card
+        *     with XPT_ABORT_CCB.
+        */
+
+       if (bootverbose) {
+               xpt_print(periph->path, "lost device\n");
+       }
+}
+
+static void
+sgcleanup(struct cam_periph *periph)
+{
+       struct sg_softc *softc;
+
+       softc = (struct sg_softc *)periph->softc;
+       if (bootverbose)
+               xpt_print(periph->path, "removing device entry\n");
+       devstat_remove_entry(&softc->device_stats);
+       cam_periph_unlock(periph);
+       destroy_dev(softc->dev);
+       cam_periph_lock(periph);
+       kfree(softc, M_DEVBUF);
+}
+
+static void
+sgasync(void *callback_arg, uint32_t code, struct cam_path *path, void *arg)
+{
+       struct cam_periph *periph;
+
+       periph = (struct cam_periph *)callback_arg;
+
+       switch (code) {
+       case AC_FOUND_DEVICE:
+       {
+               struct ccb_getdev *cgd;
+               cam_status status;
+
+               cgd = (struct ccb_getdev *)arg;
+               if (cgd == NULL)
+                       break;
+
+#if 0
+               if (cgd->protocol != PROTO_SCSI)
+                       break;
+#endif
+
+               /*
+                * Allocate a peripheral instance for this device and
+                * start the probe process.
+                */
+               status = cam_periph_alloc(sgregister, sgoninvalidate,
+                                         sgcleanup, sgstart,
+                                         "sg", CAM_PERIPH_BIO, cgd->ccb_h.path,
+                                         sgasync, AC_FOUND_DEVICE, cgd);
+               if ((status != CAM_REQ_CMP) && (status != CAM_REQ_INPROG)) {
+                       const struct cam_status_entry *entry;
+
+                       entry = cam_fetch_status_entry(status);
+                       kprintf("sgasync: Unable to attach new device "
+                               "due to status %#x: %s\n", status, entry ?
+                               entry->status_text : "Unknown");
+               }
+               break;
+       }
+       default:
+               cam_periph_async(periph, code, path, arg);
+               break;
+       }
+}
+
+static cam_status
+sgregister(struct cam_periph *periph, void *arg)
+{
+       struct sg_softc *softc;
+       struct ccb_getdev *cgd;
+       int no_tags;
+
+       cgd = (struct ccb_getdev *)arg;
+       if (periph == NULL) {
+               kprintf("sgregister: periph was NULL!!\n");
+               return (CAM_REQ_CMP_ERR);
+       }
+
+       if (cgd == NULL) {
+               kprintf("sgregister: no getdev CCB, can't register device\n");
+               return (CAM_REQ_CMP_ERR);
+       }
+
+       softc = kmalloc(sizeof(*softc), M_DEVBUF, M_WAITOK | M_ZERO);
+       if (softc == NULL) {
+               kprintf("sgregister: Unable to allocate softc\n");
+               return (CAM_REQ_CMP_ERR);
+       }
+
+       softc->state = SG_STATE_NORMAL;
+       softc->pd_type = SID_TYPE(&cgd->inq_data);
+       softc->sg_timeout = SG_DEFAULT_TIMEOUT / SG_DEFAULT_HZ * hz;
+       softc->sg_user_timeout = SG_DEFAULT_TIMEOUT;
+       TAILQ_INIT(&softc->rdwr_done);
+       periph->softc = softc;
+
+       /*
+        * We pass in 0 for all blocksize, since we don't know what the
+        * blocksize of the device is, if it even has a blocksize.
+        */
+       cam_periph_unlock(periph);
+       no_tags = (cgd->inq_data.flags & SID_CmdQue) == 0;
+       devstat_add_entry(&softc->device_stats, "sg",
+                         periph->unit_number, 0,
+                         DEVSTAT_NO_BLOCKSIZE |
+                             (no_tags ? DEVSTAT_NO_ORDERED_TAGS : 0),
+                         softc->pd_type |
+                         DEVSTAT_TYPE_IF_SCSI | DEVSTAT_PRIORITY_PASS,
+                         DEVSTAT_PRIORITY_PASS);
+
+       /* Register the device */
+       softc->dev = make_dev(&sg_ops, periph->unit_number,
+                             UID_ROOT, GID_OPERATOR, 0600, "%s%d",
+                             periph->periph_name, periph->unit_number);
+       make_dev_alias(softc->dev, "sg%c", 'a' + periph->unit_number);
+       cam_periph_lock(periph);
+       softc->dev->si_drv1 = periph;
+
+       /*
+        * Add as async callback so that we get
+        * notified if this device goes away.
+        */
+       xpt_register_async(AC_LOST_DEVICE, sgasync, periph, periph->path);
+
+       if (bootverbose)
+               xpt_announce_periph(periph, NULL);
+
+       return (CAM_REQ_CMP);
+}
+
+static void
+sgstart(struct cam_periph *periph, union ccb *start_ccb)
+{
+       struct sg_softc *softc;
+
+       softc = (struct sg_softc *)periph->softc;
+
+       switch (softc->state) {
+       case SG_STATE_NORMAL:
+               start_ccb->ccb_h.ccb_type = SG_CCB_WAITING;
+               SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
+                                 periph_links.sle);
+               periph->immediate_priority = CAM_PRIORITY_NONE;
+               wakeup(&periph->ccb_list);
+               break;
+       }
+}
+
+static void
+sgdone(struct cam_periph *periph, union ccb *done_ccb)
+{
+       struct sg_softc *softc;
+       struct ccb_scsiio *csio;
+
+       softc = (struct sg_softc *)periph->softc;
+       csio = &done_ccb->csio;
+       switch (csio->ccb_h.ccb_type) {
+       case SG_CCB_WAITING:
+               /* Caller will release the CCB */
+               wakeup(&done_ccb->ccb_h.cbfcnp);
+               return;
+       case SG_CCB_RDWR_IO:
+       {
+               struct sg_rdwr *rdwr;
+               int state;
+
+               devstat_end_transaction(
+                       &softc->device_stats,
+                       csio->dxfer_len,
+                       csio->tag_action & 0xf,
+                       ((csio->ccb_h.flags & CAM_DIR_MASK) ==
+                         CAM_DIR_NONE) ? DEVSTAT_NO_DATA :
+                           ((csio->ccb_h.flags & CAM_DIR_OUT) ?
+                            DEVSTAT_WRITE : DEVSTAT_READ));
+
+               rdwr = done_ccb->ccb_h.ccb_rdwr;
+               state = rdwr->state;
+               rdwr->state = SG_RDWR_DONE;
+               wakeup(rdwr);
+               break;
+       }
+       default:
+               panic("unknown sg CCB type");
+       }
+}
+
+static int
+sgopen(struct dev_open_args *ap)
+/*cdev_t dev, int flags, int fmt, struct thread *td)*/
+{
+       struct cam_periph *periph;
+       struct sg_softc *softc;
+       int error = 0;
+
+       periph = (struct cam_periph *)ap->a_head.a_dev->si_drv1;
+       if (periph == NULL)
+               return (ENXIO);
+
+       /*
+        * Don't allow access when we're running at a high securelevel.
+        */
+       if (securelevel > 1) {
+               cam_periph_unlock(periph);
+               cam_periph_release(periph);
+               return(EPERM);
+       }
+       cam_periph_lock(periph);
+
+       softc = (struct sg_softc *)periph->softc;
+       if (softc->flags & SG_FLAG_INVALID) {
+               cam_periph_unlock(periph);
+               return (ENXIO);
+       }
+
+       if ((softc->flags & SG_FLAG_OPEN) == 0) {
+               softc->flags |= SG_FLAG_OPEN;
+               cam_periph_unlock(periph);
+       } else {
+               /* Device closes aren't symmetrical, fix up the refcount. */
+               cam_periph_unlock(periph);
+               cam_periph_release(periph);
+       }
+
+       return (error);
+}
+
+static int
+sgclose(struct dev_close_args *ap)
+/* cdev_t dev, int flag, int fmt, struct thread *td) */
+{
+       struct cam_periph *periph;
+       struct sg_softc *softc;
+
+       periph = (struct cam_periph *)ap->a_head.a_dev->si_drv1;
+       if (periph == NULL)
+               return (ENXIO);
+
+       cam_periph_lock(periph);
+
+       softc = (struct sg_softc *)periph->softc;
+       softc->flags &= ~SG_FLAG_OPEN;
+
+       cam_periph_unlock(periph);
+       cam_periph_release(periph);
+
+       return (0);
+}
+
+static int
+sgioctl(struct dev_ioctl_args *ap)
+/* cdev_t dev, u_long cmd, caddr_t arg, int flag, struct thread *td) */
+{
+       union ccb *ccb;
+       struct ccb_scsiio *csio;
+       struct cam_periph *periph;
+       struct sg_softc *softc;
+       struct sg_io_hdr req;
+       int dir, error;
+
+       periph = (struct cam_periph *)ap->a_head.a_dev->si_drv1;
+       if (periph == NULL)
+               return (ENXIO);
+
+       cam_periph_lock(periph);
+
+       softc = (struct sg_softc *)periph->softc;
+       error = 0;
+
+       switch (ap->a_cmd) {
+       case LINUX_SCSI_GET_BUS_NUMBER: {
+               int busno;
+
+               busno = xpt_path_path_id(periph->path);
+               error = copyout(&busno, ap->a_data, sizeof(busno));
+               break;
+       }
+       case LINUX_SCSI_GET_IDLUN: {
+               struct scsi_idlun idlun;
+               struct cam_sim *sim;
+
+               idlun.dev_id = xpt_path_target_id(periph->path);
+               sim = xpt_path_sim(periph->path);
+               idlun.host_unique_id = sim->unit_number;
+               error = copyout(&idlun, ap->a_data, sizeof(idlun));
+               break;
+       }
+       case SG_GET_VERSION_NUM:
+       case LINUX_SG_GET_VERSION_NUM:
+               error = copyout(&sg_version, ap->a_data, sizeof(sg_version));
+               break;
+       case SG_SET_TIMEOUT:
+       case LINUX_SG_SET_TIMEOUT: {
+               u_int user_timeout;
+
+               error = copyin(ap->a_data, &user_timeout, sizeof(u_int));
+               if (error == 0) {
+                       softc->sg_user_timeout = user_timeout;
+                       softc->sg_timeout = user_timeout / SG_DEFAULT_HZ * hz;
+               }
+               break;
+       }
+       case SG_GET_TIMEOUT:
+       case LINUX_SG_GET_TIMEOUT:
+               /*
+                * The value is returned directly to the syscall.
+                */
+               ap->a_sysmsg->sm_result.iresult = softc->sg_user_timeout;
+               error = 0;
+               break;
+       case SG_IO:
+       case LINUX_SG_IO:
+               error = copyin(ap->a_data, &req, sizeof(req));
+               if (error)
+                       break;
+
+               if (req.cmd_len > IOCDBLEN) {
+                       error = EINVAL;
+                       break;
+               }
+
+               if (req.iovec_count != 0) {
+                       error = EOPNOTSUPP;
+                       break;
+               }
+
+               ccb = cam_periph_getccb(periph, /*priority*/5);
+               csio = &ccb->csio;
+
+               error = copyin(req.cmdp, &csio->cdb_io.cdb_bytes,
+                   req.cmd_len);
+               if (error) {
+                       xpt_release_ccb(ccb);
+                       break;
+               }
+
+               switch(req.dxfer_direction) {
+               case SG_DXFER_TO_DEV:
+                       dir = CAM_DIR_OUT;
+                       break;
+               case SG_DXFER_FROM_DEV:
+                       dir = CAM_DIR_IN;
+                       break;
+               case SG_DXFER_TO_FROM_DEV:
+                       dir = CAM_DIR_IN | CAM_DIR_OUT;
+                       break;
+               case SG_DXFER_NONE:
+               default:
+                       dir = CAM_DIR_NONE;
+                       break;
+               }
+
+               cam_fill_csio(csio,
+                             /*retries*/1,
+                             sgdone,
+                             dir|CAM_DEV_QFRZDIS,
+                             MSG_SIMPLE_Q_TAG,
+                             req.dxferp,
+                             req.dxfer_len,
+                             req.mx_sb_len,
+                             req.cmd_len,
+                             req.timeout);
+
+               error = sgsendccb(periph, ccb);
+               if (error) {
+                       req.host_status = DID_ERROR;
+                       req.driver_status = DRIVER_INVALID;
+                       xpt_release_ccb(ccb);
+                       break;
+               }
+
+               req.status = csio->scsi_status;
+               req.masked_status = (csio->scsi_status >> 1) & 0x7f;
+               sg_scsiio_status(csio, &req.host_status, &req.driver_status);
+               req.resid = csio->resid;
+               req.duration = csio->ccb_h.timeout;
+               req.info = 0;
+
+               error = copyout(&req, ap->a_data, sizeof(req));
+               if ((error == 0) && (csio->ccb_h.status & CAM_AUTOSNS_VALID)
+                   && (req.sbp != NULL)) {
+                       req.sb_len_wr = req.mx_sb_len - csio->sense_resid;
+                       error = copyout(&csio->sense_data, req.sbp,
+                                       req.sb_len_wr);
+               }
+
+               xpt_release_ccb(ccb);
+               break;
+
+       case SG_GET_RESERVED_SIZE:
+       case LINUX_SG_GET_RESERVED_SIZE: {
+               int size = 32768;
+
+               error = copyout(&size, ap->a_data, sizeof(size));
+               break;
+       }
+
+       case SG_GET_SCSI_ID:
+       case LINUX_SG_GET_SCSI_ID:
+       {
+               struct sg_scsi_id id;
+
+               id.host_no = 0; /* XXX */
+               id.channel = xpt_path_path_id(periph->path);
+               id.scsi_id = xpt_path_target_id(periph->path);
+               id.lun = xpt_path_lun_id(periph->path);
+               id.scsi_type = softc->pd_type;
+               id.h_cmd_per_lun = 1;
+               id.d_queue_depth = 1;
+               id.unused[0] = 0;
+               id.unused[1] = 0;
+
+               error = copyout(&id, ap->a_data, sizeof(id));
+               break;
+       }
+
+       case SG_EMULATED_HOST:
+       case SG_SET_TRANSFORM:
+       case SG_GET_TRANSFORM:
+       case SG_GET_NUM_WAITING:
+       case SG_SCSI_RESET:
+       case SG_GET_REQUEST_TABLE:
+       case SG_SET_KEEP_ORPHAN:
+       case SG_GET_KEEP_ORPHAN:
+       case SG_GET_ACCESS_COUNT:
+       case SG_SET_FORCE_LOW_DMA:
+       case SG_GET_LOW_DMA:
+       case SG_GET_SG_TABLESIZE:
+       case SG_SET_FORCE_PACK_ID:
+       case SG_GET_PACK_ID:
+       case SG_SET_RESERVED_SIZE:
+       case SG_GET_COMMAND_Q:
+       case SG_SET_COMMAND_Q:
+       case SG_SET_DEBUG:
+       case SG_NEXT_CMD_LEN:
+       case LINUX_SG_EMULATED_HOST:
+       case LINUX_SG_SET_TRANSFORM:
+       case LINUX_SG_GET_TRANSFORM:
+       case LINUX_SG_GET_NUM_WAITING:
+       case LINUX_SG_SCSI_RESET:
+       case LINUX_SG_GET_REQUEST_TABLE:
+       case LINUX_SG_SET_KEEP_ORPHAN:
+       case LINUX_SG_GET_KEEP_ORPHAN:
+       case LINUX_SG_GET_ACCESS_COUNT:
+       case LINUX_SG_SET_FORCE_LOW_DMA:
+       case LINUX_SG_GET_LOW_DMA:
+       case LINUX_SG_GET_SG_TABLESIZE:
+       case LINUX_SG_SET_FORCE_PACK_ID:
+       case LINUX_SG_GET_PACK_ID:
+       case LINUX_SG_SET_RESERVED_SIZE:
+       case LINUX_SG_GET_COMMAND_Q:
+       case LINUX_SG_SET_COMMAND_Q:
+       case LINUX_SG_SET_DEBUG:
+       case LINUX_SG_NEXT_CMD_LEN:
+       default:
+#ifdef CAMDEBUG
+               kprintf("sgioctl: rejecting cmd 0x%lx\n", cmd);
+#endif
+               error = ENODEV;
+               break;
+       }
+
+       cam_periph_unlock(periph);
+       return (error);
+}
+
+static int
+sgwrite(struct dev_write_args *ap)
+/*cdev_t dev, struct uio *uio, int ioflag)*/
+{
+       union ccb *ccb;
+       struct cam_periph *periph;
+       struct ccb_scsiio *csio;
+       struct sg_softc *sc;
+       struct sg_header *hdr;
+       struct sg_rdwr *rdwr;
+       u_char cdb_cmd;
+       char *buf;
+       int error = 0, cdb_len, buf_len, dir;
+       struct uio *uio = ap->a_uio;
+
+       periph = ap->a_head.a_dev->si_drv1;
+       rdwr = kmalloc(sizeof(*rdwr), M_DEVBUF, M_WAITOK | M_ZERO);
+       hdr = &rdwr->hdr.hdr;
+
+       /* Copy in the header block and sanity check it */
+       if (uio->uio_resid < sizeof(*hdr)) {
+               error = EINVAL;
+               goto out_hdr;
+       }
+       error = uiomove((char *)hdr, sizeof(*hdr), uio);
+       if (error)
+               goto out_hdr;
+
+       ccb = xpt_alloc_ccb();
+       if (ccb == NULL) {
+               error = ENOMEM;
+               goto out_hdr;
+       }
+       csio = &ccb->csio;
+
+       /*
+        * Copy in the CDB block.  The designers of the interface didn't
+        * bother to provide a size for this in the header, so we have to
+        * figure it out ourselves.
+        */
+       if (uio->uio_resid < 1)
+               goto out_ccb;
+       error = uiomove(&cdb_cmd, 1, uio);
+       if (error)
+               goto out_ccb;
+       if (hdr->twelve_byte)
+               cdb_len = 12;
+       else
+               cdb_len = scsi_group_len(cdb_cmd);
+       /*
+        * We've already read the first byte of the CDB and advanced the uio
+        * pointer.  Just read the rest.
+        */
+       csio->cdb_io.cdb_bytes[0] = cdb_cmd;
+       error = uiomove(&csio->cdb_io.cdb_bytes[1], cdb_len - 1, uio);
+       if (error)
+               goto out_ccb;
+
+       /*
+        * Now set up the data block.  Again, the designers didn't bother
+        * to make this reliable.
+        */
+       buf_len = uio->uio_resid;
+       if (buf_len != 0) {
+               buf = kmalloc(buf_len, M_DEVBUF, M_WAITOK | M_ZERO);
+               error = uiomove(buf, buf_len, uio);
+               if (error)
+                       goto out_buf;
+               dir = CAM_DIR_OUT;
+       } else if (hdr->reply_len != 0) {
+               buf = kmalloc(hdr->reply_len, M_DEVBUF, M_WAITOK | M_ZERO);
+               buf_len = hdr->reply_len;
+               dir = CAM_DIR_IN;
+       } else {
+               buf = NULL;
+               buf_len = 0;
+               dir = CAM_DIR_NONE;
+       }
+
+       cam_periph_lock(periph);
+       sc = periph->softc;
+       xpt_setup_ccb(&ccb->ccb_h, periph->path, /*priority*/5);
+       cam_fill_csio(csio,
+                     /*retries*/1,
+                     sgdone,
+                     dir|CAM_DEV_QFRZDIS,
+                     MSG_SIMPLE_Q_TAG,
+                     buf,
+                     buf_len,
+                     SG_MAX_SENSE,
+                     cdb_len,
+                     sc->sg_timeout);
+
+       /*
+        * Send off the command and hope that it works. This path does not
+        * go through sgstart because the I/O is supposed to be asynchronous.
+        */
+       rdwr->buf = buf;
+       rdwr->buf_len = buf_len;
+       rdwr->tag = hdr->pack_id;
+       rdwr->ccb = ccb;
+       rdwr->state = SG_RDWR_INPROG;
+       ccb->ccb_h.ccb_rdwr = rdwr;
+       ccb->ccb_h.ccb_type = SG_CCB_RDWR_IO;
+       TAILQ_INSERT_TAIL(&sc->rdwr_done, rdwr, rdwr_link);
+       error = sgsendrdwr(periph, ccb);
+       cam_periph_unlock(periph);
+       return (error);
+
+out_buf:
+       kfree(buf, M_DEVBUF);
+out_ccb:
+       xpt_free_ccb(ccb);
+out_hdr:
+       kfree(rdwr, M_DEVBUF);
+       return (error);
+}
+
+static int
+sgread(struct dev_read_args *ap)
+/*cdev_t dev, struct uio *uio, int ioflag)*/
+{
+       struct ccb_scsiio *csio;
+       struct cam_periph *periph;
+       struct sg_softc *sc;
+       struct sg_header *hdr;
+       struct sg_rdwr *rdwr;
+       u_short hstat, dstat;
+       int error, pack_len, reply_len, pack_id;
+       struct uio *uio = ap->a_uio;
+
+       periph = ap->a_head.a_dev->si_drv1;
+
+       /* XXX The pack len field needs to be updated and written out instead
+        * of discarded.  Not sure how to do that.
+        */
+       uio->uio_rw = UIO_WRITE;
+       if ((error = uiomove((char *)&pack_len, 4, uio)) != 0)
+               return (error);
+       if ((error = uiomove((char *)&reply_len, 4, uio)) != 0)
+               return (error);
+       if ((error = uiomove((char *)&pack_id, 4, uio)) != 0)
+               return (error);
+       uio->uio_rw = UIO_READ;
+
+       cam_periph_lock(periph);
+       sc = periph->softc;
+search:
+       TAILQ_FOREACH(rdwr, &sc->rdwr_done, rdwr_link) {
+               if (rdwr->tag == pack_id)
+                       break;
+       }
+       if (rdwr == NULL) {
+               cam_periph_unlock(periph);
+               if (tsleep(&hstat, PCATCH, "sgnull", 0) == ERESTART)
+                       return(EAGAIN);
+               cam_periph_lock(periph);
+               goto search;
+       }
+       if (rdwr->state != SG_RDWR_DONE) {
+               tsleep_interlock(rdwr, PCATCH);
+               cam_periph_unlock(periph);
+               if (rdwr->state != SG_RDWR_DONE) {
+                   if (tsleep(rdwr, PCATCH | PINTERLOCKED, "sgread", 0) ==
+                       ERESTART) {
+                               return (EAGAIN);
+                   }
+               }
+               cam_periph_lock(periph);
+               goto search;
+       }
+       TAILQ_REMOVE(&sc->rdwr_done, rdwr, rdwr_link);
+       cam_periph_unlock(periph);
+
+       hdr = &rdwr->hdr.hdr;
+       csio = &rdwr->ccb->csio;
+       sg_scsiio_status(csio, &hstat, &dstat);
+       hdr->host_status = hstat;
+       hdr->driver_status = dstat;
+       hdr->target_status = csio->scsi_status >> 1;
+
+       switch (hstat) {
+       case DID_OK:
+       case DID_PASSTHROUGH:
+       case DID_SOFT_ERROR:
+               hdr->result = 0;
+               break;
+       case DID_NO_CONNECT:
+       case DID_BUS_BUSY:
+       case DID_TIME_OUT:
+               hdr->result = EBUSY;
+               break;
+       case DID_BAD_TARGET:
+       case DID_ABORT:
+       case DID_PARITY:
+       case DID_RESET:
+       case DID_BAD_INTR:
+       case DID_ERROR:
+       default:
+               hdr->result = EIO;
+               break;
+       }
+
+       if (dstat == DRIVER_SENSE) {
+               bcopy(&csio->sense_data, hdr->sense_buffer,
+                     min(csio->sense_len, SG_MAX_SENSE));
+#ifdef CAMDEBUG
+               scsi_sense_print(csio);
+#endif
+       }
+
+       error = uiomove((char *)&hdr->result, sizeof(*hdr) -
+                       offsetof(struct sg_header, result), uio);
+       if ((error == 0) && (hdr->result == 0))
+               error = uiomove(rdwr->buf, rdwr->buf_len, uio);
+
+       cam_periph_lock(periph);
+       xpt_free_ccb(rdwr->ccb);
+       cam_periph_unlock(periph);
+       kfree(rdwr->buf, M_DEVBUF);
+       kfree(rdwr, M_DEVBUF);
+       return (error);
+}
+
+static int
+sgsendccb(struct cam_periph *periph, union ccb *ccb)
+{
+       struct sg_softc *softc;
+       struct cam_periph_map_info mapinfo;
+       int error, need_unmap = 0;
+
+       softc = periph->softc;
+       if (((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)
+           && (ccb->csio.data_ptr != NULL)) {
+               bzero(&mapinfo, sizeof(mapinfo));
+
+               /*
+                * cam_periph_mapmem calls into proc and vm functions that can
+                * sleep as well as trigger I/O, so we can't hold the lock.
+                * Dropping it here is reasonably safe.
+                */
+               cam_periph_unlock(periph);
+               error = cam_periph_mapmem(ccb, &mapinfo);
+               cam_periph_lock(periph);
+               if (error)
+                       return (error);
+               need_unmap = 1;
+       }
+
+       error = cam_periph_runccb(ccb, sgerror, CAM_RETRY_SELTO,
+                                 SF_RETRY_UA, &softc->device_stats);
+
+       if (need_unmap)
+               cam_periph_unmapmem(ccb, &mapinfo);
+
+       return (error);
+}
+
+static int
+sgsendrdwr(struct cam_periph *periph, union ccb *ccb)
+{
+       struct sg_softc *softc;
+
+       softc = periph->softc;
+       devstat_start_transaction(&softc->device_stats);
+       xpt_action(ccb);
+       return (0);
+}
+
+static int
+sgerror(union ccb *ccb, uint32_t cam_flags, uint32_t sense_flags)
+{
+       struct cam_periph *periph;
+       struct sg_softc *softc;
+
+       periph = xpt_path_periph(ccb->ccb_h.path);
+       softc = (struct sg_softc *)periph->softc;
+
+       return (cam_periph_error(ccb, cam_flags, sense_flags,
+                                &softc->saved_ccb));
+}
+
+static void
+sg_scsiio_status(struct ccb_scsiio *csio, u_short *hoststat, u_short *drvstat)
+{
+       int status;
+
+       status = csio->ccb_h.status;
+
+       switch (status & CAM_STATUS_MASK) {
+       case CAM_REQ_CMP:
+               *hoststat = DID_OK;
+               *drvstat = 0;
+               break;
+       case CAM_REQ_CMP_ERR:
+               *hoststat = DID_ERROR;
+               *drvstat = 0;
+               break;
+       case CAM_REQ_ABORTED:
+               *hoststat = DID_ABORT;
+               *drvstat = 0;
+               break;
+       case CAM_REQ_INVALID:
+               *hoststat = DID_ERROR;
+               *drvstat = DRIVER_INVALID;
+               break;
+       case CAM_DEV_NOT_THERE:
+               *hoststat = DID_BAD_TARGET;
+               *drvstat = 0;
+               break;
+       case CAM_SEL_TIMEOUT:
+               *hoststat = DID_NO_CONNECT;
+               *drvstat = 0;
+               break;
+       case CAM_CMD_TIMEOUT:
+               *hoststat = DID_TIME_OUT;
+               *drvstat = 0;
+               break;
+       case CAM_SCSI_STATUS_ERROR:
+               *hoststat = DID_ERROR;
+               *drvstat = 0;
+               break;
+       case CAM_SCSI_BUS_RESET:
+               *hoststat = DID_RESET;
+               *drvstat = 0;
+               break;
+       case CAM_UNCOR_PARITY:
+               *hoststat = DID_PARITY;
+               *drvstat = 0;
+               break;
+       case CAM_SCSI_BUSY:
+               *hoststat = DID_BUS_BUSY;
+               *drvstat = 0;
+               break;
+       default:
+               *hoststat = DID_ERROR;
+               *drvstat = DRIVER_ERROR;
+       }
+
+       if (status & CAM_AUTOSNS_VALID)
+               *drvstat = DRIVER_SENSE;
+}
+
+static int
+scsi_group_len(u_char cmd)
+{
+       int len[] = {6, 10, 10, 12, 12, 12, 10, 10};
+       int group;
+
+       group = (cmd >> 5) & 0x7;
+       return (len[group]);
+}
diff --git a/sys/bus/cam/scsi/scsi_sg.h b/sys/bus/cam/scsi/scsi_sg.h
new file mode 100644 (file)
index 0000000..f56fbdf
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Structures and definitions for SCSI commands to the SG passthrough device.
+ *
+ * $FreeBSD: src/sys/cam/scsi/scsi_sg.h,v 1.2 2007/04/10 20:03:42 scottl Exp $
+ */
+
+#ifndef _SCSI_SG_H
+#define _SCSI_SG_H
+
+#define SGIOC  '"'
+#define SG_SET_TIMEOUT         _IO(SGIOC, 0x01)
+#define SG_GET_TIMEOUT         _IO(SGIOC, 0x02)
+#define SG_EMULATED_HOST       _IO(SGIOC, 0x03)
+#define SG_SET_TRANSFORM       _IO(SGIOC, 0x04)
+#define SG_GET_TRANSFORM       _IO(SGIOC, 0x05)
+#define SG_GET_COMMAND_Q       _IO(SGIOC, 0x70)
+#define SG_SET_COMMAND_Q       _IO(SGIOC, 0x71)
+#define SG_GET_RESERVED_SIZE   _IO(SGIOC, 0x72)
+#define SG_SET_RESERVED_SIZE   _IO(SGIOC, 0x75)
+#define SG_GET_SCSI_ID         _IO(SGIOC, 0x76)
+#define SG_SET_FORCE_LOW_DMA   _IO(SGIOC, 0x79)
+#define SG_GET_LOW_DMA         _IO(SGIOC, 0x7a)
+#define SG_SET_FORCE_PACK_ID   _IO(SGIOC, 0x7b)
+#define SG_GET_PACK_ID         _IO(SGIOC, 0x7c)
+#define SG_GET_NUM_WAITING     _IO(SGIOC, 0x7d)
+#define SG_SET_DEBUG           _IO(SGIOC, 0x7e)
+#define SG_GET_SG_TABLESIZE    _IO(SGIOC, 0x7f)
+#define SG_GET_VERSION_NUM     _IO(SGIOC, 0x82)
+#define SG_NEXT_CMD_LEN                _IO(SGIOC, 0x83)
+#define SG_SCSI_RESET          _IO(SGIOC, 0x84)
+#define SG_IO                  _IO(SGIOC, 0x85)
+#define SG_GET_REQUEST_TABLE   _IO(SGIOC, 0x86)
+#define SG_SET_KEEP_ORPHAN     _IO(SGIOC, 0x87)
+#define SG_GET_KEEP_ORPHAN     _IO(SGIOC, 0x88)
+#define SG_GET_ACCESS_COUNT    _IO(SGIOC, 0x89)
+
+struct sg_io_hdr {
+       int             interface_id;
+       int             dxfer_direction;
+       u_char          cmd_len;
+       u_char          mx_sb_len;
+       u_short         iovec_count;
+       u_int           dxfer_len;
+       void            *dxferp;
+       u_char          *cmdp;
+       u_char          *sbp;
+       u_int           timeout;
+       u_int           flags;
+       int             pack_id;
+       void            *usr_ptr;
+       u_char          status;
+       u_char          masked_status;
+       u_char          msg_status;
+       u_char          sb_len_wr;
+       u_short         host_status;
+       u_short         driver_status;
+       int             resid;
+       u_int           duration;
+       u_int           info;
+};
+
+#define SG_DXFER_NONE          -1
+#define SG_DXFER_TO_DEV                -2
+#define SG_DXFER_FROM_DEV      -3
+#define SG_DXFER_TO_FROM_DEV   -4
+#define SG_DXFER_UNKNOWN       -5
+
+#define SG_MAX_SENSE            16
+
+struct sg_header {
+       int             pack_len;
+       int             reply_len;
+       int             pack_id;
+       int             result;
+       u_int           twelve_byte:1;
+       u_int           target_status:5;
+       u_int           host_status:8;
+       u_int           driver_status:8;
+       u_int           other_flags:10;
+       u_char          sense_buffer[SG_MAX_SENSE];
+};
+
+struct sg_scsi_id {
+       int             host_no;
+       int             channel;
+       int             scsi_id;
+       int             lun;
+       int             scsi_type;
+       short           h_cmd_per_lun;
+       short           d_queue_depth;
+       int             unused[2];
+};
+
+struct scsi_idlun {
+       uint32_t        dev_id;
+       uint32_t        host_unique_id;
+};
+
+/*
+ * Host codes
+ */
+#define DID_OK         0x00    /* OK */
+#define DID_NO_CONNECT 0x01    /* timeout during connect */
+#define DID_BUS_BUSY   0x02    /* timeout during command */
+#define DID_TIME_OUT   0x03    /* other timeout */
+#define DID_BAD_TARGET 0x04    /* bad target */
+#define DID_ABORT      0x05    /* abort */
+#define DID_PARITY     0x06    /* parity error */
+#define DID_ERROR      0x07    /* internal error */
+#define DID_RESET      0x08    /* reset by somebody */
+#define DID_BAD_INTR   0x09    /* unexpected interrupt */
+#define DID_PASSTHROUGH        0x0a    /* passthrough */
+#define DID_SOFT_ERROR 0x0b    /* low driver wants retry */
+#define DID_IMM_RETRY  0x0c    /* retry without decreasing retrycnt */
+
+/*
+ * Driver codes
+ */
+#define DRIVER_OK      0x00
+#define DRIVER_BUSY    0x01
+#define DRIVER_SOFT    0x02
+#define DRIVER_MEDIA   0x03
+#define DRIVER_ERROR   0x04
+
+#define DRIVER_INVALID 0x05
+#define DRIVER_TIMEOUT 0x06
+#define DRIVER_HARD    0x07
+#define DRIVER_SENSE   0x08
+
+#define SUGGEST_RETRY  0x10
+#define SUGGEST_ABORT  0x20
+#define SUGGEST_REMAP  0x30
+#define SUGGEST_DIE    0x40
+#define SUGGEST_SENSE  0x80
+#define SUGGEST_IS_OK  0xff
+
+#define DRIVER_MASK    0x0f
+#define SUGGEST_MASK   0xf0
+
+/* Other definitions */
+/* HZ isn't always available, so simulate it */
+#define SG_DEFAULT_HZ          1000
+#define SG_DEFAULT_TIMEOUT     (60*SG_DEFAULT_HZ)
+
+#endif /* !_SCSI_SG_H */
index 386fe45..8205413 100644 (file)
@@ -58,6 +58,7 @@ bus/cam/scsi/scsi_low_pisa.c          optional nsp
 bus/cam/scsi/scsi_low_pisa.c           optional stg
 bus/cam/scsi/scsi_pass.c               optional pass
 bus/cam/scsi/scsi_ses.c                        optional ses
+bus/cam/scsi/scsi_sg.c                 optional sg
 bus/cam/scsi/scsi_target.c             optional targ
 bus/cam/scsi/scsi_targ_bh.c            optional targbh
 
index 07aaa25..ed5a573 100644 (file)
@@ -144,6 +144,7 @@ device              da              # Direct Access (disks)
 device         sa              # Sequential Access (tape etc)
 device         cd              # CD
 device         pass            # Passthrough device (direct SCSI access)
+device         sg              # Passthrough device (linux scsi generic)
 
 # RAID controllers interfaced to the SCSI subsystem
 device         asr             # DPT SmartRAID V, VI and Adaptec SCSI RAID
index f010c6b..5e2cf4c 100644 (file)
 #define        LINUX_IOCTL_DISK_MAX    LINUX_BLKSSZGET
 
 /*
+ * hdio
+ */
+#define LINUX_HDIO_GET_GEO     0x0301
+#define LINUX_HDIO_GET_IDENTITY 0x030D /* not yet implemented */
+#define LINUX_HDIO_GET_GEO_BIG 0x0330
+
+#define LINUX_IOCTL_HDIO_MIN   LINUX_HDIO_GET_GEO
+#define LINUX_IOCTL_HDIO_MAX   LINUX_HDIO_GET_GEO_BIG
+
+/*
  * cdrom
  */
-#define        LINUX_CDROMPAUSE        0x5301 
-#define        LINUX_CDROMRESUME       0x5302
-#define        LINUX_CDROMPLAYMSF      0x5303
-#define        LINUX_CDROMPLAYTRKIND   0x5304
-#define        LINUX_CDROMREADTOCHDR   0x5305
-#define        LINUX_CDROMREADTOCENTRY 0x5306
-#define        LINUX_CDROMSTOP         0x5307
-#define        LINUX_CDROMSTART        0x5308
-#define        LINUX_CDROMEJECT        0x5309
-#define        LINUX_CDROMVOLCTRL      0x530a
-#define        LINUX_CDROMSUBCHNL      0x530b
-#define        LINUX_CDROMREADMODE2    0x530c
-#define        LINUX_CDROMREADMODE1    0x530d
-#define        LINUX_CDROMREADAUDIO    0x530e
-#define        LINUX_CDROMEJECT_SW     0x530f
-#define        LINUX_CDROMMULTISESSION 0x5310
-#define        LINUX_CDROM_GET_UPC     0x5311
-#define        LINUX_CDROMRESET        0x5312
-#define        LINUX_CDROMVOLREAD      0x5313
-#define        LINUX_CDROMREADRAW      0x5314
-#define        LINUX_CDROMREADCOOKED   0x5315
-#define        LINUX_CDROMSEEK         0x5316
-#define        LINUX_CDROMPLAYBLK      0x5317
-#define        LINUX_CDROMREADALL      0x5318
-#define        LINUX_CDROMCLOSETRAY    0x5319
-#define        LINUX_CDROMLOADFROMSLOT 0x531a
+#define        LINUX_CDROMPAUSE                0x5301
+#define        LINUX_CDROMRESUME               0x5302
+#define        LINUX_CDROMPLAYMSF              0x5303
+#define        LINUX_CDROMPLAYTRKIND           0x5304
+#define        LINUX_CDROMREADTOCHDR           0x5305
+#define        LINUX_CDROMREADTOCENTRY         0x5306
+#define        LINUX_CDROMSTOP                 0x5307
+#define        LINUX_CDROMSTART                0x5308
+#define        LINUX_CDROMEJECT                0x5309
+#define        LINUX_CDROMVOLCTRL              0x530a
+#define        LINUX_CDROMSUBCHNL              0x530b
+#define        LINUX_CDROMREADMODE2            0x530c
+#define        LINUX_CDROMREADMODE1            0x530d
+#define        LINUX_CDROMREADAUDIO            0x530e
+#define        LINUX_CDROMEJECT_SW             0x530f
+#define        LINUX_CDROMMULTISESSION         0x5310
+#define        LINUX_CDROM_GET_UPC             0x5311
+#define        LINUX_CDROMRESET                0x5312
+#define        LINUX_CDROMVOLREAD              0x5313
+#define        LINUX_CDROMREADRAW              0x5314
+#define        LINUX_CDROMREADCOOKED           0x5315
+#define        LINUX_CDROMSEEK                 0x5316
+#define        LINUX_CDROMPLAYBLK              0x5317
+#define        LINUX_CDROMREADALL              0x5318
+#define        LINUX_CDROMCLOSETRAY            0x5319
+#define        LINUX_CDROMLOADFROMSLOT         0x531a
+#define LINUX_CDROMGETSPINDOWN         0x531d
+#define LINUX_CDROMSETSPINDOWN         0x531e
+#define LINUX_CDROM_SET_OPTIONS                0x5320
+#define LINUX_CDROM_CLEAR_OPTIONS      0x5321
+#define LINUX_CDROM_SELECT_SPEED       0x5322
+#define LINUX_CDROM_SELECT_DISC                0x5323
+#define LINUX_CDROM_MEDIA_CHANGED      0x5325
+#define LINUX_CDROM_DRIVE_STATUS       0x5326
+#define LINUX_CDROM_DISC_STATUS                0x5327
+#define LINUX_CDROM_CHANGER_NSLOTS     0x5328
+#define LINUX_CDROM_LOCKDOOR           0x5329
+#define LINUX_CDROM_DEBUG              0x5330
+#define LINUX_CDROM_GET_CAPABILITY     0x5331
+#define LINUX_CDROMAUDIOBUFSIZ         0x5382
+#define LINUX_SCSI_GET_IDLUN           0x5382
+#define LINUX_SCSI_GET_BUS_NUMBER      0x5386
+#define LINUX_DVD_READ_STRUCT          0x5390
+#define LINUX_DVD_WRITE_STRUCT         0x5391
+#define LINUX_DVD_AUTH                 0x5392
+#define LINUX_CDROM_SEND_PACKET                0x5393
+#define LINUX_CDROM_NEXT_WRITABLE      0x5394
+#define LINUX_CDROM_LAST_WRITTEN       0x5395
 
 #define        LINUX_IOCTL_CDROM_MIN   LINUX_CDROMPAUSE
-#define        LINUX_IOCTL_CDROM_MAX   LINUX_CDROMLOADFROMSLOT
+#define        LINUX_IOCTL_CDROM_MAX   LINUX_CDROM_LAST_WRITTEN
 
 #define        LINUX_CDROM_LBA         0x01
 #define        LINUX_CDROM_MSF         0x02
 
+#define LINUX_DVD_LU_SEND_AGID         0
+#define LINUX_DVD_HOST_SEND_CHALLENGE  1
+#define LINUX_DVD_LU_SEND_KEY1         2
+#define LINUX_DVD_LU_SEND_CHALLENGE    3
+#define LINUX_DVD_HOST_SEND_KEY2       4
+#define LINUX_DVD_AUTH_ESTABLISHED     5
+#define LINUX_DVD_AUTH_FAILURE         6
+#define LINUX_DVD_LU_SEND_TITLE_KEY    7
+#define LINUX_DVD_LU_SEND_ASF          8
+#define LINUX_DVD_INVALIDATE_AGID      9
+#define LINUX_DVD_LU_SEND_RPC_STATE    10
+#define LINUX_DVD_HOST_SEND_RPC_STATE  11
+
+/*
+ * SG
+ */
+#define        LINUX_SG_SET_TIMEOUT            0x2201
+#define        LINUX_SG_GET_TIMEOUT            0x2202
+#define        LINUX_SG_EMULATED_HOST          0x2203
+#define        LINUX_SG_SET_TRANSFORM          0x2204
+#define        LINUX_SG_GET_TRANSFORM          0x2205
+#define        LINUX_SG_GET_COMMAND_Q          0x2270
+#define        LINUX_SG_SET_COMMAND_Q          0x2271
+#define        LINUX_SG_SET_RESERVED_SIZE      0x2275
+#define        LINUX_SG_GET_RESERVED_SIZE      0x2272
+#define        LINUX_SG_GET_SCSI_ID            0x2276
+#define        LINUX_SG_SET_FORCE_LOW_DMA      0x2279
+#define        LINUX_SG_GET_LOW_DMA            0x227a
+#define        LINUX_SG_SET_FORCE_PACK_ID      0x227b
+#define        LINUX_SG_GET_PACK_ID            0x227c
+#define        LINUX_SG_GET_NUM_WAITING        0x227d
+#define        LINUX_SG_SET_DEBUG              0x227e
+#define        LINUX_SG_GET_SG_TABLESIZE       0x227f
+#define        LINUX_SG_GET_VERSION_NUM        0x2282
+#define        LINUX_SG_NEXT_CMD_LEN           0x2283
+#define        LINUX_SG_SCSI_RESET             0x2284
+#define        LINUX_SG_IO                     0x2285
+#define        LINUX_SG_GET_REQUEST_TABLE      0x2286
+#define        LINUX_SG_SET_KEEP_ORPHAN        0x2287
+#define        LINUX_SG_GET_KEEP_ORPHAN        0x2288
+#define        LINUX_SG_GET_ACCESS_COUNT       0x2289
+
+#define        LINUX_IOCTL_SG_MIN      0x2200
+#define        LINUX_IOCTL_SG_MAX      0x22ff
+
+/*
+ * VFAT
+ */
+#define        LINUX_VFAT_READDIR_BOTH 0x7201
+
+#define        LINUX_IOCTL_VFAT_MIN    LINUX_VFAT_READDIR_BOTH
+#define        LINUX_IOCTL_VFAT_MAX    LINUX_VFAT_READDIR_BOTH
 /*
  * console
  */