]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] s390: ccwgroups.
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Fri, 6 Dec 2002 08:18:45 +0000 (00:18 -0800)
committerLinus Torvalds <torvalds@penguin.transmeta.com>
Fri, 6 Dec 2002 08:18:45 +0000 (00:18 -0800)
ccwgroup bus driver.

The ccwgroup driver contains 'ccw_group' devices, which needed because of
strange hardware properties: Some device drivers need two or more
ccw_devices to make up a logical device and the user has to configure
which ones belong together. We have not found a way to represent this
correctly using the linux driver model, the current implementation
is about as good as it gets.

Authors: Arnd Bergmann <arndb@de.ibm.com>,

arch/s390/defconfig
arch/s390x/defconfig
drivers/s390/Kconfig
drivers/s390/cio/ccwgroup.c [new file with mode: 0644]

index 2e76cfef54eca679bf6a00b838dc6a453c04f0d6..365dec975822a874ea888df377408d8377689bf1 100644 (file)
@@ -143,6 +143,7 @@ CONFIG_HOTPLUG=y
 CONFIG_LCS=m
 CONFIG_CTC=m
 CONFIG_IUCV=m
+CONFIG_CCWGROUP=m
 
 #
 # Networking options
index 5694a82daeabc57abcc447860e1eca6ff2c41a9e..b14708f0af1987e707fe262fdaddd058d0a200c9 100644 (file)
@@ -201,6 +201,7 @@ CONFIG_HOTPLUG=y
 CONFIG_LCS=m
 CONFIG_CTC=m
 CONFIG_IUCV=m
+CONFIG_CCWGROUP=m
 
 #
 # Networking options
index 80f98769a36148359db5eb479ad1988dc78ea91c..5352eeb6b3dce35fcb707b578dc69f36804d1a4f 100644 (file)
@@ -571,5 +571,10 @@ config IUCV
          vehicle networking under VM or VIF.  This option is also available
          as a module which will be called iucv.o. If unsure, say "Y".
 
+config CCWGROUP
+       tristate
+       depends on LCS || CTC
+       default m if LCS!=y && CTC!=y
+       default y if LCS=y || CTC=y
 endmenu
 
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
new file mode 100644 (file)
index 0000000..f06565e
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ *  drivers/s390/cio/ccwgroup.c
+ *  bus driver for ccwgroup
+ *   $Revision: 1.5 $
+ *
+ *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
+ *                       IBM Corporation
+ *    Author(s): Arnd Bergmann (arndb@de.ibm.com)
+ */
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+
+#include <asm/semaphore.h>
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+
+/* In Linux 2.4, we had a channel device layer called "chandev"
+ * that did all sorts of obscure stuff for networking devices.
+ * This is another driver that serves as a replacement for just
+ * one of its functions, namely the translation of single subchannels
+ * to devices that use multiple subchannels.
+ */
+
+/* a device matches a driver if all its slave devices match the same
+ * entry of the driver */
+static int
+ccwgroup_bus_match (struct device * dev, struct device_driver * drv)
+{
+       struct ccwgroup_device *gdev;
+       struct ccwgroup_driver *gdrv;
+
+       gdev = container_of(dev, struct ccwgroup_device, dev);
+       gdrv = container_of(drv, struct ccwgroup_driver, driver);
+
+       if (gdev->creator_id == gdrv->driver_id)
+               return 1;
+
+       return 0;
+}
+static int
+ccwgroup_hotplug (struct device *dev, char **envp, int num_envp, char *buffer,
+                 int buffer_size)
+{
+       /* TODO */
+       return 0;
+}
+
+static struct bus_type ccwgroup_bus_type = {
+       .name    = "ccwgroup",
+       .match   = ccwgroup_bus_match,
+       .hotplug = ccwgroup_hotplug,
+};
+
+static void
+ccwgroup_release (struct device *dev)
+{
+       struct ccwgroup_device *gdev;
+       int i;
+
+       gdev = to_ccwgroupdev(dev);
+
+       for (i = 0; i < gdev->count; i++)
+               put_device(&gdev->cdev[i]->dev);
+       kfree(gdev);
+}
+
+/*
+ * try to add a new ccwgroup device for one driver
+ * argc and argv[] are a list of bus_id's of devices
+ * belonging to the driver.
+ */
+int
+ccwgroup_create(struct device *root,
+               unsigned int creator_id,
+               struct ccw_driver *cdrv,
+               int argc, char *argv[])
+{
+       struct ccwgroup_device *gdev;
+       int i;
+
+       if (argc > 256) /* disallow dumb users */
+               return -EINVAL;
+
+       gdev = kmalloc(sizeof(*gdev) + argc*sizeof(gdev->cdev[0]), GFP_KERNEL);
+       if (!gdev)
+               return -ENOMEM;
+
+       for (i = 0; i < argc; i++) {
+               gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]);
+
+               /* all devices have to be of the same type in
+                * order to be grouped */
+               if (!gdev->cdev[i]
+                   || gdev->cdev[i]->id.driver_info !=
+                      gdev->cdev[0]->id.driver_info)
+                       goto error;
+       }
+
+       *gdev = (struct ccwgroup_device) {
+               .creator_id = creator_id,
+               .count = argc,
+               .dev = {
+                       .bus = &ccwgroup_bus_type,
+                       .parent = root,
+                       .release = &ccwgroup_release,
+               },
+       };
+
+       snprintf (gdev->dev.bus_id, BUS_ID_SIZE, "%s",
+                       gdev->cdev[0]->dev.bus_id);
+
+       /* TODO: make symlinks for sysfs */
+       return device_register(&gdev->dev);
+
+error:
+       for (i = 0; i < argc; i++)
+               if (gdev->cdev[i])
+                       put_device(&gdev->cdev[i]->dev);
+
+       kfree(gdev);
+
+       return -EINVAL;
+}
+
+static int __init
+init_ccwgroup (void)
+{
+       return bus_register (&ccwgroup_bus_type);
+}
+
+static void __exit
+cleanup_ccwgroup (void)
+{
+       bus_unregister (&ccwgroup_bus_type);
+}
+
+module_init(init_ccwgroup);
+module_exit(cleanup_ccwgroup);
+
+/************************** driver stuff ******************************/
+
+static int
+ccwgroup_set_online(struct ccwgroup_device *gdev)
+{
+       struct ccwgroup_driver *gdrv;
+       int ret;
+
+       if (gdev->state == CCWGROUP_ONLINE)
+               return 0;
+
+       if (!gdev->dev.driver)
+               return -EINVAL;
+
+       gdrv = to_ccwgroupdrv (gdev->dev.driver);
+       if ((ret = gdrv->set_online(gdev)))
+               return ret;
+
+       gdev->state = CCWGROUP_ONLINE;
+       return 0;
+}
+
+static int
+ccwgroup_set_offline(struct ccwgroup_device *gdev)
+{
+       struct ccwgroup_driver *gdrv;
+       int ret;
+
+       if (gdev->state == CCWGROUP_OFFLINE)
+               return 0;
+
+       if (!gdev->dev.driver)
+               return -EINVAL;
+
+       gdrv = to_ccwgroupdrv (gdev->dev.driver);
+       if ((ret = gdrv->set_offline(gdev)))
+               return ret;
+
+       gdev->state = CCWGROUP_OFFLINE;
+       return 0;
+}
+
+static ssize_t
+ccwgroup_online_store (struct device *dev, const char *buf, size_t count,
+                       loff_t off)
+{
+       struct ccwgroup_device *gdev;
+       unsigned int value;
+
+       if (off)
+               return 0;
+
+       gdev = to_ccwgroupdev(dev);
+       if (!dev->driver)
+               return count;
+
+       value = simple_strtoul(buf, 0, 0);
+
+       if (value)
+               ccwgroup_set_online(gdev);
+       else
+               ccwgroup_set_offline(gdev);
+
+       return count;
+}
+
+static ssize_t
+ccwgroup_online_show (struct device *dev, char *buf, size_t count, loff_t off)
+{
+       int online;
+
+       if (off)
+               return 0;
+
+       online = (to_ccwgroupdev(dev)->state == CCWGROUP_ONLINE);
+
+       return snprintf(buf, count, online ? "1\n" : "0\n");
+}
+
+static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store);
+
+static int
+ccwgroup_probe (struct device *dev)
+{
+       struct ccwgroup_device *gdev;
+       struct ccwgroup_driver *gdrv;
+
+       int ret;
+
+       gdev = to_ccwgroupdev(dev);
+       gdrv = to_ccwgroupdrv(dev->driver);
+
+       if ((ret = device_create_file(dev, &dev_attr_online)))
+               return ret;
+
+       pr_debug("%s: device %s\n", __func__, gdev->dev.name);
+       ret = gdrv->probe ? gdrv->probe(gdev) : -ENODEV;
+       if (ret)
+               device_remove_file(dev, &dev_attr_online);
+
+       return ret;
+}
+
+static int
+ccwgroup_remove (struct device *dev)
+{
+       struct ccwgroup_device *gdev;
+       struct ccwgroup_driver *gdrv;
+       int ret;
+
+       gdev = to_ccwgroupdev(dev);
+       gdrv = to_ccwgroupdrv(dev->driver);
+
+       pr_debug("%s: device %s\n", __func__, gdev->dev.name);
+
+       device_remove_file(dev, &dev_attr_online);
+       ccwgroup_set_offline(gdev);
+
+       ret = (gdrv && gdrv->remove) ? gdrv->remove(gdev) : 0;
+
+       return ret;
+}
+
+int
+ccwgroup_driver_register (struct ccwgroup_driver *cdriver)
+{
+       /* register our new driver with the core */
+       cdriver->driver = (struct device_driver) {
+               .bus = &ccwgroup_bus_type,
+               .name = cdriver->name,
+               .probe = ccwgroup_probe,
+               .remove = ccwgroup_remove,
+       };
+
+       return driver_register(&cdriver->driver);
+}
+
+void
+ccwgroup_driver_unregister (struct ccwgroup_driver *cdriver)
+{
+       driver_unregister(&cdriver->driver);
+}
+
+int
+ccwgroup_probe_ccwdev(struct ccw_device *cdev)
+{
+       return 0;
+}
+
+int
+ccwgroup_remove_ccwdev(struct ccw_device *cdev)
+{
+       return 0;
+}
+
+MODULE_LICENSE("GPL");
+EXPORT_SYMBOL(ccwgroup_driver_register);
+EXPORT_SYMBOL(ccwgroup_driver_unregister);
+EXPORT_SYMBOL(ccwgroup_create);
+EXPORT_SYMBOL(ccwgroup_probe_ccwdev);
+EXPORT_SYMBOL(ccwgroup_remove_ccwdev);