Add SG_IO ioctl to cciss The cciss driver is actually capable of sending SCSI CDB to the controller. So it would only be logical to have it accepting SG_IO ioctl, too. Now we can use all the nice sg_utils and get a persistent device name for free, too. Signed-off-by: Hannes Reinecke diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 0c716ee..33e7b83 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -45,6 +45,9 @@ #include #include #include #include +#include +#include +#include #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) #define DRIVER_NAME "HP CISS Driver (v 3.6.14)" @@ -1152,6 +1155,163 @@ #endif kfree(ioc); return status; } + case SG_IO: { + struct sg_io_hdr hdr; + CommandList_struct *c; + char *buff = NULL; + u64bit temp64; + unsigned long flags; + DECLARE_COMPLETION_ONSTACK(wait); + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + if (copy_from_user(&hdr, argp, sizeof(hdr))) + return -EFAULT; + + if (hdr.interface_id != 'S') + return -EINVAL; + + /* cciss only supports 16-byte commands */ + if (hdr.cmd_len > 16) + return -EINVAL; + + /* We don't support proper scatter-gather (yet) */ + if (hdr.iovec_count) + return -EINVAL; + + if ((hdr.dxfer_len < 1) && + (hdr.dxfer_direction != SG_DXFER_NONE)) + return -EINVAL; + + if (hdr.dxfer_len > 0) { + buff = kmalloc(hdr.dxfer_len, GFP_KERNEL); + if (buff == NULL) + return -EFAULT; + } + if ((hdr.dxfer_direction == SG_DXFER_TO_DEV) || + (hdr.dxfer_direction == SG_DXFER_TO_FROM_DEV)) { + /* Copy the data into the buffer we created */ + if (copy_from_user (buff, hdr.dxferp, + hdr.dxfer_len)) { + kfree(buff); + return -EFAULT; + } + } else + memset(buff, 0, hdr.dxfer_len); + + if ((c = cmd_alloc(host, 0)) == NULL) { + kfree(buff); + return -ENOMEM; + } + + /* Copy CDB */ + if (copy_from_user(c->Request.CDB, hdr.cmdp, hdr.cmd_len)) + return -EFAULT; + + /* Fill in the command type */ + c->cmd_type = CMD_IOCTL_PEND; + /* Fill in Command Header */ + c->Header.ReplyQueue = 0; + if (hdr.dxfer_len > 0) { + c->Header.SGList = 1; + c->Header.SGTotal = 1; + } else { + c->Header.SGList = 0; + c->Header.SGTotal = 0; + } + /* Default to LUN the ioctl was directed to */ + c->Header.LUN.LogDev.VolId = drv->LunID & 0x3FFFFFFF; + c->Header.LUN.LogDev.Mode = 0x01; /* Logical volume */ + c->Header.Tag.lower = c->busaddr; + + /* Fill in Request block */ + c->Request.CDBLen = hdr.cmd_len; + c->Request.Type.Type = TYPE_CMD; + c->Request.Type.Attribute = ATTR_SIMPLE; + switch(hdr.dxfer_direction) { + case SG_DXFER_NONE: + c->Request.Type.Direction = XFER_NONE; + break; + case SG_DXFER_TO_DEV: + c->Request.Type.Direction = XFER_WRITE; + break; + case SG_DXFER_FROM_DEV: + c->Request.Type.Direction = XFER_READ; + break; + case SG_DXFER_TO_FROM_DEV: + c->Request.Type.Direction = XFER_RSVD; + break; + } + c->Request.Timeout = hdr.timeout; + + /* Fill in the scatter gather information */ + if (hdr.dxfer_len > 0) { + temp64.val = pci_map_single(host->pdev, buff, + hdr.dxfer_len, + PCI_DMA_BIDIRECTIONAL); + c->SG[0].Addr.lower = temp64.val32.lower; + c->SG[0].Addr.upper = temp64.val32.upper; + c->SG[0].Len = hdr.dxfer_len; + c->SG[0].Ext = 0; + } + c->waiting = &wait; + + /* Put the request on the tail of the request queue */ + spin_lock_irqsave(CCISS_LOCK(ctlr), flags); + addQ(&host->reqQ, c); + host->Qdepth++; + start_io(host); + spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); + + wait_for_completion(&wait); + + /* unlock the buffers from DMA */ + temp64.val32.lower = c->SG[0].Addr.lower; + temp64.val32.upper = c->SG[0].Addr.upper; + pci_unmap_single(host->pdev, (dma_addr_t) temp64.val, + hdr.dxfer_len, + PCI_DMA_BIDIRECTIONAL); + + /* Copy the error information out */ + hdr.status = c->err_info->ScsiStatus; + if (c->err_info->SenseLen && hdr.mx_sb_len > 0) { + int sense_len = c->err_info->SenseLen; + + if (sense_len > hdr.mx_sb_len) + sense_len = hdr.mx_sb_len; + + if (copy_to_user(hdr.sbp, c->err_info->SenseInfo, + sense_len)) { + kfree(buff); + cmd_free(host, c, 0); + return -EFAULT; + } + hdr.sb_len_wr = sense_len; + } + hdr.resid = c->err_info->ResidualCnt; + /* Copy out the header */ + if (copy_to_user(argp, &hdr, sizeof(hdr))) { + kfree(buff); + cmd_free(host, c, 0); + return -EFAULT; + } + + if ((hdr.dxfer_direction == SG_DXFER_FROM_DEV) || + (hdr.dxfer_direction == SG_DXFER_TO_FROM_DEV)) { + /* Copy the data out to the buffer we created */ + if (copy_to_user + (hdr.dxferp, buff, hdr.dxfer_len)) { + kfree(buff); + cmd_free(host, c, 0); + return -EFAULT; + } + } + + kfree(buff); + cmd_free(host, c, 0); + return 0; + } default: return -ENOTTY; }