Merge git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[pandora-kernel.git] / drivers / s390 / cio / device.c
index eafde43..94bdd4d 100644 (file)
@@ -8,7 +8,6 @@
  *              Cornelia Huck (cornelia.huck@de.ibm.com)
  *              Martin Schwidefsky (schwidefsky@de.ibm.com)
  */
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/spinlock.h>
@@ -53,55 +52,83 @@ ccw_bus_match (struct device * dev, struct device_driver * drv)
        return 1;
 }
 
-/*
- * Hotplugging interface for ccw devices.
- * Heavily modeled on pci and usb hotplug.
- */
-static int
-ccw_uevent (struct device *dev, char **envp, int num_envp,
-            char *buffer, int buffer_size)
+/* Store modalias string delimited by prefix/suffix string into buffer with
+ * specified size. Return length of resulting string (excluding trailing '\0')
+ * even if string doesn't fit buffer (snprintf semantics). */
+static int snprint_alias(char *buf, size_t size, const char *prefix,
+                        struct ccw_device_id *id, const char *suffix)
 {
-       struct ccw_device *cdev = to_ccwdev(dev);
-       int i = 0;
-       int length = 0;
+       int len;
 
-       if (!cdev)
-               return -ENODEV;
+       len = snprintf(buf, size, "%sccw:t%04Xm%02X", prefix, id->cu_type,
+                      id->cu_model);
+       if (len > size)
+               return len;
+       buf += len;
+       size -= len;
 
-       /* what we want to pass to /sbin/hotplug */
+       if (id->dev_type != 0)
+               len += snprintf(buf, size, "dt%04Xdm%02X%s", id->dev_type,
+                               id->dev_model, suffix);
+       else
+               len += snprintf(buf, size, "dtdm%s", suffix);
 
-       envp[i++] = buffer;
-       length += scnprintf(buffer, buffer_size - length, "CU_TYPE=%04X",
-                          cdev->id.cu_type);
-       if ((buffer_size - length <= 0) || (i >= num_envp))
-               return -ENOMEM;
-       ++length;
-       buffer += length;
+       return len;
+}
+
+/* Set up environment variables for ccw device uevent. Return 0 on success,
+ * non-zero otherwise. */
+static int ccw_uevent(struct device *dev, char **envp, int num_envp,
+                     char *buffer, int buffer_size)
+{
+       struct ccw_device *cdev = to_ccwdev(dev);
+       struct ccw_device_id *id = &(cdev->id);
+       int i = 0;
+       int len;
 
+       /* CU_TYPE= */
+       len = snprintf(buffer, buffer_size, "CU_TYPE=%04X", id->cu_type) + 1;
+       if (len > buffer_size || i >= num_envp)
+               return -ENOMEM;
        envp[i++] = buffer;
-       length += scnprintf(buffer, buffer_size - length, "CU_MODEL=%02X",
-                          cdev->id.cu_model);
-       if ((buffer_size - length <= 0) || (i >= num_envp))
+       buffer += len;
+       buffer_size -= len;
+
+       /* CU_MODEL= */
+       len = snprintf(buffer, buffer_size, "CU_MODEL=%02X", id->cu_model) + 1;
+       if (len > buffer_size || i >= num_envp)
                return -ENOMEM;
-       ++length;
-       buffer += length;
+       envp[i++] = buffer;
+       buffer += len;
+       buffer_size -= len;
 
        /* The next two can be zero, that's ok for us */
-       envp[i++] = buffer;
-       length += scnprintf(buffer, buffer_size - length, "DEV_TYPE=%04X",
-                          cdev->id.dev_type);
-       if ((buffer_size - length <= 0) || (i >= num_envp))
+       /* DEV_TYPE= */
+       len = snprintf(buffer, buffer_size, "DEV_TYPE=%04X", id->dev_type) + 1;
+       if (len > buffer_size || i >= num_envp)
                return -ENOMEM;
-       ++length;
-       buffer += length;
+       envp[i++] = buffer;
+       buffer += len;
+       buffer_size -= len;
 
+       /* DEV_MODEL= */
+       len = snprintf(buffer, buffer_size, "DEV_MODEL=%02X",
+                       (unsigned char) id->dev_model) + 1;
+       if (len > buffer_size || i >= num_envp)
+               return -ENOMEM;
        envp[i++] = buffer;
-       length += scnprintf(buffer, buffer_size - length, "DEV_MODEL=%02X",
-                          cdev->id.dev_model);
-       if ((buffer_size - length <= 0) || (i >= num_envp))
+       buffer += len;
+       buffer_size -= len;
+
+       /* MODALIAS=  */
+       len = snprint_alias(buffer, buffer_size, "MODALIAS=", id, "") + 1;
+       if (len > buffer_size || i >= num_envp)
                return -ENOMEM;
+       envp[i++] = buffer;
+       buffer += len;
+       buffer_size -= len;
 
-       envp[i] = 0;
+       envp[i] = NULL;
 
        return 0;
 }
@@ -252,16 +279,11 @@ modalias_show (struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct ccw_device *cdev = to_ccwdev(dev);
        struct ccw_device_id *id = &(cdev->id);
-       int ret;
+       int len;
 
-       ret = sprintf(buf, "ccw:t%04Xm%02X",
-                       id->cu_type, id->cu_model);
-       if (id->dev_type != 0)
-               ret += sprintf(buf + ret, "dt%04Xdm%02X\n",
-                               id->dev_type, id->dev_model);
-       else
-               ret += sprintf(buf + ret, "dtdm\n");
-       return ret;
+       len = snprint_alias(buf, PAGE_SIZE, "", id, "\n") + 1;
+
+       return len > PAGE_SIZE ? PAGE_SIZE : len;
 }
 
 static ssize_t
@@ -281,7 +303,7 @@ ccw_device_remove_disconnected(struct ccw_device *cdev)
         * 'throw away device'.
         */
        sch = to_subchannel(cdev->dev.parent);
-       device_unregister(&sch->dev);
+       css_sch_device_unregister(sch);
        /* Reset intparm to zeroes. */
        sch->schib.pmcw.intparm = 0;
        cio_modify(sch);
@@ -530,21 +552,19 @@ ccw_device_register(struct ccw_device *cdev)
 }
 
 struct match_data {
-       unsigned int devno;
-       unsigned int ssid;
+       struct ccw_dev_id dev_id;
        struct ccw_device * sibling;
 };
 
 static int
 match_devno(struct device * dev, void * data)
 {
-       struct match_data * d = (struct match_data *)data;
+       struct match_data * d = data;
        struct ccw_device * cdev;
 
        cdev = to_ccwdev(dev);
        if ((cdev->private->state == DEV_STATE_DISCONNECTED) &&
-           (cdev->private->devno == d->devno) &&
-           (cdev->private->ssid == d->ssid) &&
+           ccw_dev_id_is_equal(&cdev->private->dev_id, &d->dev_id) &&
            (cdev != d->sibling)) {
                cdev->private->state = DEV_STATE_NOT_OPER;
                return 1;
@@ -552,17 +572,14 @@ match_devno(struct device * dev, void * data)
        return 0;
 }
 
-static struct ccw_device *
-get_disc_ccwdev_by_devno(unsigned int devno, unsigned int ssid,
-                        struct ccw_device *sibling)
+static struct ccw_device * get_disc_ccwdev_by_dev_id(struct ccw_dev_id *dev_id,
+                                                    struct ccw_device *sibling)
 {
        struct device *dev;
-       struct match_data data = {
-               .devno   = devno,
-               .ssid    = ssid,
-               .sibling = sibling,
-       };
+       struct match_data data;
 
+       data.dev_id = *dev_id;
+       data.sibling = sibling;
        dev = bus_find_device(&ccw_bus_type, NULL, &data, match_devno);
 
        return dev ? to_ccwdev(dev) : NULL;
@@ -574,7 +591,7 @@ ccw_device_add_changed(void *data)
 
        struct ccw_device *cdev;
 
-       cdev = (struct ccw_device *)data;
+       cdev = data;
        if (device_add(&cdev->dev)) {
                put_device(&cdev->dev);
                return;
@@ -595,9 +612,9 @@ ccw_device_do_unreg_rereg(void *data)
        struct subchannel *sch;
        int need_rename;
 
-       cdev = (struct ccw_device *)data;
+       cdev = data;
        sch = to_subchannel(cdev->dev.parent);
-       if (cdev->private->devno != sch->schib.pmcw.dev) {
+       if (cdev->private->dev_id.devno != sch->schib.pmcw.dev) {
                /*
                 * The device number has changed. This is usually only when
                 * a device has been detached under VM and then re-appeared
@@ -612,10 +629,12 @@ ccw_device_do_unreg_rereg(void *data)
                 *        get possibly sick...
                 */
                struct ccw_device *other_cdev;
+               struct ccw_dev_id dev_id;
 
                need_rename = 1;
-               other_cdev = get_disc_ccwdev_by_devno(sch->schib.pmcw.dev,
-                                                     sch->schid.ssid, cdev);
+               dev_id.devno = sch->schib.pmcw.dev;
+               dev_id.ssid = sch->schid.ssid;
+               other_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev);
                if (other_cdev) {
                        struct subchannel *other_sch;
 
@@ -626,12 +645,12 @@ ccw_device_do_unreg_rereg(void *data)
                                        other_sch->schib.pmcw.intparm = 0;
                                        cio_modify(other_sch);
                                }
-                               device_unregister(&other_sch->dev);
+                               css_sch_device_unregister(other_sch);
                        }
                }
                /* Update ssd info here. */
                css_get_ssd_info(sch);
-               cdev->private->devno = sch->schib.pmcw.dev;
+               cdev->private->dev_id.devno = sch->schib.pmcw.dev;
        } else
                need_rename = 0;
        device_remove_files(&cdev->dev);
@@ -641,7 +660,7 @@ ccw_device_do_unreg_rereg(void *data)
                snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x",
                          sch->schid.ssid, sch->schib.pmcw.dev);
        PREPARE_WORK(&cdev->private->kick_work,
-                    ccw_device_add_changed, (void *)cdev);
+                    ccw_device_add_changed, cdev);
        queue_work(ccw_device_work, &cdev->private->kick_work);
 }
 
@@ -666,7 +685,7 @@ io_subchannel_register(void *data)
        int ret;
        unsigned long flags;
 
-       cdev = (struct ccw_device *) data;
+       cdev = data;
        sch = to_subchannel(cdev->dev.parent);
 
        if (klist_node_attached(&cdev->dev.knode_parent)) {
@@ -710,7 +729,7 @@ ccw_device_call_sch_unregister(void *data)
        struct subchannel *sch;
 
        sch = to_subchannel(cdev->dev.parent);
-       device_unregister(&sch->dev);
+       css_sch_device_unregister(sch);
        /* Reset intparm to zeroes. */
        sch->schib.pmcw.intparm = 0;
        cio_modify(sch);
@@ -738,7 +757,7 @@ io_subchannel_recog_done(struct ccw_device *cdev)
                        break;
                sch = to_subchannel(cdev->dev.parent);
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_call_sch_unregister, (void *) cdev);
+                            ccw_device_call_sch_unregister, cdev);
                queue_work(slow_path_wq, &cdev->private->kick_work);
                if (atomic_dec_and_test(&ccw_device_init_count))
                        wake_up(&ccw_device_init_wq);
@@ -753,7 +772,7 @@ io_subchannel_recog_done(struct ccw_device *cdev)
                if (!get_device(&cdev->dev))
                        break;
                PREPARE_WORK(&cdev->private->kick_work,
-                            io_subchannel_register, (void *) cdev);
+                            io_subchannel_register, cdev);
                queue_work(slow_path_wq, &cdev->private->kick_work);
                break;
        }
@@ -771,9 +790,9 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
 
        /* Init private data. */
        priv = cdev->private;
-       priv->devno = sch->schib.pmcw.dev;
-       priv->ssid = sch->schid.ssid;
-       priv->sch_no = sch->schid.sch_no;
+       priv->dev_id.devno = sch->schib.pmcw.dev;
+       priv->dev_id.ssid = sch->schid.ssid;
+       priv->schid = sch->schid;
        priv->state = DEV_STATE_NOT_OPER;
        INIT_LIST_HEAD(&priv->cmb_list);
        init_waitqueue_head(&priv->wait_q);
@@ -836,10 +855,8 @@ io_subchannel_probe (struct subchannel *sch)
                return -ENOMEM;
        }
        atomic_set(&cdev->private->onoff, 0);
-       cdev->dev = (struct device) {
-               .parent = &sch->dev,
-               .release = ccw_device_release,
-       };
+       cdev->dev.parent = &sch->dev;
+       cdev->dev.release = ccw_device_release;
        INIT_LIST_HEAD(&cdev->private->kick_work.entry);
        /* Do first half of device_register. */
        device_initialize(&cdev->dev);
@@ -893,7 +910,7 @@ io_subchannel_remove (struct subchannel *sch)
         */
        if (get_device(&cdev->dev)) {
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_unregister, (void *) cdev);
+                            ccw_device_unregister, cdev);
                queue_work(ccw_device_work, &cdev->private->kick_work);
        }
        return 0;
@@ -978,9 +995,7 @@ ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch)
        int rc;
 
        /* Initialize the ccw_device structure. */
-       cdev->dev = (struct device) {
-               .parent = &sch->dev,
-       };
+       cdev->dev.parent= &sch->dev;
        rc = io_subchannel_recog(cdev, sch);
        if (rc)
                return rc;
@@ -1038,7 +1053,7 @@ __ccwdev_check_busid(struct device *dev, void *id)
 {
        char *bus_id;
 
-       bus_id = (char *)id;
+       bus_id = id;
 
        return (strncmp(bus_id, dev->bus_id, BUS_ID_SIZE) == 0);
 }
@@ -1058,7 +1073,7 @@ get_ccwdev_by_busid(struct ccw_driver *cdrv, const char *bus_id)
                                 __ccwdev_check_busid);
        put_driver(drv);
 
-       return dev ? to_ccwdev(dev) : 0;
+       return dev ? to_ccwdev(dev) : NULL;
 }
 
 /************************** device driver handling ************************/
@@ -1083,7 +1098,7 @@ ccw_device_probe (struct device *dev)
        ret = cdrv->probe ? cdrv->probe(cdev) : -ENODEV;
 
        if (ret) {
-               cdev->drv = 0;
+               cdev->drv = NULL;
                return ret;
        }
 
@@ -1114,7 +1129,7 @@ ccw_device_remove (struct device *dev)
                                 ret, cdev->dev.bus_id);
        }
        ccw_device_set_timeout(cdev, 0);
-       cdev->drv = 0;
+       cdev->drv = NULL;
        return 0;
 }