diff options
author | Bartlomiej Zolnierkiewicz <bzolnier@gmail.com> | 2008-04-18 00:46:23 +0200 |
---|---|---|
committer | Bartlomiej Zolnierkiewicz <bzolnier@gmail.com> | 2008-04-18 00:46:23 +0200 |
commit | f74c91413ec6140ee0553180c5f56fdd27c22a2e (patch) | |
tree | 75ba3f7000ba290dc19b1523b12ab95dc5c7b1ea | |
parent | 50672e5d7486c9ab312432cbe180ac071f1de8e0 (diff) | |
download | op-kernel-dev-f74c91413ec6140ee0553180c5f56fdd27c22a2e.zip op-kernel-dev-f74c91413ec6140ee0553180c5f56fdd27c22a2e.tar.gz |
ide: add warm-plug support for IDE devices (take 2)
* Add 'struct class ide_port_class' ('ide_port' class) and a 'struct
device *portdev' ('ide_port' class device) in ide_hwif_t.
* Register 'ide_port' class in ide_init() and unregister it in
cleanup_module().
* Create ->portdev in ide_register_port () and unregister it in
ide_unregister().
* Add "delete_devices" class device attribute for unregistering IDE devices
on a port and "scan" one for probing+registering IDE devices on a port.
* Add ide_sysfs_register_port() helper for registering "delete_devices"
and "scan" attributes with ->portdev. Call it in ide_device_add_all().
* Document IDE warm-plug support in Documentation/ide/warm-plug-howto.txt.
v2:
* Convert patch from using 'struct class_device' to use 'struct device'.
(thanks to Kay Sievers for doing it)
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
-rw-r--r-- | Documentation/ide/warm-plug-howto.txt | 13 | ||||
-rw-r--r-- | drivers/ide/ide-probe.c | 71 | ||||
-rw-r--r-- | drivers/ide/ide.c | 24 | ||||
-rw-r--r-- | include/linux/ide.h | 5 |
4 files changed, 110 insertions, 3 deletions
diff --git a/Documentation/ide/warm-plug-howto.txt b/Documentation/ide/warm-plug-howto.txt new file mode 100644 index 0000000..d588546 --- /dev/null +++ b/Documentation/ide/warm-plug-howto.txt @@ -0,0 +1,13 @@ + +IDE warm-plug HOWTO +=================== + +To warm-plug devices on a port 'idex': + +# echo -n "1" > /sys/class/ide_port/idex/delete_devices + +unplug old device(s) and plug new device(s) + +# echo -n "1" > /sys/class/ide_port/idex/scan + +done diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 468c4ac..510254a 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -623,7 +623,7 @@ static void hwif_release_dev (struct device *dev) complete(&hwif->gendev_rel_comp); } -static void ide_register_port(ide_hwif_t *hwif) +static int ide_register_port(ide_hwif_t *hwif) { int ret; @@ -639,9 +639,23 @@ static void ide_register_port(ide_hwif_t *hwif) } hwif->gendev.release = hwif_release_dev; ret = device_register(&hwif->gendev); - if (ret < 0) + if (ret < 0) { printk(KERN_WARNING "IDE: %s: device_register error: %d\n", __FUNCTION__, ret); + goto out; + } + + get_device(&hwif->gendev); + + hwif->portdev = device_create(ide_port_class, &hwif->gendev, + MKDEV(0, 0), hwif->name); + if (IS_ERR(hwif->portdev)) { + ret = PTR_ERR(hwif->portdev); + device_unregister(&hwif->gendev); + } + dev_set_drvdata(hwif->portdev, hwif); +out: + return ret; } /** @@ -1378,6 +1392,58 @@ static void ide_port_cable_detect(ide_hwif_t *hwif) } } +static ssize_t store_delete_devices(struct device *portdev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + ide_hwif_t *hwif = dev_get_drvdata(portdev); + + if (strncmp(buf, "1", n)) + return -EINVAL; + + ide_port_unregister_devices(hwif); + + return n; +}; + +static DEVICE_ATTR(delete_devices, S_IWUSR, NULL, store_delete_devices); + +static ssize_t store_scan(struct device *portdev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + ide_hwif_t *hwif = dev_get_drvdata(portdev); + + if (strncmp(buf, "1", n)) + return -EINVAL; + + ide_port_unregister_devices(hwif); + ide_port_scan(hwif); + + return n; +}; + +static DEVICE_ATTR(scan, S_IWUSR, NULL, store_scan); + +static struct device_attribute *ide_port_attrs[] = { + &dev_attr_delete_devices, + &dev_attr_scan, + NULL +}; + +static int ide_sysfs_register_port(ide_hwif_t *hwif) +{ + int i, rc; + + for (i = 0; ide_port_attrs[i]; i++) { + rc = device_create_file(hwif->portdev, ide_port_attrs[i]); + if (rc) + break; + } + + return rc; +} + int ide_device_add_all(u8 *idx, const struct ide_port_info *d) { ide_hwif_t *hwif, *mate = NULL; @@ -1474,6 +1540,7 @@ int ide_device_add_all(u8 *idx, const struct ide_port_info *d) hwif = &ide_hwifs[idx[i]]; if (hwif->present) { + ide_sysfs_register_port(hwif); ide_proc_register_port(hwif); ide_proc_port_register_devices(hwif); } diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index cb18ba8..d791b1f 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -78,6 +78,8 @@ /* default maximum number of failures */ #define IDE_DEFAULT_MAX_FAILURES 1 +struct class *ide_port_class; + static const u8 ide_hwif_to_major[] = { IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, IDE4_MAJOR, IDE5_MAJOR, @@ -591,6 +593,7 @@ void ide_unregister(unsigned int index, int init_default, int restore) ide_remove_port_from_hwgroup(hwif); + device_unregister(hwif->portdev); device_unregister(&hwif->gendev); wait_for_completion(&hwif->gendev_rel_comp); @@ -1590,6 +1593,13 @@ struct bus_type ide_bus_type = { EXPORT_SYMBOL_GPL(ide_bus_type); +static void ide_port_class_release(struct device *portdev) +{ + ide_hwif_t *hwif = dev_get_drvdata(portdev); + + put_device(&hwif->gendev); +} + /* * This is gets invoked once during initialization, to set *everything* up */ @@ -1610,11 +1620,23 @@ static int __init ide_init(void) return ret; } + ide_port_class = class_create(THIS_MODULE, "ide_port"); + if (IS_ERR(ide_port_class)) { + ret = PTR_ERR(ide_port_class); + goto out_port_class; + } + ide_port_class->dev_release = ide_port_class_release; + init_ide_data(); proc_ide_create(); return 0; + +out_port_class: + bus_unregister(&ide_bus_type); + + return ret; } #ifdef MODULE @@ -1651,6 +1673,8 @@ void __exit cleanup_module (void) proc_ide_destroy(); + class_destroy(ide_port_class); + bus_unregister(&ide_bus_type); } diff --git a/include/linux/ide.h b/include/linux/ide.h index e43570a..9cebf30 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -579,7 +579,9 @@ typedef struct hwif_s { unsigned mmio : 1; /* host uses MMIO */ unsigned straight8 : 1; /* Alan's straight 8 check */ - struct device gendev; + struct device gendev; + struct device *portdev; + struct completion gendev_rel_comp; /* To deal with device release() */ void *hwif_data; /* extra hwif data */ @@ -1275,6 +1277,7 @@ extern struct mutex ide_cfg_mtx; #define local_irq_set(flags) do { local_save_flags((flags)); local_irq_enable_in_hardirq(); } while (0) extern struct bus_type ide_bus_type; +extern struct class *ide_port_class; /* check if CACHE FLUSH (EXT) command is supported (bits defined in ATA-6) */ #define ide_id_has_flush_cache(id) ((id)->cfs_enable_2 & 0x3000) |