diff options
Diffstat (limited to 'drivers/firewire/core-device.c')
| -rw-r--r-- | drivers/firewire/core-device.c | 317 |
1 files changed, 185 insertions, 132 deletions
diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 6113b896e79..2c6d5e118ac 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -32,15 +32,15 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/random.h> #include <linux/rwsem.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/string.h> #include <linux/workqueue.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/byteorder.h> -#include <asm/system.h> #include "core.h" @@ -165,25 +165,44 @@ static bool match_ids(const struct ieee1394_device_id *id_table, int *id) return (match & id_table->match_flags) == id_table->match_flags; } -static bool is_fw_unit(struct device *dev); - -static int fw_unit_match(struct device *dev, struct device_driver *drv) +static const struct ieee1394_device_id *unit_match(struct device *dev, + struct device_driver *drv) { const struct ieee1394_device_id *id_table = container_of(drv, struct fw_driver, driver)->id_table; int id[] = {0, 0, 0, 0}; - /* We only allow binding to fw_units. */ - if (!is_fw_unit(dev)) - return 0; - get_modalias_ids(fw_unit(dev), id); for (; id_table->match_flags != 0; id_table++) if (match_ids(id_table, id)) - return 1; + return id_table; - return 0; + return NULL; +} + +static bool is_fw_unit(struct device *dev); + +static int fw_unit_match(struct device *dev, struct device_driver *drv) +{ + /* We only allow binding to fw_units. */ + return is_fw_unit(dev) && unit_match(dev, drv) != NULL; +} + +static int fw_unit_probe(struct device *dev) +{ + struct fw_driver *driver = + container_of(dev->driver, struct fw_driver, driver); + + return driver->probe(fw_unit(dev), unit_match(dev, dev->driver)); +} + +static int fw_unit_remove(struct device *dev) +{ + struct fw_driver *driver = + container_of(dev->driver, struct fw_driver, driver); + + return driver->remove(fw_unit(dev)), 0; } static int get_modalias(struct fw_unit *unit, char *buffer, size_t buffer_size) @@ -213,6 +232,8 @@ static int fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env) struct bus_type fw_bus_type = { .name = "firewire", .match = fw_unit_match, + .probe = fw_unit_probe, + .remove = fw_unit_remove, }; EXPORT_SYMBOL(fw_bus_type); @@ -399,6 +420,14 @@ static ssize_t guid_show(struct device *dev, return ret; } +static ssize_t is_local_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fw_device *device = fw_device(dev); + + return sprintf(buf, "%u\n", device->is_local); +} + static int units_sprintf(char *buf, const u32 *directory) { struct fw_csr_iterator ci; @@ -448,6 +477,7 @@ static ssize_t units_show(struct device *dev, static struct device_attribute fw_device_attributes[] = { __ATTR_RO(config_rom), __ATTR_RO(guid), + __ATTR_RO(is_local), __ATTR_RO(units), __ATTR_NULL, }; @@ -455,15 +485,20 @@ static struct device_attribute fw_device_attributes[] = { static int read_rom(struct fw_device *device, int generation, int index, u32 *data) { - int rcode; + u64 offset = (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + index * 4; + int i, rcode; /* device->node_id, accessed below, must not be older than generation */ smp_rmb(); - rcode = fw_run_transaction(device->card, TCODE_READ_QUADLET_REQUEST, - device->node_id, generation, device->max_speed, - (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + index * 4, - data, 4); + for (i = 10; i < 100; i += 10) { + rcode = fw_run_transaction(device->card, + TCODE_READ_QUADLET_REQUEST, device->node_id, + generation, device->max_speed, offset, data, 4); + if (rcode != RCODE_BUSY) + break; + msleep(i); + } be32_to_cpus(data); return rcode; @@ -477,13 +512,15 @@ static int read_rom(struct fw_device *device, * generation changes under us, read_config_rom 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. + * Returns either a result code or a negative error code. */ static int read_config_rom(struct fw_device *device, int generation) { + struct fw_card *card = device->card; const u32 *old_rom, *new_rom; u32 *rom, *stack; u32 sp, key; - int i, end, length, ret = -1; + int i, end, length, ret; rom = kmalloc(sizeof(*rom) * MAX_CONFIG_ROM_SIZE + sizeof(*stack) * MAX_CONFIG_ROM_SIZE, GFP_KERNEL); @@ -497,18 +534,21 @@ static int read_config_rom(struct fw_device *device, int generation) /* First read the bus info block. */ for (i = 0; i < 5; i++) { - if (read_rom(device, generation, i, &rom[i]) != RCODE_COMPLETE) + ret = read_rom(device, generation, i, &rom[i]); + if (ret != RCODE_COMPLETE) goto out; /* - * As per IEEE1212 7.2, during power-up, devices can + * As per IEEE1212 7.2, during initialization, devices can * reply with a 0 for the first quadlet of the config * rom to indicate that they are booting (for example, * if the firmware is on the disk of a external * harddisk). In that case we just fail, and the * retry mechanism will try again later. */ - if (i == 0 && rom[i] == 0) + if (i == 0 && rom[i] == 0) { + ret = RCODE_BUSY; goto out; + } } device->max_speed = device->node->max_speed; @@ -524,12 +564,12 @@ static int read_config_rom(struct fw_device *device, int generation) */ if ((rom[2] & 0x7) < device->max_speed || device->max_speed == SCODE_BETA || - device->card->beta_repeaters_present) { + card->beta_repeaters_present) { u32 dummy; /* for S1600 and S3200 */ if (device->max_speed == SCODE_BETA) - device->max_speed = device->card->link_speed; + device->max_speed = card->link_speed; while (device->max_speed > SCODE_100) { if (read_rom(device, generation, 0, &dummy) == @@ -558,11 +598,14 @@ static int read_config_rom(struct fw_device *device, int generation) */ key = stack[--sp]; i = key & 0xffffff; - if (WARN_ON(i >= MAX_CONFIG_ROM_SIZE)) + if (WARN_ON(i >= MAX_CONFIG_ROM_SIZE)) { + ret = -ENXIO; goto out; + } /* Read header quadlet for the block to get the length. */ - if (read_rom(device, generation, i, &rom[i]) != RCODE_COMPLETE) + ret = read_rom(device, generation, i, &rom[i]); + if (ret != RCODE_COMPLETE) goto out; end = i + (rom[i] >> 16) + 1; if (end > MAX_CONFIG_ROM_SIZE) { @@ -571,9 +614,9 @@ static int read_config_rom(struct fw_device *device, int generation) * a firmware bug. Ignore this whole block, i.e. * simply set a fake block length of 0. */ - fw_error("skipped invalid ROM block %x at %llx\n", - rom[i], - i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); + fw_err(card, "skipped invalid ROM block %x at %llx\n", + rom[i], + i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); rom[i] = 0; end = i; } @@ -585,8 +628,8 @@ static int read_config_rom(struct fw_device *device, int generation) * it references another block, and push it in that case. */ for (; i < end; i++) { - if (read_rom(device, generation, i, &rom[i]) != - RCODE_COMPLETE) + ret = read_rom(device, generation, i, &rom[i]); + if (ret != RCODE_COMPLETE) goto out; if ((key >> 30) != 3 || (rom[i] >> 30) < 2) @@ -599,9 +642,10 @@ static int read_config_rom(struct fw_device *device, int generation) * the ROM don't have to check offsets all the time. */ if (i + (rom[i] & 0xffffff) >= MAX_CONFIG_ROM_SIZE) { - fw_error("skipped unsupported ROM entry %x at %llx\n", - rom[i], - i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); + fw_err(card, + "skipped unsupported ROM entry %x at %llx\n", + rom[i], + i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); rom[i] = 0; continue; } @@ -613,8 +657,10 @@ static int read_config_rom(struct fw_device *device, int generation) old_rom = device->config_rom; new_rom = kmemdup(rom, length * 4, GFP_KERNEL); - if (new_rom == NULL) + if (new_rom == NULL) { + ret = -ENOMEM; goto out; + } down_write(&fw_device_rwsem); device->config_rom = new_rom; @@ -622,7 +668,7 @@ static int read_config_rom(struct fw_device *device, int generation) up_write(&fw_device_rwsem); kfree(old_rom); - ret = 0; + ret = RCODE_COMPLETE; device->max_rec = rom[2] >> 12 & 0xf; device->cmc = rom[2] >> 30 & 1; device->irmc = rom[2] >> 31 & 1; @@ -636,6 +682,7 @@ static void fw_unit_release(struct device *dev) { struct fw_unit *unit = fw_unit(dev); + fw_device_put(fw_parent_device(unit)); kfree(unit); } @@ -666,10 +713,8 @@ static void create_units(struct fw_device *device) * match the drivers id_tables against it. */ unit = kzalloc(sizeof(*unit), GFP_KERNEL); - if (unit == NULL) { - fw_error("failed to allocate memory for unit\n"); + if (unit == NULL) continue; - } unit->directory = ci.p + value - 1; unit->device.bus = &fw_bus_type; @@ -687,6 +732,7 @@ static void create_units(struct fw_device *device) if (device_register(&unit->device) < 0) goto skip_unit; + fw_device_get(device); continue; skip_unit: @@ -725,6 +771,15 @@ struct fw_device *fw_device_get_by_devt(dev_t devt) return device; } +struct workqueue_struct *fw_workqueue; +EXPORT_SYMBOL(fw_workqueue); + +static void fw_schedule_device_work(struct fw_device *device, + unsigned long delay) +{ + queue_delayed_work(fw_workqueue, &device->work, delay); +} + /* * These defines control the retry behavior for reading the config * rom. It shouldn't be necessary to tweak these; if the device @@ -747,9 +802,10 @@ static void fw_device_shutdown(struct work_struct *work) container_of(work, struct fw_device, work.work); int minor = MINOR(device->device.devt); - if (time_is_after_jiffies(device->card->reset_jiffies + SHUTDOWN_DELAY) + if (time_before64(get_jiffies_64(), + device->card->reset_jiffies + SHUTDOWN_DELAY) && !list_empty(&device->card->link)) { - schedule_delayed_work(&device->work, SHUTDOWN_DELAY); + fw_schedule_device_work(device, SHUTDOWN_DELAY); return; } @@ -858,10 +914,10 @@ static int lookup_existing_device(struct device *dev, void *data) smp_wmb(); /* update node_id before generation */ old->generation = card->generation; old->config_rom_retries = 0; - fw_notify("rediscovered device %s\n", dev_name(dev)); + fw_notice(card, "rediscovered device %s\n", dev_name(dev)); - PREPARE_DELAYED_WORK(&old->work, fw_device_update); - schedule_delayed_work(&old->work, 0); + old->workfn = fw_device_update; + fw_schedule_device_work(old, 0); if (current_node == card->root_node) fw_schedule_bm_work(card, 0); @@ -939,6 +995,7 @@ static void fw_device_init(struct work_struct *work) { struct fw_device *device = container_of(work, struct fw_device, work.work); + struct fw_card *card = device->card; struct device *revived_dev; int minor, ret; @@ -948,22 +1005,25 @@ static void fw_device_init(struct work_struct *work) * device. */ - if (read_config_rom(device, device->generation) < 0) { + ret = read_config_rom(device, device->generation); + if (ret != RCODE_COMPLETE) { if (device->config_rom_retries < MAX_RETRIES && atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { device->config_rom_retries++; - schedule_delayed_work(&device->work, RETRY_DELAY); + fw_schedule_device_work(device, RETRY_DELAY); } else { - fw_notify("giving up on config rom for node id %x\n", - device->node_id); - if (device->node == device->card->root_node) - fw_schedule_bm_work(device->card, 0); + if (device->node->link_on) + fw_notice(card, "giving up on node %x: reading config rom failed: %s\n", + device->node_id, + fw_rcode_string(ret)); + if (device->node == card->root_node) + fw_schedule_bm_work(card, 0); fw_device_release(&device->device); } return; } - revived_dev = device_find_child(device->card->device, + revived_dev = device_find_child(card->device, device, lookup_existing_device); if (revived_dev) { put_device(revived_dev); @@ -976,17 +1036,16 @@ static void fw_device_init(struct work_struct *work) fw_device_get(device); down_write(&fw_device_rwsem); - ret = idr_pre_get(&fw_device_idr, GFP_KERNEL) ? - idr_get_new(&fw_device_idr, device, &minor) : - -ENOMEM; + minor = idr_alloc(&fw_device_idr, device, 0, 1 << MINORBITS, + GFP_KERNEL); up_write(&fw_device_rwsem); - if (ret < 0) + if (minor < 0) goto error; device->device.bus = &fw_bus_type; device->device.type = &fw_device_type; - device->device.parent = device->card->device; + device->device.parent = card->device; device->device.devt = MKDEV(fw_cdev_major, minor); dev_set_name(&device->device, "fw%d", minor); @@ -998,7 +1057,7 @@ static void fw_device_init(struct work_struct *work) &device->attribute_group); if (device_add(&device->device)) { - fw_error("Failed to add device.\n"); + fw_err(card, "failed to add device\n"); goto error_with_cdev; } @@ -1016,24 +1075,18 @@ static void fw_device_init(struct work_struct *work) if (atomic_cmpxchg(&device->state, FW_DEVICE_INITIALIZING, FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { - PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); - schedule_delayed_work(&device->work, SHUTDOWN_DELAY); + device->workfn = fw_device_shutdown; + fw_schedule_device_work(device, SHUTDOWN_DELAY); } else { - if (device->config_rom_retries) - fw_notify("created device %s: GUID %08x%08x, S%d00, " - "%d config ROM retries\n", - dev_name(&device->device), - 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", - dev_name(&device->device), - device->config_rom[3], device->config_rom[4], - 1 << device->max_speed); + fw_notice(card, "created device %s: GUID %08x%08x, S%d00\n", + dev_name(&device->device), + device->config_rom[3], device->config_rom[4], + 1 << device->max_speed); device->config_rom_retries = 0; set_broadcast_channel(device, device->generation); + + add_device_randomness(&device->config_rom[3], 8); } /* @@ -1042,8 +1095,8 @@ static void fw_device_init(struct work_struct *work) * just end up running the IRM work a couple of extra times - * pretty harmless. */ - if (device->node == device->card->root_node) - fw_schedule_bm_work(device->card, 0); + if (device->node == card->root_node) + fw_schedule_bm_work(card, 0); return; @@ -1057,31 +1110,30 @@ static void fw_device_init(struct work_struct *work) put_device(&device->device); /* our reference */ } -enum { - REREAD_BIB_ERROR, - REREAD_BIB_GONE, - REREAD_BIB_UNCHANGED, - REREAD_BIB_CHANGED, -}; - /* Reread and compare bus info block and header of root directory */ -static int reread_config_rom(struct fw_device *device, int generation) +static int reread_config_rom(struct fw_device *device, int generation, + bool *changed) { u32 q; - int i; + int i, rcode; for (i = 0; i < 6; i++) { - if (read_rom(device, generation, i, &q) != RCODE_COMPLETE) - return REREAD_BIB_ERROR; + rcode = read_rom(device, generation, i, &q); + if (rcode != RCODE_COMPLETE) + return rcode; if (i == 0 && q == 0) - return REREAD_BIB_GONE; + /* inaccessible (see read_config_rom); retry later */ + return RCODE_BUSY; - if (q != device->config_rom[i]) - return REREAD_BIB_CHANGED; + if (q != device->config_rom[i]) { + *changed = true; + return RCODE_COMPLETE; + } } - return REREAD_BIB_UNCHANGED; + *changed = false; + return RCODE_COMPLETE; } static void fw_device_refresh(struct work_struct *work) @@ -1089,23 +1141,14 @@ static void fw_device_refresh(struct work_struct *work) struct fw_device *device = container_of(work, struct fw_device, work.work); struct fw_card *card = device->card; - int node_id = device->node_id; + int ret, node_id = device->node_id; + bool changed; - switch (reread_config_rom(device, device->generation)) { - case REREAD_BIB_ERROR: - if (device->config_rom_retries < MAX_RETRIES / 2 && - atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { - device->config_rom_retries++; - schedule_delayed_work(&device->work, RETRY_DELAY / 2); + ret = reread_config_rom(device, device->generation, &changed); + if (ret != RCODE_COMPLETE) + goto failed_config_rom; - return; - } - goto give_up; - - case REREAD_BIB_GONE: - goto gone; - - case REREAD_BIB_UNCHANGED: + if (!changed) { if (atomic_cmpxchg(&device->state, FW_DEVICE_INITIALIZING, FW_DEVICE_RUNNING) == FW_DEVICE_GONE) @@ -1114,9 +1157,6 @@ static void fw_device_refresh(struct work_struct *work) fw_device_update(work); device->config_rom_retries = 0; goto out; - - case REREAD_BIB_CHANGED: - break; } /* @@ -1125,16 +1165,9 @@ static void fw_device_refresh(struct work_struct *work) */ device_for_each_child(&device->device, NULL, shutdown_unit); - if (read_config_rom(device, device->generation) < 0) { - if (device->config_rom_retries < MAX_RETRIES && - atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { - device->config_rom_retries++; - schedule_delayed_work(&device->work, RETRY_DELAY); - - return; - } - goto give_up; - } + ret = read_config_rom(device, device->generation); + if (ret != RCODE_COMPLETE) + goto failed_config_rom; fw_device_cdev_update(device); create_units(device); @@ -1147,30 +1180,48 @@ static void fw_device_refresh(struct work_struct *work) FW_DEVICE_RUNNING) == FW_DEVICE_GONE) goto gone; - fw_notify("refreshed device %s\n", dev_name(&device->device)); + fw_notice(card, "refreshed device %s\n", dev_name(&device->device)); device->config_rom_retries = 0; goto out; - give_up: - fw_notify("giving up on refresh of device %s\n", dev_name(&device->device)); + failed_config_rom: + if (device->config_rom_retries < MAX_RETRIES && + atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { + device->config_rom_retries++; + fw_schedule_device_work(device, RETRY_DELAY); + return; + } + + fw_notice(card, "giving up on refresh of device %s: %s\n", + dev_name(&device->device), fw_rcode_string(ret)); gone: atomic_set(&device->state, FW_DEVICE_GONE); - PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); - schedule_delayed_work(&device->work, SHUTDOWN_DELAY); + device->workfn = fw_device_shutdown; + fw_schedule_device_work(device, SHUTDOWN_DELAY); out: if (node_id == card->root_node->node_id) fw_schedule_bm_work(card, 0); } +static void fw_device_workfn(struct work_struct *work) +{ + struct fw_device *device = container_of(to_delayed_work(work), + struct fw_device, work); + device->workfn(work); +} + void fw_node_event(struct fw_card *card, struct fw_node *node, int event) { struct fw_device *device; switch (event) { case FW_NODE_CREATED: - case FW_NODE_LINK_ON: - if (!node->link_on) - break; + /* + * Attempt to scan the node, regardless whether its self ID has + * the L (link active) flag set or not. Some broken devices + * send L=0 but have an up-and-running link; others send L=1 + * without actually having a link. + */ create: device = kzalloc(sizeof(*device), GFP_ATOMIC); if (device == NULL) @@ -1208,11 +1259,13 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) * power-up after getting plugged in. We schedule the * first config rom scan half a second after bus reset. */ - INIT_DELAYED_WORK(&device->work, fw_device_init); - schedule_delayed_work(&device->work, INITIAL_DELAY); + device->workfn = fw_device_init; + INIT_DELAYED_WORK(&device->work, fw_device_workfn); + fw_schedule_device_work(device, INITIAL_DELAY); break; case FW_NODE_INITIATED_RESET: + case FW_NODE_LINK_ON: device = node->data; if (device == NULL) goto create; @@ -1223,23 +1276,23 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) if (atomic_cmpxchg(&device->state, FW_DEVICE_RUNNING, FW_DEVICE_INITIALIZING) == FW_DEVICE_RUNNING) { - PREPARE_DELAYED_WORK(&device->work, fw_device_refresh); - schedule_delayed_work(&device->work, + device->workfn = fw_device_refresh; + fw_schedule_device_work(device, device->is_local ? 0 : INITIAL_DELAY); } break; case FW_NODE_UPDATED: - if (!node->link_on || node->data == NULL) + device = node->data; + if (device == NULL) break; - 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); - schedule_delayed_work(&device->work, 0); + device->workfn = fw_device_update; + fw_schedule_device_work(device, 0); } break; @@ -1263,8 +1316,8 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) device = node->data; if (atomic_xchg(&device->state, FW_DEVICE_GONE) == FW_DEVICE_RUNNING) { - PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); - schedule_delayed_work(&device->work, + device->workfn = fw_device_shutdown; + fw_schedule_device_work(device, list_empty(&card->link) ? 0 : SHUTDOWN_DELAY); } break; |
