]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/firewire/fw-device.c
firewire: don't use extern on public symbols
[linux-2.6-omap-h63xx.git] / drivers / firewire / fw-device.c
index 5599265da4a6a442ec1deb3a00656a47127dec67..99d1c418d2b0daf0820775dd2c3bdd040cede06a 100644 (file)
 #include <linux/kthread.h>
 #include <linux/device.h>
 #include <linux/delay.h>
+#include <linux/idr.h>
+#include <linux/rwsem.h>
+#include <asm/semaphore.h>
+#include <linux/ctype.h>
 #include "fw-transaction.h"
 #include "fw-topology.h"
 #include "fw-device.h"
@@ -135,9 +139,6 @@ fw_unit_uevent(struct device *dev, char **envp, int num_envp,
        int length = 0;
        int i = 0;
 
-       if (!is_fw_unit(dev))
-               goto out;
-
        get_modalias(unit, modalias, sizeof modalias);
 
        if (add_uevent_var(envp, num_envp, &i,
@@ -145,7 +146,6 @@ fw_unit_uevent(struct device *dev, char **envp, int num_envp,
                           "MODALIAS=%s", modalias))
                return -ENOMEM;
 
- out:
        envp[i] = NULL;
 
        return 0;
@@ -154,18 +154,17 @@ fw_unit_uevent(struct device *dev, char **envp, int num_envp,
 struct bus_type fw_bus_type = {
        .name = "firewire",
        .match = fw_unit_match,
-       .uevent = fw_unit_uevent,
 };
 EXPORT_SYMBOL(fw_bus_type);
 
-extern struct fw_device *fw_device_get(struct fw_device *device)
+struct fw_device *fw_device_get(struct fw_device *device)
 {
        get_device(&device->device);
 
        return device;
 }
 
-extern void fw_device_put(struct fw_device *device)
+void fw_device_put(struct fw_device *device)
 {
        put_device(&device->device);
 }
@@ -195,9 +194,129 @@ int fw_device_enable_phys_dma(struct fw_device *device)
 }
 EXPORT_SYMBOL(fw_device_enable_phys_dma);
 
+struct config_rom_attribute {
+       struct device_attribute attr;
+       u32 key;
+};
+
+static ssize_t
+show_immediate(struct device *dev, struct device_attribute *dattr, char *buf)
+{
+       struct config_rom_attribute *attr =
+               container_of(dattr, struct config_rom_attribute, attr);
+       struct fw_csr_iterator ci;
+       u32 *dir;
+       int key, value;
+
+       if (is_fw_unit(dev))
+               dir = fw_unit(dev)->directory;
+       else
+               dir = fw_device(dev)->config_rom + 5;
+
+       fw_csr_iterator_init(&ci, dir);
+       while (fw_csr_iterator_next(&ci, &key, &value))
+               if (attr->key == key)
+                       return snprintf(buf, buf ? PAGE_SIZE : 0,
+                                       "0x%06x\n", value);
+
+       return -ENOENT;
+}
+
+#define IMMEDIATE_ATTR(name, key)                              \
+       { __ATTR(name, S_IRUGO, show_immediate, NULL), key }
+
+static ssize_t
+show_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf)
+{
+       struct config_rom_attribute *attr =
+               container_of(dattr, struct config_rom_attribute, attr);
+       struct fw_csr_iterator ci;
+       u32 *dir, *block = NULL, *p, *end;
+       int length, key, value, last_key = 0;
+       char *b;
+
+       if (is_fw_unit(dev))
+               dir = fw_unit(dev)->directory;
+       else
+               dir = fw_device(dev)->config_rom + 5;
+
+       fw_csr_iterator_init(&ci, dir);
+       while (fw_csr_iterator_next(&ci, &key, &value)) {
+               if (attr->key == last_key &&
+                   key == (CSR_DESCRIPTOR | CSR_LEAF))
+                       block = ci.p - 1 + value;
+               last_key = key;
+       }
+
+       if (block == NULL)
+               return -ENOENT;
+
+       length = min(block[0] >> 16, 256U);
+       if (length < 3)
+               return -ENOENT;
+
+       if (block[1] != 0 || block[2] != 0)
+               /* Unknown encoding. */
+               return -ENOENT;
+
+       if (buf == NULL)
+               return length * 4;
+
+       b = buf;
+       end = &block[length + 1];
+       for (p = &block[3]; p < end; p++, b += 4)
+               * (u32 *) b = (__force u32) __cpu_to_be32(*p);
+
+       /* Strip trailing whitespace and add newline. */
+       while (b--, (isspace(*b) || *b == '\0') && b > buf);
+       strcpy(b + 1, "\n");
+
+       return b + 2 - buf;
+}
+
+#define TEXT_LEAF_ATTR(name, key)                              \
+       { __ATTR(name, S_IRUGO, show_text_leaf, NULL), key }
+
+static struct config_rom_attribute config_rom_attributes[] = {
+       IMMEDIATE_ATTR(vendor, CSR_VENDOR),
+       IMMEDIATE_ATTR(hardware_version, CSR_HARDWARE_VERSION),
+       IMMEDIATE_ATTR(specifier_id, CSR_SPECIFIER_ID),
+       IMMEDIATE_ATTR(version, CSR_VERSION),
+       IMMEDIATE_ATTR(model, CSR_MODEL),
+       TEXT_LEAF_ATTR(vendor_name, CSR_VENDOR),
+       TEXT_LEAF_ATTR(model_name, CSR_MODEL),
+       TEXT_LEAF_ATTR(hardware_version_name, CSR_HARDWARE_VERSION),
+};
+
+static void
+init_fw_attribute_group(struct device *dev,
+                       struct device_attribute *attrs,
+                       struct fw_attribute_group *group)
+{
+       struct device_attribute *attr;
+       int i, j;
+
+       for (j = 0; attrs[j].attr.name != NULL; j++)
+               group->attrs[j] = &attrs[j].attr;
+
+       for (i = 0; i < ARRAY_SIZE(config_rom_attributes); i++) {
+               attr = &config_rom_attributes[i].attr;
+               if (attr->show(dev, attr, NULL) < 0)
+                       continue;
+               group->attrs[j++] = &attr->attr;
+       }
+
+       BUG_ON(j >= ARRAY_SIZE(group->attrs));
+       group->attrs[j++] = NULL;
+       group->groups[0] = &group->group;
+       group->groups[1] = NULL;
+       group->group.attrs = group->attrs;
+       dev->groups = group->groups;
+}
+
 static ssize_t
-show_modalias_attribute(struct device *dev,
-                       struct device_attribute *attr, char *buf)
+modalias_show(struct device *dev,
+             struct device_attribute *attr, char *buf)
 {
        struct fw_unit *unit = fw_unit(dev);
        int length;
@@ -208,14 +327,25 @@ show_modalias_attribute(struct device *dev,
        return length + 1;
 }
 
-static struct device_attribute modalias_attribute = {
-       .attr = { .name = "modalias", .mode = S_IRUGO, },
-       .show = show_modalias_attribute,
+static ssize_t
+rom_index_show(struct device *dev,
+              struct device_attribute *attr, char *buf)
+{
+       struct fw_device *device = fw_device(dev->parent);
+       struct fw_unit *unit = fw_unit(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+                       (int)(unit->directory - device->config_rom));
+}
+
+static struct device_attribute fw_unit_attributes[] = {
+       __ATTR_RO(modalias),
+       __ATTR_RO(rom_index),
+       __ATTR_NULL,
 };
 
 static ssize_t
-show_config_rom_attribute(struct device *dev,
-                         struct device_attribute *attr, char *buf)
+config_rom_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct fw_device *device = fw_device(dev);
 
@@ -224,9 +354,22 @@ show_config_rom_attribute(struct device *dev,
        return device->config_rom_length * 4;
 }
 
-static struct device_attribute config_rom_attribute = {
-       .attr = {.name = "config_rom", .mode = S_IRUGO,},
-       .show = show_config_rom_attribute,
+static ssize_t
+guid_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct fw_device *device = fw_device(dev);
+       u64 guid;
+
+       guid = ((u64)device->config_rom[3] << 32) | device->config_rom[4];
+
+       return snprintf(buf, PAGE_SIZE, "0x%016llx\n",
+                       (unsigned long long)guid);
+}
+
+static struct device_attribute fw_device_attributes[] = {
+       __ATTR_RO(config_rom),
+       __ATTR_RO(guid),
+       __ATTR_NULL,
 };
 
 struct read_quadlet_callback_data {
@@ -351,9 +494,14 @@ static void fw_unit_release(struct device *dev)
        kfree(unit);
 }
 
+static struct device_type fw_unit_type = {
+       .uevent         = fw_unit_uevent,
+       .release        = fw_unit_release,
+};
+
 static int is_fw_unit(struct device *dev)
 {
-       return dev->release == fw_unit_release;
+       return dev->type == &fw_unit_type;
 }
 
 static void create_units(struct fw_device *device)
@@ -378,47 +526,65 @@ static void create_units(struct fw_device *device)
 
                unit->directory = ci.p + value - 1;
                unit->device.bus = &fw_bus_type;
-               unit->device.release = fw_unit_release;
+               unit->device.type = &fw_unit_type;
                unit->device.parent = &device->device;
                snprintf(unit->device.bus_id, sizeof unit->device.bus_id,
                         "%s.%d", device->device.bus_id, i++);
 
-               if (device_register(&unit->device) < 0) {
-                       kfree(unit);
-                       continue;
-               }
+               init_fw_attribute_group(&unit->device,
+                                       fw_unit_attributes,
+                                       &unit->attribute_group);
+               if (device_register(&unit->device) < 0)
+                       goto skip_unit;
 
-               if (device_create_file(&unit->device, &modalias_attribute) < 0) {
-                       device_unregister(&unit->device);
-                       kfree(unit);
-               }
+               continue;
+
+       skip_unit:
+               kfree(unit);
        }
 }
 
 static int shutdown_unit(struct device *device, void *data)
 {
-       struct fw_unit *unit = fw_unit(device);
-
-       if (is_fw_unit(device)) {
-               device_remove_file(&unit->device, &modalias_attribute);
-               device_unregister(&unit->device);
-       }
+       device_unregister(device);
 
        return 0;
 }
 
+static DECLARE_RWSEM(idr_rwsem);
+static DEFINE_IDR(fw_device_idr);
+int fw_cdev_major;
+
+struct fw_device *fw_device_from_devt(dev_t devt)
+{
+       struct fw_device *device;
+
+       down_read(&idr_rwsem);
+       device = idr_find(&fw_device_idr, MINOR(devt));
+       up_read(&idr_rwsem);
+
+       return device;
+}
+
 static void fw_device_shutdown(struct work_struct *work)
 {
        struct fw_device *device =
                container_of(work, struct fw_device, work.work);
+       int minor = MINOR(device->device.devt);
 
-       device_remove_file(&device->device, &config_rom_attribute);
-       cdev_del(&device->cdev);
-       unregister_chrdev_region(device->device.devt, 1);
+       down_write(&idr_rwsem);
+       idr_remove(&fw_device_idr, minor);
+       up_write(&idr_rwsem);
+
+       fw_device_cdev_remove(device);
        device_for_each_child(&device->device, NULL, shutdown_unit);
        device_unregister(&device->device);
 }
 
+static struct device_type fw_device_type = {
+       .release        = fw_device_release,
+};
+
 /* These defines control the retry behavior for reading the config
  * rom.  It shouldn't be necessary to tweak these; if the device
  * doesn't respond to a config rom read within 10 seconds, it's not
@@ -428,15 +594,15 @@ static void fw_device_shutdown(struct work_struct *work)
  * aggressive than that, since it scales pretty well; if 10 devices
  * are plugged in, they're all getting read within one second. */
 
-#define MAX_RETRIES    5
-#define RETRY_DELAY    (2 * HZ)
+#define MAX_RETRIES    10
+#define RETRY_DELAY    (3 * HZ)
 #define INITIAL_DELAY  (HZ / 2)
 
 static void fw_device_init(struct work_struct *work)
 {
-       static atomic_t serial = ATOMIC_INIT(-1);
        struct fw_device *device =
                container_of(work, struct fw_device, work.work);
+       int minor, err;
 
        /* All failure paths here set node->data to NULL, so that we
         * don't try to do device_for_each_child() on a kfree()'d
@@ -456,33 +622,27 @@ static void fw_device_init(struct work_struct *work)
                return;
        }
 
+       err = -ENOMEM;
+       down_write(&idr_rwsem);
+       if (idr_pre_get(&fw_device_idr, GFP_KERNEL))
+               err = idr_get_new(&fw_device_idr, device, &minor);
+       up_write(&idr_rwsem);
+       if (err < 0)
+               goto error;
+
        device->device.bus = &fw_bus_type;
-       device->device.release = fw_device_release;
+       device->device.type = &fw_device_type;
        device->device.parent = device->card->device;
+       device->device.devt = MKDEV(fw_cdev_major, minor);
        snprintf(device->device.bus_id, sizeof device->device.bus_id,
-                "fw%d", atomic_inc_return(&serial));
-
-       if (alloc_chrdev_region(&device->device.devt, 0, 1, "fw")) {
-               fw_error("Failed to register char device region.\n");
-               goto error;
-       }
-
-       cdev_init(&device->cdev, &fw_device_ops);
-       device->cdev.owner = THIS_MODULE;
-       kobject_set_name(&device->cdev.kobj, device->device.bus_id);
-       if (cdev_add(&device->cdev, device->device.devt, 1)) {
-               fw_error("Failed to register char device.\n");
-               goto error;
-       }
+                "fw%d", minor);
 
+       init_fw_attribute_group(&device->device,
+                               fw_device_attributes,
+                               &device->attribute_group);
        if (device_add(&device->device)) {
                fw_error("Failed to add device.\n");
-               goto error;
-       }
-
-       if (device_create_file(&device->device, &config_rom_attribute) < 0) {
-               fw_error("Failed to create config rom file.\n");
-               goto error_with_device;
+               goto error_with_cdev;
        }
 
        create_units(device);
@@ -511,11 +671,11 @@ static void fw_device_init(struct work_struct *work)
 
        return;
 
- error_with_device:
-       device_del(&device->device);
+ error_with_cdev:
+       down_write(&idr_rwsem);
+       idr_remove(&fw_device_idr, minor);
+       up_write(&idr_rwsem);
  error:
-       cdev_del(&device->cdev);
-       unregister_chrdev_region(device->device.devt, 1);
        put_device(&device->device);
 }
 
@@ -524,8 +684,11 @@ static int update_unit(struct device *dev, void *data)
        struct fw_unit *unit = fw_unit(dev);
        struct fw_driver *driver = (struct fw_driver *)dev->driver;
 
-       if (is_fw_unit(dev) && driver != NULL && driver->update != NULL)
+       if (is_fw_unit(dev) && driver != NULL && driver->update != NULL) {
+               down(&dev->sem);
                driver->update(unit);
+               up(&dev->sem);
+       }
 
        return 0;
 }
@@ -535,6 +698,7 @@ static void fw_device_update(struct work_struct *work)
        struct fw_device *device =
                container_of(work, struct fw_device, work.work);
 
+       fw_device_cdev_update(device);
        device_for_each_child(&device->device, NULL, update_unit);
 }
 
@@ -564,6 +728,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
                device->node = fw_node_get(node);
                device->node_id = node->node_id;
                device->generation = card->generation;
+               INIT_LIST_HEAD(&device->client_list);
 
                /* Set the node data to point back to this device so
                 * FW_NODE_UPDATED callbacks can update the node_id