Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eddeb0e2 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6: (43 commits)
  firewire: cleanups
  firewire: fix synchronization of gap counts
  firewire: wait until PHY configuration packet was transmitted (fix bus reset loop)
  firewire: remove unused struct member
  firewire: use bitwise and to get reg in handle_registers
  firewire: replace more hex values with defined csr constants
  firewire: reread config ROM when device reset the bus
  firewire: replace static ROM cache by allocated cache
  firewire: fw-ohci: work around generation bug in TI controllers (fix AV/C and more)
  firewire: fw-ohci: extend logging of bus generations and node ID
  firewire: fw-ohci: conditionally log busReset interrupts
  firewire: fw-ohci: don't append to AT context when it's not active
  firewire: fw-ohci: log regAccessFail events
  firewire: fw-ohci: make sure HCControl register LPS bit is set
  firewire: fw-ohci: missing PPC PMac feature calls in failure path
  firewire: fw-ohci: untangle a mixed unsigned/signed expression
  firewire: debug interrupt events
  firewire: fw-ohci: catch self_id_count == 0
  firewire: fw-ohci: add self ID error check
  firewire: fw-ohci: refactor probe, remove, suspend, resume
  ...
parents 855d854a db8be076
Loading
Loading
Loading
Loading
+10 −6
Original line number Original line Diff line number Diff line
@@ -41,15 +41,19 @@ to a working state and enables physical DMA by default for all remote nodes.
This can be turned off by ohci1394's module parameter phys_dma=0.
This can be turned off by ohci1394's module parameter phys_dma=0.


The alternative firewire-ohci driver in drivers/firewire uses filtered physical
The alternative firewire-ohci driver in drivers/firewire uses filtered physical
DMA, hence is not yet suitable for remote debugging.
DMA by default, which is more secure but not suitable for remote debugging.
Compile the driver with CONFIG_FIREWIRE_OHCI_REMOTE_DMA (Kernel hacking menu:
Remote debugging over FireWire with firewire-ohci) to get unfiltered physical
DMA.


Because ohci1394 depends on the PCI enumeration to be completed, an
Because ohci1394 and firewire-ohci depend on the PCI enumeration to be
initialization routine which runs pretty early (long before console_init()
completed, an initialization routine which runs pretty early has been
which makes the printk buffer appear on the console can be called) was written.
implemented for x86.  This routine runs long before console_init() can be
called, i.e. before the printk buffer appears on the console.


To activate it, enable CONFIG_PROVIDE_OHCI1394_DMA_INIT (Kernel hacking menu:
To activate it, enable CONFIG_PROVIDE_OHCI1394_DMA_INIT (Kernel hacking menu:
Provide code for enabling DMA over FireWire early on boot) and pass the
Remote debugging over FireWire early on boot) and pass the parameter
parameter "ohci1394_dma=early" to the recompiled kernel on boot.
"ohci1394_dma=early" to the recompiled kernel on boot.


Tools
Tools
-----
-----
+5 −0
Original line number Original line Diff line number Diff line
@@ -54,6 +54,11 @@ config FIREWIRE_OHCI
	  directive, use "install modulename /bin/true" for the modules to be
	  directive, use "install modulename /bin/true" for the modules to be
	  blacklisted.
	  blacklisted.


config FIREWIRE_OHCI_DEBUG
	bool
	depends on FIREWIRE_OHCI
	default y

config FIREWIRE_SBP2
config FIREWIRE_SBP2
	tristate "Support for storage devices (SBP-2 protocol driver)"
	tristate "Support for storage devices (SBP-2 protocol driver)"
	depends on FIREWIRE && SCSI
	depends on FIREWIRE && SCSI
+5 −45
Original line number Original line Diff line number Diff line
@@ -167,7 +167,6 @@ fw_core_add_descriptor(struct fw_descriptor *desc)


	return 0;
	return 0;
}
}
EXPORT_SYMBOL(fw_core_add_descriptor);


void
void
fw_core_remove_descriptor(struct fw_descriptor *desc)
fw_core_remove_descriptor(struct fw_descriptor *desc)
@@ -182,7 +181,6 @@ fw_core_remove_descriptor(struct fw_descriptor *desc)


	mutex_unlock(&card_mutex);
	mutex_unlock(&card_mutex);
}
}
EXPORT_SYMBOL(fw_core_remove_descriptor);


static const char gap_count_table[] = {
static const char gap_count_table[] = {
	63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40
	63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40
@@ -220,7 +218,7 @@ fw_card_bm_work(struct work_struct *work)
	struct bm_data bmd;
	struct bm_data bmd;
	unsigned long flags;
	unsigned long flags;
	int root_id, new_root_id, irm_id, gap_count, generation, grace;
	int root_id, new_root_id, irm_id, gap_count, generation, grace;
	int do_reset = 0;
	bool do_reset = false;


	spin_lock_irqsave(&card->lock, flags);
	spin_lock_irqsave(&card->lock, flags);
	local_node = card->local_node;
	local_node = card->local_node;
@@ -331,7 +329,7 @@ fw_card_bm_work(struct work_struct *work)
		 */
		 */
		spin_unlock_irqrestore(&card->lock, flags);
		spin_unlock_irqrestore(&card->lock, flags);
		goto out;
		goto out;
	} else if (root_device->config_rom[2] & BIB_CMC) {
	} else if (root_device->cmc) {
		/*
		/*
		 * FIXME: I suppose we should set the cmstr bit in the
		 * FIXME: I suppose we should set the cmstr bit in the
		 * STATE_CLEAR register of this node, as described in
		 * STATE_CLEAR register of this node, as described in
@@ -360,14 +358,14 @@ fw_card_bm_work(struct work_struct *work)
		gap_count = 63;
		gap_count = 63;


	/*
	/*
	 * Finally, figure out if we should do a reset or not.  If we've
	 * Finally, figure out if we should do a reset or not.  If we have
	 * done less that 5 resets with the same physical topology and we
	 * done less than 5 resets with the same physical topology and we
	 * have either a new root or a new gap count setting, let's do it.
	 * have either a new root or a new gap count setting, let's do it.
	 */
	 */


	if (card->bm_retries++ < 5 &&
	if (card->bm_retries++ < 5 &&
	    (card->gap_count != gap_count || new_root_id != root_id))
	    (card->gap_count != gap_count || new_root_id != root_id))
		do_reset = 1;
		do_reset = true;


	spin_unlock_irqrestore(&card->lock, flags);
	spin_unlock_irqrestore(&card->lock, flags);


@@ -398,7 +396,6 @@ fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver,
{
{
	static atomic_t index = ATOMIC_INIT(-1);
	static atomic_t index = ATOMIC_INIT(-1);


	kref_init(&card->kref);
	atomic_set(&card->device_count, 0);
	atomic_set(&card->device_count, 0);
	card->index = atomic_inc_return(&index);
	card->index = atomic_inc_return(&index);
	card->driver = driver;
	card->driver = driver;
@@ -429,12 +426,6 @@ fw_card_add(struct fw_card *card,
	card->link_speed = link_speed;
	card->link_speed = link_speed;
	card->guid = guid;
	card->guid = guid;


	/*
	 * The subsystem grabs a reference when the card is added and
	 * drops it when the driver calls fw_core_remove_card.
	 */
	fw_card_get(card);

	mutex_lock(&card_mutex);
	mutex_lock(&card_mutex);
	config_rom = generate_config_rom(card, &length);
	config_rom = generate_config_rom(card, &length);
	list_add_tail(&card->link, &card_list);
	list_add_tail(&card->link, &card_list);
@@ -540,40 +531,9 @@ fw_core_remove_card(struct fw_card *card)
	cancel_delayed_work_sync(&card->work);
	cancel_delayed_work_sync(&card->work);
	fw_flush_transactions(card);
	fw_flush_transactions(card);
	del_timer_sync(&card->flush_timer);
	del_timer_sync(&card->flush_timer);

	fw_card_put(card);
}
}
EXPORT_SYMBOL(fw_core_remove_card);
EXPORT_SYMBOL(fw_core_remove_card);


struct fw_card *
fw_card_get(struct fw_card *card)
{
	kref_get(&card->kref);

	return card;
}
EXPORT_SYMBOL(fw_card_get);

static void
release_card(struct kref *kref)
{
	struct fw_card *card = container_of(kref, struct fw_card, kref);

	kfree(card);
}

/*
 * An assumption for fw_card_put() is that the card driver allocates
 * the fw_card struct with kalloc and that it has been shut down
 * before the last ref is dropped.
 */
void
fw_card_put(struct fw_card *card)
{
	kref_put(&card->kref, release_card);
}
EXPORT_SYMBOL(fw_card_put);

int
int
fw_core_initiate_bus_reset(struct fw_card *card, int short_reset)
fw_core_initiate_bus_reset(struct fw_card *card, int short_reset)
{
{
+10 −3
Original line number Original line Diff line number Diff line
@@ -269,21 +269,28 @@ static int ioctl_get_info(struct client *client, void *buffer)
{
{
	struct fw_cdev_get_info *get_info = buffer;
	struct fw_cdev_get_info *get_info = buffer;
	struct fw_cdev_event_bus_reset bus_reset;
	struct fw_cdev_event_bus_reset bus_reset;
	unsigned long ret = 0;


	client->version = get_info->version;
	client->version = get_info->version;
	get_info->version = FW_CDEV_VERSION;
	get_info->version = FW_CDEV_VERSION;


	down_read(&fw_device_rwsem);

	if (get_info->rom != 0) {
	if (get_info->rom != 0) {
		void __user *uptr = u64_to_uptr(get_info->rom);
		void __user *uptr = u64_to_uptr(get_info->rom);
		size_t want = get_info->rom_length;
		size_t want = get_info->rom_length;
		size_t have = client->device->config_rom_length * 4;
		size_t have = client->device->config_rom_length * 4;


		if (copy_to_user(uptr, client->device->config_rom,
		ret = copy_to_user(uptr, client->device->config_rom,
				 min(want, have)))
				   min(want, have));
			return -EFAULT;
	}
	}
	get_info->rom_length = client->device->config_rom_length * 4;
	get_info->rom_length = client->device->config_rom_length * 4;


	up_read(&fw_device_rwsem);

	if (ret != 0)
		return -EFAULT;

	client->bus_reset_closure = get_info->bus_reset_closure;
	client->bus_reset_closure = get_info->bus_reset_closure;
	if (get_info->bus_reset != 0) {
	if (get_info->bus_reset != 0) {
		void __user *uptr = u64_to_uptr(get_info->bus_reset);
		void __user *uptr = u64_to_uptr(get_info->bus_reset);
+215 −48
Original line number Original line Diff line number Diff line
@@ -25,7 +25,7 @@
#include <linux/device.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/delay.h>
#include <linux/idr.h>
#include <linux/idr.h>
#include <linux/rwsem.h>
#include <linux/string.h>
#include <asm/semaphore.h>
#include <asm/semaphore.h>
#include <asm/system.h>
#include <asm/system.h>
#include <linux/ctype.h>
#include <linux/ctype.h>
@@ -160,9 +160,9 @@ static void fw_device_release(struct device *dev)
	 * Take the card lock so we don't set this to NULL while a
	 * Take the card lock so we don't set this to NULL while a
	 * FW_NODE_UPDATED callback is being handled.
	 * FW_NODE_UPDATED callback is being handled.
	 */
	 */
	spin_lock_irqsave(&device->card->lock, flags);
	spin_lock_irqsave(&card->lock, flags);
	device->node->data = NULL;
	device->node->data = NULL;
	spin_unlock_irqrestore(&device->card->lock, flags);
	spin_unlock_irqrestore(&card->lock, flags);


	fw_node_put(device->node);
	fw_node_put(device->node);
	kfree(device->config_rom);
	kfree(device->config_rom);
@@ -195,7 +195,9 @@ show_immediate(struct device *dev, struct device_attribute *dattr, char *buf)
		container_of(dattr, struct config_rom_attribute, attr);
		container_of(dattr, struct config_rom_attribute, attr);
	struct fw_csr_iterator ci;
	struct fw_csr_iterator ci;
	u32 *dir;
	u32 *dir;
	int key, value;
	int key, value, ret = -ENOENT;

	down_read(&fw_device_rwsem);


	if (is_fw_unit(dev))
	if (is_fw_unit(dev))
		dir = fw_unit(dev)->directory;
		dir = fw_unit(dev)->directory;
@@ -204,11 +206,15 @@ show_immediate(struct device *dev, struct device_attribute *dattr, char *buf)


	fw_csr_iterator_init(&ci, dir);
	fw_csr_iterator_init(&ci, dir);
	while (fw_csr_iterator_next(&ci, &key, &value))
	while (fw_csr_iterator_next(&ci, &key, &value))
		if (attr->key == key)
		if (attr->key == key) {
			return snprintf(buf, buf ? PAGE_SIZE : 0,
			ret = snprintf(buf, buf ? PAGE_SIZE : 0,
				       "0x%06x\n", value);
				       "0x%06x\n", value);
			break;
		}

	up_read(&fw_device_rwsem);


	return -ENOENT;
	return ret;
}
}


#define IMMEDIATE_ATTR(name, key)				\
#define IMMEDIATE_ATTR(name, key)				\
@@ -221,9 +227,11 @@ show_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf)
		container_of(dattr, struct config_rom_attribute, attr);
		container_of(dattr, struct config_rom_attribute, attr);
	struct fw_csr_iterator ci;
	struct fw_csr_iterator ci;
	u32 *dir, *block = NULL, *p, *end;
	u32 *dir, *block = NULL, *p, *end;
	int length, key, value, last_key = 0;
	int length, key, value, last_key = 0, ret = -ENOENT;
	char *b;
	char *b;


	down_read(&fw_device_rwsem);

	if (is_fw_unit(dev))
	if (is_fw_unit(dev))
		dir = fw_unit(dev)->directory;
		dir = fw_unit(dev)->directory;
	else
	else
@@ -238,18 +246,20 @@ show_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf)
	}
	}


	if (block == NULL)
	if (block == NULL)
		return -ENOENT;
		goto out;


	length = min(block[0] >> 16, 256U);
	length = min(block[0] >> 16, 256U);
	if (length < 3)
	if (length < 3)
		return -ENOENT;
		goto out;


	if (block[1] != 0 || block[2] != 0)
	if (block[1] != 0 || block[2] != 0)
		/* Unknown encoding. */
		/* Unknown encoding. */
		return -ENOENT;
		goto out;


	if (buf == NULL)
	if (buf == NULL) {
		return length * 4;
		ret = length * 4;
		goto out;
	}


	b = buf;
	b = buf;
	end = &block[length + 1];
	end = &block[length + 1];
@@ -259,8 +269,11 @@ show_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf)
	/* Strip trailing whitespace and add newline. */
	/* Strip trailing whitespace and add newline. */
	while (b--, (isspace(*b) || *b == '\0') && b > buf);
	while (b--, (isspace(*b) || *b == '\0') && b > buf);
	strcpy(b + 1, "\n");
	strcpy(b + 1, "\n");
	ret = b + 2 - buf;
 out:
	up_read(&fw_device_rwsem);


	return b + 2 - buf;
	return ret;
}
}


#define TEXT_LEAF_ATTR(name, key)				\
#define TEXT_LEAF_ATTR(name, key)				\
@@ -337,19 +350,28 @@ static ssize_t
config_rom_show(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);
	struct fw_device *device = fw_device(dev);
	size_t length;


	memcpy(buf, device->config_rom, device->config_rom_length * 4);
	down_read(&fw_device_rwsem);
	length = device->config_rom_length * 4;
	memcpy(buf, device->config_rom, length);
	up_read(&fw_device_rwsem);


	return device->config_rom_length * 4;
	return length;
}
}


static ssize_t
static ssize_t
guid_show(struct device *dev, struct device_attribute *attr, char *buf)
guid_show(struct device *dev, struct device_attribute *attr, char *buf)
{
{
	struct fw_device *device = fw_device(dev);
	struct fw_device *device = fw_device(dev);
	int ret;


	return snprintf(buf, PAGE_SIZE, "0x%08x%08x\n",
	down_read(&fw_device_rwsem);
	ret = snprintf(buf, PAGE_SIZE, "0x%08x%08x\n",
		       device->config_rom[3], device->config_rom[4]);
		       device->config_rom[3], device->config_rom[4]);
	up_read(&fw_device_rwsem);

	return ret;
}
}


static struct device_attribute fw_device_attributes[] = {
static struct device_attribute fw_device_attributes[] = {
@@ -388,7 +410,7 @@ read_rom(struct fw_device *device, int generation, int index, u32 *data)


	init_completion(&callback_data.done);
	init_completion(&callback_data.done);


	offset = 0xfffff0000400ULL + index * 4;
	offset = (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + index * 4;
	fw_send_request(device->card, &t, TCODE_READ_QUADLET_REQUEST,
	fw_send_request(device->card, &t, TCODE_READ_QUADLET_REQUEST,
			device->node_id, generation, device->max_speed,
			device->node_id, generation, device->max_speed,
			offset, NULL, 4, complete_transaction, &callback_data);
			offset, NULL, 4, complete_transaction, &callback_data);
@@ -400,6 +422,9 @@ read_rom(struct fw_device *device, int generation, int index, u32 *data)
	return callback_data.rcode;
	return callback_data.rcode;
}
}


#define READ_BIB_ROM_SIZE	256
#define READ_BIB_STACK_SIZE	16

/*
/*
 * Read the bus info block, perform a speed probe, and read all of the rest of
 * 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
 * the config ROM.  We do all this with a cached bus generation.  If the bus
@@ -409,16 +434,23 @@ read_rom(struct fw_device *device, int generation, int index, u32 *data)
 */
 */
static int read_bus_info_block(struct fw_device *device, int generation)
static int read_bus_info_block(struct fw_device *device, int generation)
{
{
	static u32 rom[256];
	u32 *rom, *stack, *old_rom, *new_rom;
	u32 stack[16], sp, key;
	u32 sp, key;
	int i, end, length;
	int i, end, length, ret = -1;

	rom = kmalloc(sizeof(*rom) * READ_BIB_ROM_SIZE +
		      sizeof(*stack) * READ_BIB_STACK_SIZE, GFP_KERNEL);
	if (rom == NULL)
		return -ENOMEM;

	stack = &rom[READ_BIB_ROM_SIZE];


	device->max_speed = SCODE_100;
	device->max_speed = SCODE_100;


	/* First read the bus info block. */
	/* First read the bus info block. */
	for (i = 0; i < 5; i++) {
	for (i = 0; i < 5; i++) {
		if (read_rom(device, generation, i, &rom[i]) != RCODE_COMPLETE)
		if (read_rom(device, generation, i, &rom[i]) != RCODE_COMPLETE)
			return -1;
			goto out;
		/*
		/*
		 * As per IEEE1212 7.2, during power-up, devices can
		 * As per IEEE1212 7.2, during power-up, devices can
		 * reply with a 0 for the first quadlet of the config
		 * reply with a 0 for the first quadlet of the config
@@ -428,7 +460,7 @@ static int read_bus_info_block(struct fw_device *device, int generation)
		 * retry mechanism will try again later.
		 * retry mechanism will try again later.
		 */
		 */
		if (i == 0 && rom[i] == 0)
		if (i == 0 && rom[i] == 0)
			return -1;
			goto out;
	}
	}


	device->max_speed = device->node->max_speed;
	device->max_speed = device->node->max_speed;
@@ -478,26 +510,26 @@ static int read_bus_info_block(struct fw_device *device, int generation)
		 */
		 */
		key = stack[--sp];
		key = stack[--sp];
		i = key & 0xffffff;
		i = key & 0xffffff;
		if (i >= ARRAY_SIZE(rom))
		if (i >= READ_BIB_ROM_SIZE)
			/*
			/*
			 * The reference points outside the standard
			 * The reference points outside the standard
			 * config rom area, something's fishy.
			 * config rom area, something's fishy.
			 */
			 */
			return -1;
			goto out;


		/* Read header quadlet for the block to get the length. */
		/* Read header quadlet for the block to get the length. */
		if (read_rom(device, generation, i, &rom[i]) != RCODE_COMPLETE)
		if (read_rom(device, generation, i, &rom[i]) != RCODE_COMPLETE)
			return -1;
			goto out;
		end = i + (rom[i] >> 16) + 1;
		end = i + (rom[i] >> 16) + 1;
		i++;
		i++;
		if (end > ARRAY_SIZE(rom))
		if (end > READ_BIB_ROM_SIZE)
			/*
			/*
			 * This block extends outside standard config
			 * This block extends outside standard config
			 * area (and the array we're reading it
			 * area (and the array we're reading it
			 * into).  That's broken, so ignore this
			 * into).  That's broken, so ignore this
			 * device.
			 * device.
			 */
			 */
			return -1;
			goto out;


		/*
		/*
		 * Now read in the block.  If this is a directory
		 * Now read in the block.  If this is a directory
@@ -507,9 +539,9 @@ static int read_bus_info_block(struct fw_device *device, int generation)
		while (i < end) {
		while (i < end) {
			if (read_rom(device, generation, i, &rom[i]) !=
			if (read_rom(device, generation, i, &rom[i]) !=
			    RCODE_COMPLETE)
			    RCODE_COMPLETE)
				return -1;
				goto out;
			if ((key >> 30) == 3 && (rom[i] >> 30) > 1 &&
			if ((key >> 30) == 3 && (rom[i] >> 30) > 1 &&
			    sp < ARRAY_SIZE(stack))
			    sp < READ_BIB_STACK_SIZE)
				stack[sp++] = i + rom[i];
				stack[sp++] = i + rom[i];
			i++;
			i++;
		}
		}
@@ -517,13 +549,23 @@ static int read_bus_info_block(struct fw_device *device, int generation)
			length = i;
			length = i;
	}
	}


	device->config_rom = kmalloc(length * 4, GFP_KERNEL);
	old_rom = device->config_rom;
	if (device->config_rom == NULL)
	new_rom = kmemdup(rom, length * 4, GFP_KERNEL);
		return -1;
	if (new_rom == NULL)
	memcpy(device->config_rom, rom, length * 4);
		goto out;

	down_write(&fw_device_rwsem);
	device->config_rom = new_rom;
	device->config_rom_length = length;
	device->config_rom_length = length;
	up_write(&fw_device_rwsem);


	return 0;
	kfree(old_rom);
	ret = 0;
	device->cmc = rom[2] & 1 << 30;
 out:
	kfree(rom);

	return ret;
}
}


static void fw_unit_release(struct device *dev)
static void fw_unit_release(struct device *dev)
@@ -592,7 +634,14 @@ static int shutdown_unit(struct device *device, void *data)
	return 0;
	return 0;
}
}


static DECLARE_RWSEM(idr_rwsem);
/*
 * fw_device_rwsem acts as dual purpose mutex:
 *   - serializes accesses to fw_device_idr,
 *   - serializes accesses to fw_device.config_rom/.config_rom_length and
 *     fw_unit.directory, unless those accesses happen at safe occasions
 */
DECLARE_RWSEM(fw_device_rwsem);

static DEFINE_IDR(fw_device_idr);
static DEFINE_IDR(fw_device_idr);
int fw_cdev_major;
int fw_cdev_major;


@@ -600,11 +649,11 @@ struct fw_device *fw_device_get_by_devt(dev_t devt)
{
{
	struct fw_device *device;
	struct fw_device *device;


	down_read(&idr_rwsem);
	down_read(&fw_device_rwsem);
	device = idr_find(&fw_device_idr, MINOR(devt));
	device = idr_find(&fw_device_idr, MINOR(devt));
	if (device)
	if (device)
		fw_device_get(device);
		fw_device_get(device);
	up_read(&idr_rwsem);
	up_read(&fw_device_rwsem);


	return device;
	return device;
}
}
@@ -619,9 +668,9 @@ static void fw_device_shutdown(struct work_struct *work)
	device_for_each_child(&device->device, NULL, shutdown_unit);
	device_for_each_child(&device->device, NULL, shutdown_unit);
	device_unregister(&device->device);
	device_unregister(&device->device);


	down_write(&idr_rwsem);
	down_write(&fw_device_rwsem);
	idr_remove(&fw_device_idr, minor);
	idr_remove(&fw_device_idr, minor);
	up_write(&idr_rwsem);
	up_write(&fw_device_rwsem);
	fw_device_put(device);
	fw_device_put(device);
}
}


@@ -674,10 +723,10 @@ static void fw_device_init(struct work_struct *work)
	err = -ENOMEM;
	err = -ENOMEM;


	fw_device_get(device);
	fw_device_get(device);
	down_write(&idr_rwsem);
	down_write(&fw_device_rwsem);
	if (idr_pre_get(&fw_device_idr, GFP_KERNEL))
	if (idr_pre_get(&fw_device_idr, GFP_KERNEL))
		err = idr_get_new(&fw_device_idr, device, &minor);
		err = idr_get_new(&fw_device_idr, device, &minor);
	up_write(&idr_rwsem);
	up_write(&fw_device_rwsem);


	if (err < 0)
	if (err < 0)
		goto error;
		goto error;
@@ -711,7 +760,7 @@ static void fw_device_init(struct work_struct *work)
	if (atomic_cmpxchg(&device->state,
	if (atomic_cmpxchg(&device->state,
		    FW_DEVICE_INITIALIZING,
		    FW_DEVICE_INITIALIZING,
		    FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) {
		    FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) {
		fw_device_shutdown(&device->work.work);
		fw_device_shutdown(work);
	} else {
	} else {
		if (device->config_rom_retries)
		if (device->config_rom_retries)
			fw_notify("created device %s: GUID %08x%08x, S%d00, "
			fw_notify("created device %s: GUID %08x%08x, S%d00, "
@@ -725,6 +774,7 @@ static void fw_device_init(struct work_struct *work)
				  device->device.bus_id,
				  device->device.bus_id,
				  device->config_rom[3], device->config_rom[4],
				  device->config_rom[3], device->config_rom[4],
				  1 << device->max_speed);
				  1 << device->max_speed);
		device->config_rom_retries = 0;
	}
	}


	/*
	/*
@@ -739,9 +789,9 @@ static void fw_device_init(struct work_struct *work)
	return;
	return;


 error_with_cdev:
 error_with_cdev:
	down_write(&idr_rwsem);
	down_write(&fw_device_rwsem);
	idr_remove(&fw_device_idr, minor);
	idr_remove(&fw_device_idr, minor);
	up_write(&idr_rwsem);
	up_write(&fw_device_rwsem);
 error:
 error:
	fw_device_put(device);		/* fw_device_idr's reference */
	fw_device_put(device);		/* fw_device_idr's reference */


@@ -771,6 +821,106 @@ static void fw_device_update(struct work_struct *work)
	device_for_each_child(&device->device, NULL, update_unit);
	device_for_each_child(&device->device, NULL, update_unit);
}
}


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_bus_info_block(struct fw_device *device, int generation)
{
	u32 q;
	int i;

	for (i = 0; i < 6; i++) {
		if (read_rom(device, generation, i, &q) != RCODE_COMPLETE)
			return REREAD_BIB_ERROR;

		if (i == 0 && q == 0)
			return REREAD_BIB_GONE;

		if (i > device->config_rom_length || q != device->config_rom[i])
			return REREAD_BIB_CHANGED;
	}

	return REREAD_BIB_UNCHANGED;
}

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;

	switch (reread_bus_info_block(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);

			return;
		}
		goto give_up;

	case REREAD_BIB_GONE:
		goto gone;

	case REREAD_BIB_UNCHANGED:
		if (atomic_cmpxchg(&device->state,
			    FW_DEVICE_INITIALIZING,
			    FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN)
			goto gone;

		fw_device_update(work);
		device->config_rom_retries = 0;
		goto out;

	case REREAD_BIB_CHANGED:
		break;
	}

	/*
	 * Something changed.  We keep things simple and don't investigate
	 * further.  We just destroy all previous units and create new ones.
	 */
	device_for_each_child(&device->device, NULL, shutdown_unit);

	if (read_bus_info_block(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;
	}

	create_units(device);

	if (atomic_cmpxchg(&device->state,
		    FW_DEVICE_INITIALIZING,
		    FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN)
		goto gone;

	fw_notify("refreshed device %s\n", device->device.bus_id);
	device->config_rom_retries = 0;
	goto out;

 give_up:
	fw_notify("giving up on refresh of device %s\n", device->device.bus_id);
 gone:
	atomic_set(&device->state, FW_DEVICE_SHUTDOWN);
	fw_device_shutdown(work);
 out:
	if (node_id == card->root_node->node_id)
		schedule_delayed_work(&card->work, 0);
}

void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
{
{
	struct fw_device *device;
	struct fw_device *device;
@@ -780,7 +930,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
	case FW_NODE_LINK_ON:
	case FW_NODE_LINK_ON:
		if (!node->link_on)
		if (!node->link_on)
			break;
			break;

 create:
		device = kzalloc(sizeof(*device), GFP_ATOMIC);
		device = kzalloc(sizeof(*device), GFP_ATOMIC);
		if (device == NULL)
		if (device == NULL)
			break;
			break;
@@ -819,6 +969,23 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
		schedule_delayed_work(&device->work, INITIAL_DELAY);
		schedule_delayed_work(&device->work, INITIAL_DELAY);
		break;
		break;


	case FW_NODE_INITIATED_RESET:
		device = node->data;
		if (device == NULL)
			goto create;

		device->node_id = node->node_id;
		smp_wmb();  /* update node_id before generation */
		device->generation = card->generation;
		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,
				node == card->local_node ? 0 : INITIAL_DELAY);
		}
		break;

	case FW_NODE_UPDATED:
	case FW_NODE_UPDATED:
		if (!node->link_on || node->data == NULL)
		if (!node->link_on || node->data == NULL)
			break;
			break;
Loading