X-Git-Url: http://pilppa.org/gitweb/?a=blobdiff_plain;f=drivers%2Ffirewire%2Ffw-device.c;h=2ab13e0f3469c2f3ccd4b68d120be57f4107c3a6;hb=9ef64cb4320df821638b508f79aa8b858cca99f0;hp=2b6586341635b9ed57dafc5f6ebb977924177010;hpb=e030dbf91a87da7e8be3be3ca781558695bea683;p=linux-2.6-omap-h63xx.git diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c index 2b658634163..2ab13e0f346 100644 --- a/drivers/firewire/fw-device.c +++ b/drivers/firewire/fw-device.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "fw-transaction.h" #include "fw-topology.h" @@ -130,23 +131,16 @@ static int get_modalias(struct fw_unit *unit, char *buffer, size_t buffer_size) } static int -fw_unit_uevent(struct device *dev, char **envp, int num_envp, - char *buffer, int buffer_size) +fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env) { struct fw_unit *unit = fw_unit(dev); char modalias[64]; - int length = 0; - int i = 0; get_modalias(unit, modalias, sizeof(modalias)); - if (add_uevent_var(envp, num_envp, &i, - buffer, buffer_size, &length, - "MODALIAS=%s", modalias)) + if (add_uevent_var(env, "MODALIAS=%s", modalias)) return -ENOMEM; - envp[i] = NULL; - return 0; } @@ -189,9 +183,14 @@ static void fw_device_release(struct device *dev) int fw_device_enable_phys_dma(struct fw_device *device) { + int generation = device->generation; + + /* device->node_id, accessed below, must not be older than generation */ + smp_rmb(); + return device->card->driver->enable_phys_dma(device->card, device->node_id, - device->generation); + generation); } EXPORT_SYMBOL(fw_device_enable_phys_dma); @@ -359,12 +358,9 @@ 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); + return snprintf(buf, PAGE_SIZE, "0x%08x%08x\n", + device->config_rom[3], device->config_rom[4]); } static struct device_attribute fw_device_attributes[] = { @@ -391,17 +387,21 @@ complete_transaction(struct fw_card *card, int rcode, complete(&callback_data->done); } -static int read_rom(struct fw_device *device, int index, u32 * data) +static int +read_rom(struct fw_device *device, int generation, int index, u32 *data) { struct read_quadlet_callback_data callback_data; struct fw_transaction t; u64 offset; + /* device->node_id, accessed below, must not be older than generation */ + smp_rmb(); + init_completion(&callback_data.done); offset = 0xfffff0000400ULL + index * 4; fw_send_request(device->card, &t, TCODE_READ_QUADLET_REQUEST, - device->node_id, device->generation, device->max_speed, + device->node_id, generation, device->max_speed, offset, NULL, 4, complete_transaction, &callback_data); wait_for_completion(&callback_data.done); @@ -411,7 +411,14 @@ static int read_rom(struct fw_device *device, int index, u32 * data) return callback_data.rcode; } -static int read_bus_info_block(struct fw_device *device) +/* + * Read the bus info block, perform a speed probe, and read all of the rest of + * the config ROM. We do all this with a cached bus generation. If the bus + * generation changes under us, read_bus_info_block will fail and get retried. + * It's better to start all over in this case because the node from which we + * are reading the ROM may have changed the ROM during the reset. + */ +static int read_bus_info_block(struct fw_device *device, int generation) { static u32 rom[256]; u32 stack[16], sp, key; @@ -421,7 +428,7 @@ static int read_bus_info_block(struct fw_device *device) /* First read the bus info block. */ for (i = 0; i < 5; i++) { - if (read_rom(device, i, &rom[i]) != RCODE_COMPLETE) + if (read_rom(device, generation, i, &rom[i]) != RCODE_COMPLETE) return -1; /* * As per IEEE1212 7.2, during power-up, devices can @@ -456,7 +463,8 @@ static int read_bus_info_block(struct fw_device *device) device->max_speed = device->card->link_speed; while (device->max_speed > SCODE_100) { - if (read_rom(device, 0, &dummy) == RCODE_COMPLETE) + if (read_rom(device, generation, 0, &dummy) == + RCODE_COMPLETE) break; device->max_speed--; } @@ -489,7 +497,7 @@ static int read_bus_info_block(struct fw_device *device) return -1; /* Read header quadlet for the block to get the length. */ - if (read_rom(device, i, &rom[i]) != RCODE_COMPLETE) + if (read_rom(device, generation, i, &rom[i]) != RCODE_COMPLETE) return -1; end = i + (rom[i] >> 16) + 1; i++; @@ -508,7 +516,8 @@ static int read_bus_info_block(struct fw_device *device) * it references another block, and push it in that case. */ while (i < end) { - if (read_rom(device, i, &rom[i]) != RCODE_COMPLETE) + if (read_rom(device, generation, i, &rom[i]) != + RCODE_COMPLETE) return -1; if ((key >> 30) == 3 && (rom[i] >> 30) > 1 && sp < ARRAY_SIZE(stack)) @@ -598,12 +607,14 @@ 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 *fw_device_get_by_devt(dev_t devt) { struct fw_device *device; down_read(&idr_rwsem); device = idr_find(&fw_device_idr, MINOR(devt)); + if (device) + fw_device_get(device); up_read(&idr_rwsem); return device; @@ -615,13 +626,14 @@ static void fw_device_shutdown(struct work_struct *work) container_of(work, struct fw_device, work.work); int minor = MINOR(device->device.devt); - 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); + + down_write(&idr_rwsem); + idr_remove(&fw_device_idr, minor); + up_write(&idr_rwsem); + fw_device_put(device); } static struct device_type fw_device_type = { @@ -655,7 +667,7 @@ static void fw_device_init(struct work_struct *work) * device. */ - if (read_bus_info_block(device) < 0) { + if (read_bus_info_block(device, device->generation) < 0) { if (device->config_rom_retries < MAX_RETRIES) { device->config_rom_retries++; schedule_delayed_work(&device->work, RETRY_DELAY); @@ -670,10 +682,13 @@ static void fw_device_init(struct work_struct *work) } err = -ENOMEM; + + fw_device_get(device); 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; @@ -705,13 +720,22 @@ static void fw_device_init(struct work_struct *work) */ if (atomic_cmpxchg(&device->state, FW_DEVICE_INITIALIZING, - FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) + FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) { fw_device_shutdown(&device->work.work); - else - fw_notify("created new fw device %s " - "(%d config rom retries, S%d00)\n", - device->device.bus_id, device->config_rom_retries, - 1 << device->max_speed); + } else { + if (device->config_rom_retries) + fw_notify("created device %s: GUID %08x%08x, S%d00, " + "%d config ROM retries\n", + device->device.bus_id, + device->config_rom[3], device->config_rom[4], + 1 << device->max_speed, + device->config_rom_retries); + else + fw_notify("created device %s: GUID %08x%08x, S%d00\n", + device->device.bus_id, + device->config_rom[3], device->config_rom[4], + 1 << device->max_speed); + } /* * Reschedule the IRM work if we just finished reading the @@ -729,7 +753,9 @@ static void fw_device_init(struct work_struct *work) idr_remove(&fw_device_idr, minor); up_write(&idr_rwsem); error: - put_device(&device->device); + fw_device_put(device); /* fw_device_idr's reference */ + + put_device(&device->device); /* our reference */ } static int update_unit(struct device *dev, void *data) @@ -808,6 +834,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) device = node->data; device->node_id = node->node_id; + smp_wmb(); /* update node_id before generation */ device->generation = card->generation; if (atomic_read(&device->state) == FW_DEVICE_RUNNING) { PREPARE_DELAYED_WORK(&device->work, fw_device_update);