static int cciss_ioctl(struct inode *inode, struct file *filep,
unsigned int cmd, unsigned long arg)
{
struct block_device *bdev = inode->i_bdev;
struct gendisk *disk = bdev->bd_disk;
ctlr_info_t *host = get_host(disk);
drive_info_struct *drv = get_drv(disk);
int ctlr = host->ctlr;
void __user *argp = (void __user *)arg;
#ifdef CCISS_DEBUG
printk(KERN_DEBUG "cciss_ioctl: Called with cmd=%x %lx\n", cmd, arg);
#endif /* CCISS_DEBUG */
switch (cmd) {
case CCISS_GETPCIINFO:
{
cciss_pci_info_struct pciinfo;
if (!arg)
return -EINVAL;
pciinfo.domain = pci_domain_nr(host->pdev->bus);
pciinfo.bus = host->pdev->bus->number;
pciinfo.dev_fn = host->pdev->devfn;
pciinfo.board_id = host->board_id;
if (copy_to_user
(argp, &pciinfo, sizeof(cciss_pci_info_struct)))
return -EFAULT;
return 0;
}
case CCISS_GETINTINFO:
{
cciss_coalint_struct intinfo;
if (!arg)
return -EINVAL;
intinfo.delay =
readl(&host->cfgtable->HostWrite.CoalIntDelay);
intinfo.count =
readl(&host->cfgtable->HostWrite.CoalIntCount);
if (copy_to_user
(argp, &intinfo, sizeof(cciss_coalint_struct)))
return -EFAULT;
return 0;
}
case CCISS_SETINTINFO:
{
cciss_coalint_struct intinfo;
unsigned long flags;
int i;
if (!arg)
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (copy_from_user
(&intinfo, argp, sizeof(cciss_coalint_struct)))
return -EFAULT;
if ((intinfo.delay == 0) && (intinfo.count == 0))
{
// printk("cciss_ioctl: delay and count cannot be 0\n");
return -EINVAL;
}
spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
/* Update the field, and then ring the doorbell */
writel(intinfo.delay,
&(host->cfgtable->HostWrite.CoalIntDelay));
writel(intinfo.count,
&(host->cfgtable->HostWrite.CoalIntCount));
writel(CFGTBL_ChangeReq, host->vaddr + SA5_DOORBELL);
for (i = 0; i < MAX_IOCTL_CONFIG_WAIT; i++) {
if (!(readl(host->vaddr + SA5_DOORBELL)
& CFGTBL_ChangeReq))
break;
/* delay and try again */
udelay(1000);
}
spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
if (i >= MAX_IOCTL_CONFIG_WAIT)
return -EAGAIN;
return 0;
}
case CCISS_GETNODENAME:
{
NodeName_type NodeName;
int i;
if (!arg)
return -EINVAL;
for (i = 0; i < 16; i++)
NodeName[i] =
readb(&host->cfgtable->ServerName[i]);
if (copy_to_user(argp, NodeName, sizeof(NodeName_type)))
return -EFAULT;
return 0;
}
case CCISS_SETNODENAME:
{
NodeName_type NodeName;
unsigned long flags;
int i;
if (!arg)
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (copy_from_user
(NodeName, argp, sizeof(NodeName_type)))
return -EFAULT;
spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
/* Update the field, and then ring the doorbell */
for (i = 0; i < 16; i++)
writeb(NodeName[i],
&host->cfgtable->ServerName[i]);
writel(CFGTBL_ChangeReq, host->vaddr + SA5_DOORBELL);
for (i = 0; i < MAX_IOCTL_CONFIG_WAIT; i++) {
if (!(readl(host->vaddr + SA5_DOORBELL)
& CFGTBL_ChangeReq))
break;
/* delay and try again */
udelay(1000);
}
spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
if (i >= MAX_IOCTL_CONFIG_WAIT)
return -EAGAIN;
return 0;
}
case CCISS_GETHEARTBEAT:
{
Heartbeat_type heartbeat;
if (!arg)
return -EINVAL;
heartbeat = readl(&host->cfgtable->HeartBeat);
if (copy_to_user
(argp, &heartbeat, sizeof(Heartbeat_type)))
return -EFAULT;
return 0;
}
case CCISS_GETBUSTYPES:
{
BusTypes_type BusTypes;
if (!arg)
return -EINVAL;
BusTypes = readl(&host->cfgtable->BusTypes);
if (copy_to_user
(argp, &BusTypes, sizeof(BusTypes_type)))
return -EFAULT;
return 0;
}
case CCISS_GETFIRMVER:
{
FirmwareVer_type firmware;
if (!arg)
return -EINVAL;
memcpy(firmware, host->firm_ver, 4);
if (copy_to_user
(argp, firmware, sizeof(FirmwareVer_type)))
return -EFAULT;
return 0;
}
case CCISS_GETDRIVVER:
{
DriverVer_type DriverVer = DRIVER_VERSION;
if (!arg)
return -EINVAL;
if (copy_to_user
(argp, &DriverVer, sizeof(DriverVer_type)))
return -EFAULT;
return 0;
}
case CCISS_REVALIDVOLS:
return rebuild_lun_table(host, NULL);
case CCISS_GETLUNINFO:{
LogvolInfo_struct luninfo;
luninfo.LunID = drv->LunID;
luninfo.num_opens = drv->usage_count;
luninfo.num_parts = 0;
if (copy_to_user(argp, &luninfo,
sizeof(LogvolInfo_struct)))
return -EFAULT;
return 0;
}
case CCISS_DEREGDISK:
return rebuild_lun_table(host, disk);
case CCISS_REGNEWD:
return rebuild_lun_table(host, NULL);
case CCISS_PASSTHRU:
{
IOCTL_Command_struct iocommand;
CommandList_struct *c;
char *buff = NULL;
u64bit temp64;
unsigned long flags;
DECLARE_COMPLETION_ONSTACK(wait);
if (!arg)
return -EINVAL;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
if (copy_from_user
(&iocommand, argp, sizeof(IOCTL_Command_struct)))
return -EFAULT;
if ((iocommand.buf_size < 1) &&
(iocommand.Request.Type.Direction != XFER_NONE)) {
return -EINVAL;
}
#if 0 /* 'buf_size' member is 16-bits, and always smaller than kmalloc limit */
/* Check kmalloc limits */
if (iocommand.buf_size > 128000)
return -EINVAL;
#endif
if (iocommand.buf_size > 0) {
buff = kmalloc(iocommand.buf_size, GFP_KERNEL);
if (buff == NULL)
return -EFAULT;
}
if (iocommand.Request.Type.Direction == XFER_WRITE) {
/* Copy the data into the buffer we created */
if (copy_from_user
(buff, iocommand.buf, iocommand.buf_size)) {
kfree(buff);
return -EFAULT;
}
} else {
memset(buff, 0, iocommand.buf_size);
}
if ((c = cmd_alloc(host, 0)) == NULL) {
kfree(buff);
return -ENOMEM;
}
// Fill in the command type
c->cmd_type = CMD_IOCTL_PEND;
// Fill in Command Header
c->Header.ReplyQueue = 0; // unused in simple mode
if (iocommand.buf_size > 0) // buffer to fill
{
c->Header.SGList = 1;
c->Header.SGTotal = 1;
} else // no buffers to fill
{
c->Header.SGList = 0;
c->Header.SGTotal = 0;
}
c->Header.LUN = iocommand.LUN_info;
c->Header.Tag.lower = c->busaddr; // use the kernel address the cmd block for tag
// Fill in Request block
c->Request = iocommand.Request;
// Fill in the scatter gather information
if (iocommand.buf_size > 0) {
temp64.val = pci_map_single(host->pdev, buff,
iocommand.buf_size,
PCI_DMA_BIDIRECTIONAL);
c->SG[0].Addr.lower = temp64.val32.lower;
c->SG[0].Addr.upper = temp64.val32.upper;
c->SG[0].Len = iocommand.buf_size;
c->SG[0].Ext = 0; // we are not chaining
}
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,
iocommand.buf_size,
PCI_DMA_BIDIRECTIONAL);
/* Copy the error information out */
iocommand.error_info = *(c->err_info);
if (copy_to_user
(argp, &iocommand, sizeof(IOCTL_Command_struct))) {
kfree(buff);
cmd_free(host, c, 0);
return -EFAULT;
}
if (iocommand.Request.Type.Direction == XFER_READ) {
/* Copy the data out of the buffer we created */
if (copy_to_user
(iocommand.buf, buff, iocommand.buf_size)) {
kfree(buff);
cmd_free(host, c, 0);
return -EFAULT;
}
}
kfree(buff);
cmd_free(host, c, 0);
return 0;
}
case CCISS_BIG_PASSTHRU:{
BIG_IOCTL_Command_struct *ioc;
CommandList_struct *c;
unsigned char **buff = NULL;
int *buff_size = NULL;
u64bit temp64;
unsigned long flags;
BYTE sg_used = 0;
int status = 0;
int i;
DECLARE_COMPLETION_ONSTACK(wait);
__u32 left;
__u32 sz;
BYTE __user *data_ptr;
if (!arg)
return -EINVAL;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
ioc = (BIG_IOCTL_Command_struct *)
kmalloc(sizeof(*ioc), GFP_KERNEL);
if (!ioc) {
status = -ENOMEM;
goto cleanup1;
}
if (copy_from_user(ioc, argp, sizeof(*ioc))) {
status = -EFAULT;
goto cleanup1;
}
if ((ioc->buf_size < 1) &&
(ioc->Request.Type.Direction != XFER_NONE)) {
status = -EINVAL;
goto cleanup1;
}
/* Check kmalloc limits using all SGs */
if (ioc->malloc_size > MAX_KMALLOC_SIZE) {
status = -EINVAL;
goto cleanup1;
}
if (ioc->buf_size > ioc->malloc_size * MAXSGENTRIES) {
status = -EINVAL;
goto cleanup1;
}
buff =
kzalloc(MAXSGENTRIES * sizeof(char *), GFP_KERNEL);
if (!buff) {
status = -ENOMEM;
goto cleanup1;
}
buff_size = kmalloc(MAXSGENTRIES * sizeof(int),
GFP_KERNEL);
if (!buff_size) {
status = -ENOMEM;
goto cleanup1;
}
left = ioc->buf_size;
data_ptr = ioc->buf;
while (left) {
sz = (left >
ioc->malloc_size) ? ioc->
malloc_size : left;
buff_size[sg_used] = sz;
buff[sg_used] = kmalloc(sz, GFP_KERNEL);
if (buff[sg_used] == NULL) {
status = -ENOMEM;
goto cleanup1;
}
if (ioc->Request.Type.Direction == XFER_WRITE) {
if (copy_from_user
(buff[sg_used], data_ptr, sz)) {
status = -ENOMEM;
goto cleanup1;
}
} else {
memset(buff[sg_used], 0, sz);
}
left -= sz;
data_ptr += sz;
sg_used++;
}
if ((c = cmd_alloc(host, 0)) == NULL) {
status = -ENOMEM;
goto cleanup1;
}
c->cmd_type = CMD_IOCTL_PEND;
c->Header.ReplyQueue = 0;
if (ioc->buf_size > 0) {
c->Header.SGList = sg_used;
c->Header.SGTotal = sg_used;
} else {
c->Header.SGList = 0;
c->Header.SGTotal = 0;
}
c->Header.LUN = ioc->LUN_info;
c->Header.Tag.lower = c->busaddr;
c->Request = ioc->Request;
if (ioc->buf_size > 0) {
int i;
for (i = 0; i < sg_used; i++) {
temp64.val =
pci_map_single(host->pdev, buff[i],
buff_size[i],
PCI_DMA_BIDIRECTIONAL);
c->SG[i].Addr.lower =
temp64.val32.lower;
c->SG[i].Addr.upper =
temp64.val32.upper;
c->SG[i].Len = buff_size[i];
c->SG[i].Ext = 0; /* we are not chaining */
}
}
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 */
for (i = 0; i < sg_used; i++) {
temp64.val32.lower = c->SG[i].Addr.lower;
temp64.val32.upper = c->SG[i].Addr.upper;
pci_unmap_single(host->pdev,
(dma_addr_t) temp64.val, buff_size[i],
PCI_DMA_BIDIRECTIONAL);
}
/* Copy the error information out */
ioc->error_info = *(c->err_info);
if (copy_to_user(argp, ioc, sizeof(*ioc))) {
cmd_free(host, c, 0);
status = -EFAULT;
goto cleanup1;
}
if (ioc->Request.Type.Direction == XFER_READ) {
/* Copy the data out of the buffer we created */
BYTE __user *ptr = ioc->buf;
for (i = 0; i < sg_used; i++) {
if (copy_to_user
(ptr, buff[i], buff_size[i])) {
cmd_free(host, c, 0);
status = -EFAULT;
goto cleanup1;
}
ptr += buff_size[i];
}
}
cmd_free(host, c, 0);
status = 0;
cleanup1:
if (buff) {
for (i = 0; i < sg_used; i++)
kfree(buff[i]);
kfree(buff);
}
kfree(buff_size);
kfree(ioc);
return status;
}
/* scsi_cmd_ioctl handles these, below, though some are not */
/* very meaningful for cciss. SG_IO is the main one people want. */
case SG_GET_VERSION_NUM:
case SG_SET_TIMEOUT:
case SG_GET_TIMEOUT:
case SG_GET_RESERVED_SIZE:
case SG_SET_RESERVED_SIZE:
case SG_EMULATED_HOST:
case SG_IO:
case SCSI_IOCTL_SEND_COMMAND:
return scsi_cmd_ioctl(filep, disk->queue, disk, cmd, argp);
/* scsi_cmd_ioctl would normally handle these, below, but */
/* they aren't a good fit for cciss, as CD-ROMs are */
/* not supported, and we don't have any bus/target/lun */
/* which we present to the kernel. */
case CDROM_SEND_PACKET:
case CDROMCLOSETRAY:
case CDROMEJECT:
case SCSI_IOCTL_GET_IDLUN:
case SCSI_IOCTL_GET_BUS_NUMBER:
default:
return -ENOTTY;
}
}
syntax highlighted by Code2HTML, v. 0.9.1