/* * Configuration OSM * * Copyright (C) 2005 Markus Lidel * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * Fixes/additions: * Markus Lidel * initial version. */ #include #include #include #include #include #include #define OSM_NAME "config-osm" #define OSM_VERSION "1.248" #define OSM_DESCRIPTION "I2O Configuration OSM" /* access mode user rw */ #define S_IWRSR (S_IRUSR | S_IWUSR) static struct i2o_driver i2o_config_driver; /* Special file operations for sysfs */ struct fops_attribute { struct bin_attribute bin; struct file_operations fops; }; /** * sysfs_read_dummy */ static ssize_t sysfs_read_dummy(struct kobject *kobj, char *buf, loff_t offset, size_t count) { return 0; }; /** * sysfs_write_dummy */ static ssize_t sysfs_write_dummy(struct kobject *kobj, char *buf, loff_t offset, size_t count) { return 0; }; /** * sysfs_create_fops_file - Creates attribute with special file operations * @kobj: kobject which should contains the attribute * @attr: attributes which should be used to create file * * First creates attribute @attr in kobject @kobj. If it is the first time * this function is called, merge old fops from sysfs with new one and * write it back. Afterwords the new fops will be set for the created * attribute. * * Returns 0 on success or negative error code on failure. */ static int sysfs_create_fops_file(struct kobject *kobj, struct fops_attribute *attr) { struct file_operations tmp, *fops; struct dentry *d; struct qstr qstr; int rc; fops = &attr->fops; if (fops->read) attr->bin.read = sysfs_read_dummy; if (fops->write) attr->bin.write = sysfs_write_dummy; if ((rc = sysfs_create_bin_file(kobj, &attr->bin))) return rc; qstr.name = attr->bin.attr.name; qstr.len = strlen(qstr.name); qstr.hash = full_name_hash(qstr.name, qstr.len); if ((d = lookup_hash(&qstr, kobj->dentry))) { if (!fops->owner) { memcpy(&tmp, d->d_inode->i_fop, sizeof(tmp)); if (fops->read) tmp.read = fops->read; if (fops->write) tmp.write = fops->write; memcpy(fops, &tmp, sizeof(tmp)); } d->d_inode->i_fop = fops; } else sysfs_remove_bin_file(kobj, &attr->bin); return -ENOENT; }; /** * sysfs_remove_fops_file - Remove attribute with special file operations * @kobj: kobject which contains the attribute * @attr: attributes which are used to create file * * Only wrapper arround sysfs_remove_bin_file() * * Returns 0 on success or negative error code on failure. */ static inline int sysfs_remove_fops_file(struct kobject *kobj, struct fops_attribute *attr) { return sysfs_remove_bin_file(kobj, &attr->bin); }; /** * i2o_config_read_hrt - Returns the HRT of the controller * @kob: kernel object handle * @buf: buffer into which the HRT should be copied * @off: file offset * @count: number of bytes to read * * Put @count bytes starting at @off into @buf from the HRT of the I2O * controller corresponding to @kobj. * * Returns number of bytes copied into buffer. */ static ssize_t i2o_config_read_hrt(struct kobject *kobj, char *buf, loff_t offset, size_t count) { struct i2o_controller *c = kobj_to_i2o_device(kobj)->iop; i2o_hrt *hrt = c->hrt.virt; u32 size = (hrt->num_entries * hrt->entry_len + 2) * 4; if (offset > size) return 0; if (offset + count > size) count = size - offset; memcpy(buf, (u8 *) hrt + offset, count); return count; }; /** * i2o_config_read_lct - Returns the LCT of the controller * @kob: kernel object handle * @buf: buffer into which the LCT should be copied * @off: file offset * @count: number of bytes to read * * Put @count bytes starting at @off into @buf from the LCT of the I2O * controller corresponding to @kobj. * * Returns number of bytes copied into buffer. */ static ssize_t i2o_config_read_lct(struct kobject *kobj, char *buf, loff_t offset, size_t count) { struct i2o_controller *c = kobj_to_i2o_device(kobj)->iop; u32 size = c->lct->table_size * 4; if (offset > size) return 0; if (offset + count > size) count = size - offset; memcpy(buf, (u8 *) c->lct + offset, count); return count; }; #define I2O_CONFIG_SW_ATTR(_name,_mode,_type,_swid) \ static ssize_t i2o_config_##_name##_read(struct file *file, char __user *buf, size_t count, loff_t * offset) { \ return i2o_config_sw_read(file, buf, count, offset, _type, _swid); \ };\ \ static ssize_t i2o_config_##_name##_write(struct file *file, const char __user *buf, size_t count, loff_t * offset) { \ return i2o_config_sw_write(file, buf, count, offset, _type, _swid); \ }; \ \ static struct fops_attribute i2o_config_attr_##_name = { \ .bin = { .attr = { .name = __stringify(_name), .mode = _mode, \ .owner = THIS_MODULE }, \ .size = 0, }, \ .fops = { .write = i2o_config_##_name##_write, \ .read = i2o_config_##_name##_read} \ }; #ifdef CONFIG_I2O_EXT_ADAPTEC /** * i2o_config_dpt_reagion - Converts type and id to flash region * @swtype: type of software module reading * @swid: id of software which should be read * * Converts type and id from I2O spec to the matching region for DPT / * Adaptec controllers. * * Returns region which match type and id or -1 on error. */ static u32 i2o_config_dpt_region(u8 swtype, u8 swid) { switch (swtype) { case I2O_SOFTWARE_MODULE_IRTOS: /* * content: operation firmware * region size: * 0xbc000 for 2554, 3754, 2564, 3757 * 0x170000 for 2865 * 0x17c000 for 3966 */ if (!swid) return 0; break; case I2O_SOFTWARE_MODULE_IOP_PRIVATE: /* * content: BIOS and SMOR * BIOS size: first 0x8000 bytes * region size: * 0x40000 for 2554, 3754, 2564, 3757 * 0x80000 for 2865, 3966 */ if (!swid) return 1; break; case I2O_SOFTWARE_MODULE_IOP_CONFIG: switch (swid) { case 0: /* * content: NVRAM defaults * region size: 0x2000 bytes */ return 2; case 1: /* * content: serial number * region size: 0x2000 bytes */ return 3; } break; } return -1; }; #endif /** * i2o_config_sw_read - Read a software module from controller * @file: file pointer * @buf: buffer into which the data should be copied * @count: number of bytes to read * @off: file offset * @swtype: type of software module reading * @swid: id of software which should be read * * Transfers @count bytes at offset @offset from IOP into buffer using * type @swtype and id @swid as described in I2O spec. * * Returns number of bytes copied into buffer or error code on failure. */ static ssize_t i2o_config_sw_read(struct file *file, char __user * buf, size_t count, loff_t * offset, u8 swtype, u32 swid) { struct sysfs_dirent *sd = file->f_dentry->d_parent->d_fsdata; struct kobject *kobj = sd->s_element; struct i2o_controller *c = kobj_to_i2o_device(kobj)->iop; u32 m, function = I2O_CMD_SW_UPLOAD; struct i2o_dma buffer; struct i2o_message __iomem *msg; u32 __iomem *mptr; int rc, status; m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); if (m == I2O_QUEUE_EMPTY) return -EBUSY; mptr = &msg->body[3]; if ((rc = i2o_dma_alloc(&c->pdev->dev, &buffer, count, GFP_KERNEL))) { i2o_msg_nop(c, m); return rc; } #ifdef CONFIG_I2O_EXT_ADAPTEC if (c->adaptec) { mptr = &msg->body[4]; function = I2O_CMD_PRIVATE; writel(TEN_WORD_MSG_SIZE | SGL_OFFSET_8, &msg->u.head[0]); writel(I2O_VENDOR_DPT << 16 | I2O_DPT_FLASH_READ, &msg->body[0]); writel(i2o_config_dpt_region(swtype, swid), &msg->body[1]); writel(*offset, &msg->body[2]); writel(count, &msg->body[3]); } else #endif writel(NINE_WORD_MSG_SIZE | SGL_OFFSET_7, &msg->u.head[0]); writel(0xD0000000 | count, mptr++); writel(buffer.phys, mptr); writel(function << 24 | HOST_TID << 12 | ADAPTER_TID, &msg->u.head[1]); writel(i2o_config_driver.context, &msg->u.head[2]); writel(0, &msg->u.head[3]); #ifdef CONFIG_I2O_EXT_ADAPTEC if (!c->adaptec) #endif { writel((u32) swtype << 16 | (u32) 1 << 8, &msg->body[0]); writel(0, &msg->body[1]); writel(swid, &msg->body[2]); } status = i2o_msg_post_wait_mem(c, m, 60, &buffer); if (status == I2O_POST_WAIT_OK) { if (!(rc = copy_to_user(buf, buffer.virt, count))) { rc = count; *offset += count; } } else rc = -EIO; if (status != -ETIMEDOUT) i2o_dma_free(&c->pdev->dev, &buffer); return rc; }; /** * i2o_config_sw_write - Write a software module to controller * @file: file pointer * @buf: buffer into which the data should be copied * @count: number of bytes to read * @off: file offset * @swtype: type of software module writing * @swid: id of software which should be written * * Transfers @count bytes at offset @offset from buffer to IOP using * type @swtype and id @swid as described in I2O spec. * * Returns number of bytes copied from buffer or error code on failure. */ static ssize_t i2o_config_sw_write(struct file *file, const char __user * buf, size_t count, loff_t * offset, u8 swtype, u32 swid) { struct sysfs_dirent *sd = file->f_dentry->d_parent->d_fsdata; struct kobject *kobj = sd->s_element; struct i2o_controller *c = kobj_to_i2o_device(kobj)->iop; u32 m, function = I2O_CMD_SW_DOWNLOAD; struct i2o_dma buffer; struct i2o_message __iomem *msg; u32 __iomem *mptr; int rc, status; m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); if (m == I2O_QUEUE_EMPTY) return -EBUSY; mptr = &msg->body[3]; if ((rc = i2o_dma_alloc(&c->pdev->dev, &buffer, count, GFP_KERNEL))) goto nop_msg; if ((rc = copy_from_user(buffer.virt, buf, count))) goto free_buffer; #ifdef CONFIG_I2O_EXT_ADAPTEC if (c->adaptec) { mptr = &msg->body[4]; function = I2O_CMD_PRIVATE; writel(TEN_WORD_MSG_SIZE | SGL_OFFSET_8, &msg->u.head[0]); writel(I2O_VENDOR_DPT << 16 | I2O_DPT_FLASH_WRITE, &msg->body[0]); writel(i2o_config_dpt_region(swtype, swid), &msg->body[1]); writel(*offset, &msg->body[2]); writel(count, &msg->body[3]); } else #endif writel(NINE_WORD_MSG_SIZE | SGL_OFFSET_7, &msg->u.head[0]); writel(0xD4000000 | count, mptr++); writel(buffer.phys, mptr); writel(function << 24 | HOST_TID << 12 | ADAPTER_TID, &msg->u.head[1]); writel(i2o_config_driver.context, &msg->u.head[2]); writel(0, &msg->u.head[3]); #ifdef CONFIG_I2O_EXT_ADAPTEC if (!c->adaptec) #endif { writel((u32) swtype << 16 | (u32) 1 << 8, &msg->body[0]); writel(0, &msg->body[1]); writel(swid, &msg->body[2]); } status = i2o_msg_post_wait_mem(c, m, 60, &buffer); if (status != -ETIMEDOUT) i2o_dma_free(&c->pdev->dev, &buffer); if (status != I2O_POST_WAIT_OK) return -EIO; *offset += count; return count; free_buffer: i2o_dma_free(&c->pdev->dev, &buffer); nop_msg: i2o_msg_nop(c, m); return rc; }; /* attribute for HRT in sysfs */ static struct bin_attribute i2o_config_hrt_attr = { .attr = { .name = "hrt", .mode = S_IRUGO, .owner = THIS_MODULE}, .size = 0, .read = i2o_config_read_hrt }; /* attribute for LCT in sysfs */ static struct bin_attribute i2o_config_lct_attr = { .attr = { .name = "lct", .mode = S_IRUGO, .owner = THIS_MODULE}, .size = 0, .read = i2o_config_read_lct }; /* IRTOS firmware access */ I2O_CONFIG_SW_ATTR(irtos, S_IWRSR, I2O_SOFTWARE_MODULE_IRTOS, 0); #ifdef CONFIG_I2O_EXT_ADAPTEC /* * attribute for BIOS / SMOR, nvram and serial number access on DPT / Adaptec * controllers */ I2O_CONFIG_SW_ATTR(bios, S_IWRSR, I2O_SOFTWARE_MODULE_IOP_PRIVATE, 0); I2O_CONFIG_SW_ATTR(nvram, S_IWRSR, I2O_SOFTWARE_MODULE_IOP_CONFIG, 0); I2O_CONFIG_SW_ATTR(serial, S_IWRSR, I2O_SOFTWARE_MODULE_IOP_CONFIG, 1); #endif /** * i2o_config_notify_controller_add - Notify of added controller * @c: the controller which was added * * If a I2O controller is added, we catch the notification to add sysfs * entries. */ static void i2o_config_notify_controller_add(struct i2o_controller *c) { struct kobject *kobj = &c->exec->device.kobj; sysfs_create_bin_file(kobj, &i2o_config_hrt_attr); sysfs_create_bin_file(kobj, &i2o_config_lct_attr); sysfs_create_fops_file(kobj, &i2o_config_attr_irtos); #ifdef CONFIG_I2O_EXT_ADAPTEC if (c->adaptec) { sysfs_create_fops_file(kobj, &i2o_config_attr_bios); sysfs_create_fops_file(kobj, &i2o_config_attr_nvram); sysfs_create_fops_file(kobj, &i2o_config_attr_serial); } #endif }; /** * i2o_config_notify_controller_remove - Notify of removed controller * @c: the controller which was removed * * If a I2O controller is removed, we catch the notification to remove the * sysfs entries. */ static void i2o_config_notify_controller_remove(struct i2o_controller *c) { struct kobject *kobj = &c->exec->device.kobj; #ifdef CONFIG_I2O_EXT_ADAPTEC if (c->adaptec) { sysfs_remove_fops_file(kobj, &i2o_config_attr_serial); sysfs_remove_fops_file(kobj, &i2o_config_attr_nvram); sysfs_remove_fops_file(kobj, &i2o_config_attr_bios); } #endif sysfs_remove_fops_file(kobj, &i2o_config_attr_irtos); sysfs_remove_bin_file(kobj, &i2o_config_lct_attr); sysfs_remove_bin_file(kobj, &i2o_config_hrt_attr); }; /* Config OSM driver struct */ static struct i2o_driver i2o_config_driver = { .name = OSM_NAME, .notify_controller_add = i2o_config_notify_controller_add, .notify_controller_remove = i2o_config_notify_controller_remove }; #ifdef CONFIG_I2O_CONFIG_OLD_IOCTL #include "i2o_config.c" #endif /** * i2o_config_init - Configuration OSM initialization function * * Registers Configuration OSM in the I2O core and if old ioctl's are * compiled in initialize them. * * Returns 0 on success or negative error code on failure. */ static int __init i2o_config_init(void) { printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n"); if (i2o_driver_register(&i2o_config_driver)) { osm_err("handler register failed.\n"); return -EBUSY; } #ifdef CONFIG_I2O_CONFIG_OLD_IOCTL if (i2o_config_old_init()) i2o_driver_unregister(&i2o_config_driver); #endif return 0; } /** * i2o_config_exit - Configuration OSM exit function * * If old ioctl's are compiled in exit remove them and unregisters * Configuration OSM from I2O core. */ static void i2o_config_exit(void) { #ifdef CONFIG_I2O_CONFIG_OLD_IOCTL i2o_config_old_exit(); #endif i2o_driver_unregister(&i2o_config_driver); } MODULE_AUTHOR("Markus Lidel "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION(OSM_DESCRIPTION); MODULE_VERSION(OSM_VERSION); module_init(i2o_config_init); module_exit(i2o_config_exit);