* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * Questions/Comments/Bugfixes to Cciss-discuss@lists.sourceforge.net
+ * Questions/Comments/Bugfixes to iss_storagedev@hp.com
*
*/
#include <linux/completion.h>
#define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
-#define DRIVER_NAME "Compaq CISS Driver (v 2.6.2)"
-#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,6,2)
+#define DRIVER_NAME "HP CISS Driver (v 2.6.4)"
+#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,6,4)
/* Embedded module documentation macros - see modules.h */
MODULE_AUTHOR("Hewlett-Packard Company");
-MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.2");
+MODULE_DESCRIPTION("Driver for HP Controller SA5xxx SA6xxx version 2.6.4");
MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400"
- " SA6i");
+ " SA6i P600");
MODULE_LICENSE("GPL");
#include "cciss_cmd.h"
0x0E11, 0x409D, 0, 0, 0},
{ PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC,
0x0E11, 0x4091, 0, 0, 0},
- { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC,
- 0x0E11, 0x409E, 0, 0, 0},
- { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC,
- 0x103C, 0x3211, 0, 0, 0},
+ { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSA,
+ 0x103C, 0x3225, 0, 0, 0},
{0,}
};
MODULE_DEVICE_TABLE(pci, cciss_pci_device_id);
{ 0x409C0E11, "Smart Array 6400", &SA5_access},
{ 0x409D0E11, "Smart Array 6400 EM", &SA5_access},
{ 0x40910E11, "Smart Array 6i", &SA5_access},
- { 0x409E0E11, "Smart Array 6422", &SA5_access},
- { 0x3211103C, "Smart Array V100", &SA5_access},
+ { 0x3225103C, "Smart Array P600", &SA5_access},
};
/* How long to wait (in millesconds) for board to go into simple mode */
/*define how many times we will try a command because of bus resets */
#define MAX_CMD_RETRIES 3
-#define READ_AHEAD 256
+#define READ_AHEAD 1024
#define NR_CMDS 384 /* #commands that can be outstanding */
#define MAX_CTLR 8
static void cciss_procinit(int i) {}
#endif /* CONFIG_PROC_FS */
+#ifdef CONFIG_COMPAT
+static long cciss_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg);
+#endif
+
static struct block_device_operations cciss_fops = {
.owner = THIS_MODULE,
.open = cciss_open,
.release = cciss_release,
.ioctl = cciss_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = cciss_compat_ioctl,
+#endif
.revalidate_disk= cciss_revalidate,
};
}
return c;
}
-#ifdef CONFIG_PROC_FS
#include "cciss_scsi.c" /* For SCSI tape support */
+#ifdef CONFIG_PROC_FS
+
/*
* Report information about this controller.
*/
-#define ENG_GIG 1048576000
+#define ENG_GIG 1000000000
#define ENG_GIG_FACTOR (ENG_GIG/512)
#define RAID_UNKNOWN 6
-static const char *raid_label[] = {"0","4","1(0+1)","5","5+1","ADG",
+static const char *raid_label[] = {"0","4","1(1+0)","5","5+1","ADG",
"UNKNOWN"};
static struct proc_dir_entry *proc_cciss;
ctlr_info_t *h = (ctlr_info_t*)data;
drive_info_struct *drv;
unsigned long flags;
- unsigned int vol_sz, vol_sz_frac;
+ sector_t vol_sz, vol_sz_frac;
ctlr = h->ctlr;
pos += size; len += size;
cciss_proc_tape_report(ctlr, buffer, &pos, &len);
for(i=0; i<=h->highest_lun; i++) {
- sector_t tmp;
drv = &h->drv[i];
if (drv->block_size == 0)
continue;
- vol_sz = drv->nr_blocks;
- sector_div(vol_sz, ENG_GIG_FACTOR);
-
- /*
- * Awkwardly do this:
- * vol_sz_frac =
- * (drv->nr_blocks%ENG_GIG_FACTOR)*100/ENG_GIG_FACTOR;
- */
- tmp = drv->nr_blocks;
- vol_sz_frac = sector_div(tmp, ENG_GIG_FACTOR);
-
- /* Now, vol_sz_frac = (drv->nr_blocks%ENG_GIG_FACTOR) */
+ vol_sz = drv->nr_blocks;
+ vol_sz_frac = sector_div(vol_sz, ENG_GIG_FACTOR);
vol_sz_frac *= 100;
sector_div(vol_sz_frac, ENG_GIG_FACTOR);
if (drv->raid_level > 5)
drv->raid_level = RAID_UNKNOWN;
size = sprintf(buffer+len, "cciss/c%dd%d:"
- "\t%4d.%02dGB\tRAID %s\n",
- ctlr, i, vol_sz,vol_sz_frac,
+ "\t%4u.%02uGB\tRAID %s\n",
+ ctlr, i, (int)vol_sz, (int)vol_sz_frac,
raid_label[drv->raid_level]);
pos += size; len += size;
}
/*
* Root is allowed to open raw volume zero even if it's not configured
- * so array config can still work. I don't think I really like this,
+ * so array config can still work. Root is also allowed to open any
+ * volume that has a LUN ID, so it can issue IOCTL to reread the
+ * disk information. I don't think I really like this
* but I'm already using way to many device nodes to claim another one
* for "raw controller".
*/
if (drv->nr_blocks == 0) {
- if (iminor(inode) != 0)
+ if (iminor(inode) != 0) { /* not node 0? */
+ /* if not node 0 make sure it is a partition = 0 */
+ if (iminor(inode) & 0x0f) {
return -ENXIO;
+ /* if it is, make sure we have a LUN ID */
+ } else if (drv->LunID == 0) {
+ return -ENXIO;
+ }
+ }
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
}
}
#ifdef CONFIG_COMPAT
-/* for AMD 64 bit kernel compatibility with 32-bit userland ioctls */
-extern long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg);
-extern int
-register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int,
- unsigned int, unsigned long, struct file *));
-extern int unregister_ioctl32_conversion(unsigned int cmd);
-
-static int cciss_ioctl32_passthru(unsigned int fd, unsigned cmd, unsigned long arg, struct file *file);
-static int cciss_ioctl32_big_passthru(unsigned int fd, unsigned cmd, unsigned long arg,
- struct file *file);
-
-typedef int (*handler_type) (unsigned int, unsigned int, unsigned long, struct file *);
-
-static struct ioctl32_map {
- unsigned int cmd;
- handler_type handler;
- int registered;
-} cciss_ioctl32_map[] = {
- { CCISS_GETPCIINFO, (handler_type) sys_ioctl, 0 },
- { CCISS_GETINTINFO, (handler_type) sys_ioctl, 0 },
- { CCISS_SETINTINFO, (handler_type) sys_ioctl, 0 },
- { CCISS_GETNODENAME, (handler_type) sys_ioctl, 0 },
- { CCISS_SETNODENAME, (handler_type) sys_ioctl, 0 },
- { CCISS_GETHEARTBEAT, (handler_type) sys_ioctl, 0 },
- { CCISS_GETBUSTYPES, (handler_type) sys_ioctl, 0 },
- { CCISS_GETFIRMVER, (handler_type) sys_ioctl, 0 },
- { CCISS_GETDRIVVER, (handler_type) sys_ioctl, 0 },
- { CCISS_REVALIDVOLS, (handler_type) sys_ioctl, 0 },
- { CCISS_PASSTHRU32, cciss_ioctl32_passthru, 0 },
- { CCISS_DEREGDISK, (handler_type) sys_ioctl, 0 },
- { CCISS_REGNEWDISK, (handler_type) sys_ioctl, 0 },
- { CCISS_REGNEWD, (handler_type) sys_ioctl, 0 },
- { CCISS_RESCANDISK, (handler_type) sys_ioctl, 0 },
- { CCISS_GETLUNINFO, (handler_type) sys_ioctl, 0 },
- { CCISS_BIG_PASSTHRU32, cciss_ioctl32_big_passthru, 0 },
-};
-#define NCCISS_IOCTL32_ENTRIES (sizeof(cciss_ioctl32_map) / sizeof(cciss_ioctl32_map[0]))
-static void register_cciss_ioctl32(void)
+
+static int do_ioctl(struct file *f, unsigned cmd, unsigned long arg)
{
- int i, rc;
-
- for (i=0; i < NCCISS_IOCTL32_ENTRIES; i++) {
- rc = register_ioctl32_conversion(
- cciss_ioctl32_map[i].cmd,
- cciss_ioctl32_map[i].handler);
- if (rc != 0) {
- printk(KERN_WARNING "cciss: failed to register "
- "32 bit compatible ioctl 0x%08x\n",
- cciss_ioctl32_map[i].cmd);
- cciss_ioctl32_map[i].registered = 0;
- } else
- cciss_ioctl32_map[i].registered = 1;
- }
+ int ret;
+ lock_kernel();
+ ret = cciss_ioctl(f->f_dentry->d_inode, f, cmd, arg);
+ unlock_kernel();
+ return ret;
}
-static void unregister_cciss_ioctl32(void)
+
+static int cciss_ioctl32_passthru(struct file *f, unsigned cmd, unsigned long arg);
+static int cciss_ioctl32_big_passthru(struct file *f, unsigned cmd, unsigned long arg);
+
+static long cciss_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg)
{
- int i, rc;
+ switch (cmd) {
+ case CCISS_GETPCIINFO:
+ case CCISS_GETINTINFO:
+ case CCISS_SETINTINFO:
+ case CCISS_GETNODENAME:
+ case CCISS_SETNODENAME:
+ case CCISS_GETHEARTBEAT:
+ case CCISS_GETBUSTYPES:
+ case CCISS_GETFIRMVER:
+ case CCISS_GETDRIVVER:
+ case CCISS_REVALIDVOLS:
+ case CCISS_DEREGDISK:
+ case CCISS_REGNEWDISK:
+ case CCISS_REGNEWD:
+ case CCISS_RESCANDISK:
+ case CCISS_GETLUNINFO:
+ return do_ioctl(f, cmd, arg);
- for (i=0; i < NCCISS_IOCTL32_ENTRIES; i++) {
- if (!cciss_ioctl32_map[i].registered)
- continue;
- rc = unregister_ioctl32_conversion(
- cciss_ioctl32_map[i].cmd);
- if (rc == 0) {
- cciss_ioctl32_map[i].registered = 0;
- continue;
- }
- printk(KERN_WARNING "cciss: failed to unregister "
- "32 bit compatible ioctl 0x%08x\n",
- cciss_ioctl32_map[i].cmd);
+ case CCISS_PASSTHRU32:
+ return cciss_ioctl32_passthru(f, cmd, arg);
+ case CCISS_BIG_PASSTHRU32:
+ return cciss_ioctl32_big_passthru(f, cmd, arg);
+
+ default:
+ return -ENOIOCTLCMD;
}
}
-int cciss_ioctl32_passthru(unsigned int fd, unsigned cmd, unsigned long arg,
- struct file *file)
+
+static int cciss_ioctl32_passthru(struct file *f, unsigned cmd, unsigned long arg)
{
IOCTL32_Command_struct __user *arg32 =
(IOCTL32_Command_struct __user *) arg;
if (err)
return -EFAULT;
- err = sys_ioctl(fd, CCISS_PASSTHRU, (unsigned long) p);
+ err = do_ioctl(f, CCISS_PASSTHRU, (unsigned long) p);
if (err)
return err;
- err |= copy_in_user(&arg32->error_info, &p->error_info, sizeof(&arg32->error_info));
+ err |= copy_in_user(&arg32->error_info, &p->error_info, sizeof(arg32->error_info));
if (err)
return -EFAULT;
return err;
}
-int cciss_ioctl32_big_passthru(unsigned int fd, unsigned cmd, unsigned long arg,
- struct file *file)
+static int cciss_ioctl32_big_passthru(struct file *file, unsigned cmd, unsigned long arg)
{
BIG_IOCTL32_Command_struct __user *arg32 =
(BIG_IOCTL32_Command_struct __user *) arg;
if (err)
return -EFAULT;
- err = sys_ioctl(fd, CCISS_BIG_PASSTHRU, (unsigned long) p);
+ err = do_ioctl(file, CCISS_BIG_PASSTHRU, (unsigned long) p);
if (err)
return err;
- err |= copy_in_user(&arg32->error_info, &p->error_info, sizeof(&arg32->error_info));
+ err |= copy_in_user(&arg32->error_info, &p->error_info, sizeof(arg32->error_info));
if (err)
return -EFAULT;
return err;
}
-#else
-static inline void register_cciss_ioctl32(void) {}
-static inline void unregister_cciss_ioctl32(void) {}
#endif
/*
* ioctl
luninfo.num_opens = drv->usage_count;
luninfo.num_parts = 0;
/* count partitions 1 to 15 with sizes > 0 */
- for(i=1; i <MAX_PART; i++) {
+ for (i = 0; i < MAX_PART - 1; i++) {
if (!disk->part[i])
continue;
if (disk->part[i]->nr_sects != 0)
kfree(buff);
return -EFAULT;
}
+ } else {
+ memset(buff, 0, iocommand.buf_size);
}
if ((c = cmd_alloc(host , 0)) == NULL)
{
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;
return(status);
}
default:
- return -EBADRQC;
+ return -ENOTTY;
}
}
-static int cciss_revalidate(struct gendisk *disk)
-{
- drive_info_struct *drv = disk->private_data;
- set_capacity(disk, drv->nr_blocks);
- return 0;
-}
-
/*
* revalidate_allvol is for online array config utilities. After a
* utility reconfigures the drives in the array, it can use this function
for (i = 0; i < NWD; i++) {
struct gendisk *disk = host->gendisk[i];
drive_info_struct *drv = &(host->drv[i]);
- if (!drv->nr_blocks)
+ /* we must register the controller even if no disks exist */
+ /* this is for the online array utilities */
+ if (!drv->heads && i)
continue;
blk_queue_hardsect_size(host->queue, drv->block_size);
set_capacity(disk, drv->nr_blocks);
drv->sectors = 32; // Sectors per track
drv->cylinders = total_size / 255 / 32;
} else {
+ unsigned int t;
+
drv->block_size = block_size;
drv->nr_blocks = total_size;
drv->heads = inq_buff->data_byte[6];
drv->sectors = inq_buff->data_byte[7];
drv->cylinders = (inq_buff->data_byte[4] & 0xff) << 8;
drv->cylinders += inq_buff->data_byte[5];
+ drv->raid_level = inq_buff->data_byte[8];
+ t = drv->heads * drv->sectors;
+ if (t > 1) {
+ drv->cylinders = total_size/t;
+ }
}
} else { /* Get geometry failed */
- printk(KERN_WARNING "cciss: reading geometry failed, "
- "continuing with default geometry\n");
- drv->block_size = block_size;
- drv->nr_blocks = total_size;
- drv->heads = 255;
- drv->sectors = 32; // Sectors per track
- drv->cylinders = total_size / 255 / 32;
+ printk(KERN_WARNING "cciss: reading geometry failed\n");
}
printk(KERN_INFO " heads= %d, sectors= %d, cylinders= %d\n\n",
drv->heads, drv->sectors, drv->cylinders);
return_code = sendcmd(CCISS_READ_CAPACITY,
ctlr, buf, sizeof(*buf), 1, logvol, 0, NULL, TYPE_CMD);
if (return_code == IO_OK) {
- *total_size = be32_to_cpu(*((__u32 *) &buf->total_size[0]))+1;
- *block_size = be32_to_cpu(*((__u32 *) &buf->block_size[0]));
+ *total_size = be32_to_cpu(*((__be32 *) &buf->total_size[0]))+1;
+ *block_size = be32_to_cpu(*((__be32 *) &buf->block_size[0]));
} else { /* read capacity command failed */
printk(KERN_WARNING "cciss: read capacity failed\n");
*total_size = 0;
*total_size, *block_size);
return;
}
+
static int register_new_disk(ctlr_info_t *h)
{
struct gendisk *disk;
/* setup partitions per disk */
disk = h->gendisk[logvol];
set_capacity(disk, h->drv[logvol].nr_blocks);
- add_disk(disk);
+ /* if it's the controller it's already added */
+ if(logvol)
+ add_disk(disk);
freeret:
kfree(ld_buff);
kfree(size_buff);
logvol = -1;
goto freeret;
}
+
+static int cciss_revalidate(struct gendisk *disk)
+{
+ ctlr_info_t *h = get_host(disk);
+ drive_info_struct *drv = get_drv(disk);
+ int logvol;
+ int FOUND=0;
+ unsigned int block_size;
+ unsigned int total_size;
+ ReadCapdata_struct *size_buff = NULL;
+ InquiryData_struct *inq_buff = NULL;
+
+ for(logvol=0; logvol < CISS_MAX_LUN; logvol++)
+ {
+ if(h->drv[logvol].LunID == drv->LunID) {
+ FOUND=1;
+ break;
+ }
+ }
+
+ if (!FOUND) return 1;
+
+ size_buff = kmalloc(sizeof( ReadCapdata_struct), GFP_KERNEL);
+ if (size_buff == NULL)
+ {
+ printk(KERN_WARNING "cciss: out of memory\n");
+ return 1;
+ }
+ inq_buff = kmalloc(sizeof( InquiryData_struct), GFP_KERNEL);
+ if (inq_buff == NULL)
+ {
+ printk(KERN_WARNING "cciss: out of memory\n");
+ kfree(size_buff);
+ return 1;
+ }
+
+ cciss_read_capacity(h->ctlr, logvol, size_buff, 1, &total_size, &block_size);
+ cciss_geometry_inquiry(h->ctlr, logvol, 1, total_size, block_size, inq_buff, drv);
+
+ blk_queue_hardsect_size(h->queue, drv->block_size);
+ set_capacity(disk, drv->nr_blocks);
+
+ kfree(size_buff);
+ kfree(inq_buff);
+ return 0;
+}
+
/*
* Wait polling for a command to complete.
* The memory mapped FIFO is polled for the completion.
/*
* Map (physical) PCI mem into (virtual) kernel space
*/
-static ulong remap_pci_mem(ulong base, ulong size)
+static void __iomem *remap_pci_mem(ulong base, ulong size)
{
ulong page_base = ((ulong) base) & PAGE_MASK;
ulong page_offs = ((ulong) base) - page_base;
- ulong page_remapped = (ulong) ioremap(page_base, page_offs+size);
+ void __iomem *page_remapped = ioremap(page_base, page_offs+size);
- return (ulong) (page_remapped ? (page_remapped + page_offs) : 0UL);
+ return page_remapped ? (page_remapped + page_offs) : NULL;
}
/*
static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
{
ushort subsystem_vendor_id, subsystem_device_id, command;
- unchar irq = pdev->irq;
__u32 board_id, scratchpad = 0;
__u64 cfg_offset;
__u32 cfg_base_addr;
#ifdef CCISS_DEBUG
printk("command = %x\n", command);
- printk("irq = %x\n", irq);
+ printk("irq = %x\n", pdev->irq);
printk("board_id = %x\n", board_id);
#endif /* CCISS_DEBUG */
- c->intr = irq;
+ c->intr = pdev->irq;
/*
* Memory base addr is first addr , the second points to the config
#ifdef CCISS_DEBUG
printk("cfg offset = %x\n", cfg_offset);
#endif /* CCISS_DEBUG */
- c->cfgtable = (CfgTable_struct *)
- remap_pci_mem(pci_resource_start(pdev, cfg_base_addr_index)
- + cfg_offset, sizeof(CfgTable_struct));
+ c->cfgtable = remap_pci_mem(pci_resource_start(pdev,
+ cfg_base_addr_index) + cfg_offset,
+ sizeof(CfgTable_struct));
c->board_id = board_id;
#ifdef CCISS_DEBUG
disk->fops = &cciss_fops;
disk->queue = hba[i]->queue;
disk->private_data = drv;
- if( !(drv->nr_blocks))
+ /* we must register the controller even if no disks exist */
+ /* this is for the online array utilities */
+ if(!drv->heads && j)
continue;
blk_queue_hardsect_size(hba[i]->queue, drv->block_size);
set_capacity(disk, drv->nr_blocks);
}
free_irq(hba[i]->intr, hba[i]);
pci_set_drvdata(pdev, NULL);
- iounmap((void*)hba[i]->vaddr);
+ iounmap(hba[i]->vaddr);
cciss_unregister_scsi(i); /* unhook from SCSI subsystem */
unregister_blkdev(COMPAQ_CISS_MAJOR+i, hba[i]->devname);
remove_proc_entry(hba[i]->devname, proc_cciss);
static int __init init_cciss_module(void)
{
- register_cciss_ioctl32();
return ( cciss_init());
}
{
int i;
- unregister_cciss_ioctl32();
pci_unregister_driver(&cciss_pci_driver);
/* double check that all controller entrys have been removed */
for (i=0; i< MAX_CTLR; i++)