#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 "fw-transaction.h"
#include "fw-topology.h"
#include "fw-device.h"
"MODALIAS=%s", modalias))
return -ENOMEM;
- out:
+ out:
envp[i] = NULL;
return 0;
}
struct bus_type fw_bus_type = {
- .name = "fw",
+ .name = "firewire",
.match = fw_unit_match,
.uevent = fw_unit_uevent,
};
.show = show_config_rom_attribute,
};
+static ssize_t
+show_rom_index_attribute(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 rom_index_attribute = {
+ .attr = { .name = "rom_index", .mode = S_IRUGO, },
+ .show = show_rom_index_attribute,
+};
+
struct read_quadlet_callback_data {
struct completion done;
int rcode;
device_unregister(&unit->device);
kfree(unit);
}
+
+ if (device_create_file(&unit->device, &rom_index_attribute) < 0) {
+ device_unregister(&unit->device);
+ kfree(unit);
+ }
}
}
return 0;
}
+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(&fw_bus_type.subsys.rwsem);
+ device = idr_find(&fw_device_idr, MINOR(devt));
+ up_read(&fw_bus_type.subsys.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);
+
+ down_write(&fw_bus_type.subsys.rwsem);
+ idr_remove(&fw_device_idr, minor);
+ up_write(&fw_bus_type.subsys.rwsem);
+ fw_device_cdev_remove(device);
device_remove_file(&device->device, &config_rom_attribute);
- cdev_del(&device->cdev);
- unregister_chrdev_region(device->device.devt, 1);
device_for_each_child(&device->device, NULL, shutdown_unit);
device_unregister(&device->device);
}
static void fw_device_init(struct work_struct *work)
{
- static int serial;
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
} else {
fw_notify("giving up on config rom for node id %x\n",
device->node_id);
+ if (device->node == device->card->root_node)
+ schedule_delayed_work(&device->card->work, 0);
fw_device_release(&device->device);
}
return;
}
+ err = -ENOMEM;
+ down_write(&fw_bus_type.subsys.rwsem);
+ if (idr_pre_get(&fw_device_idr, GFP_KERNEL))
+ err = idr_get_new(&fw_device_idr, device, &minor);
+ up_write(&fw_bus_type.subsys.rwsem);
+ if (err < 0)
+ goto error;
+
device->device.bus = &fw_bus_type;
device->device.release = fw_device_release;
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", 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);
if (device_add(&device->device)) {
fw_error("Failed to add device.\n");
- goto error;
+ goto error_with_cdev;
}
if (device_create_file(&device->device, &config_rom_attribute) < 0) {
* necessary. We have to use the atomic cmpxchg here to avoid
* racing with the FW_NODE_DESTROYED case in
* fw_node_event(). */
- if (cmpxchg(&device->state,
+ if (atomic_cmpxchg(&device->state,
FW_DEVICE_INITIALIZING,
FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN)
fw_device_shutdown(&device->work.work);
return;
- error_with_device:
+ error_with_device:
device_del(&device->device);
- error:
- cdev_del(&device->cdev);
- unregister_chrdev_region(device->device.devt, 1);
+ error_with_cdev:
+ down_write(&fw_bus_type.subsys.rwsem);
+ idr_remove(&fw_device_idr, minor);
+ up_write(&fw_bus_type.subsys.rwsem);
+ error:
put_device(&device->device);
}
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;
}
+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);
+}
+
void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
{
struct fw_device *device;
- /* Ignore events for the local node (i.e. the node that
- * corresponds to the ieee1394 controller in this linux box). */
- if (node == card->local_node)
- return;
-
switch (event) {
case FW_NODE_CREATED:
case FW_NODE_LINK_ON:
* device_for_each_child() in FW_NODE_UPDATED is
* doesn't freak out. */
device_initialize(&device->device);
- device->state = FW_DEVICE_INITIALIZING;
+ atomic_set(&device->state, FW_DEVICE_INITIALIZING);
device->card = fw_card_get(card);
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
device = node->data;
device->node_id = node->node_id;
device->generation = card->generation;
- device_for_each_child(&device->device, NULL, update_unit);
+ if (atomic_read(&device->state) == FW_DEVICE_RUNNING) {
+ PREPARE_DELAYED_WORK(&device->work, fw_device_update);
+ schedule_delayed_work(&device->work, 0);
+ }
break;
case FW_NODE_DESTROYED:
* the device in shutdown state to have that code fail
* to create the device. */
device = node->data;
- if (xchg(&device->state,
- FW_DEVICE_SHUTDOWN) == FW_DEVICE_RUNNING) {
- INIT_DELAYED_WORK(&device->work, fw_device_shutdown);
+ if (atomic_xchg(&device->state,
+ FW_DEVICE_SHUTDOWN) == FW_DEVICE_RUNNING) {
+ PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown);
schedule_delayed_work(&device->work, 0);
}
break;