diff options
Diffstat (limited to 'linux-2.6-firewire-git-update.patch')
-rw-r--r-- | linux-2.6-firewire-git-update.patch | 3682 |
1 files changed, 3682 insertions, 0 deletions
diff --git a/linux-2.6-firewire-git-update.patch b/linux-2.6-firewire-git-update.patch new file mode 100644 index 000000000..685808133 --- /dev/null +++ b/linux-2.6-firewire-git-update.patch @@ -0,0 +1,3682 @@ +linux1394-2.6.git tree vs. linus v2.6.29-rc3-git1 on 20090130 by jarod + +--- + firewire-git/drivers/firewire/fw-card.c | 68 - + firewire-git/drivers/firewire/fw-cdev.c | 1014 +++++++++++++++++-------- + firewire-git/drivers/firewire/fw-device.c | 43 - + firewire-git/drivers/firewire/fw-device.h | 7 + firewire-git/drivers/firewire/fw-iso.c | 225 ++++- + firewire-git/drivers/firewire/fw-ohci.c | 236 ++--- + firewire-git/drivers/firewire/fw-sbp2.c | 57 - + firewire-git/drivers/firewire/fw-topology.c | 28 + firewire-git/drivers/firewire/fw-topology.h | 19 + firewire-git/drivers/firewire/fw-transaction.c | 151 +-- + firewire-git/drivers/firewire/fw-transaction.h | 125 --- + include/linux/firewire-cdev.h | 170 +++- + 12 files changed, 1359 insertions(+), 784 deletions(-) + +diff -Naurp linux-2.6-git/drivers/firewire/fw-card.c firewire-git/drivers/firewire/fw-card.c +--- linux-2.6-git/drivers/firewire/fw-card.c 2009-01-30 13:39:02.989651512 -0500 ++++ firewire-git/drivers/firewire/fw-card.c 2009-01-30 13:35:51.859771884 -0500 +@@ -63,8 +63,7 @@ static int descriptor_count; + #define BIB_CMC ((1) << 30) + #define BIB_IMC ((1) << 31) + +-static u32 * +-generate_config_rom(struct fw_card *card, size_t *config_rom_length) ++static u32 *generate_config_rom(struct fw_card *card, size_t *config_rom_length) + { + struct fw_descriptor *desc; + static u32 config_rom[256]; +@@ -128,8 +127,7 @@ generate_config_rom(struct fw_card *card + return config_rom; + } + +-static void +-update_config_roms(void) ++static void update_config_roms(void) + { + struct fw_card *card; + u32 *config_rom; +@@ -141,8 +139,7 @@ update_config_roms(void) + } + } + +-int +-fw_core_add_descriptor(struct fw_descriptor *desc) ++int fw_core_add_descriptor(struct fw_descriptor *desc) + { + size_t i; + +@@ -171,8 +168,7 @@ fw_core_add_descriptor(struct fw_descrip + return 0; + } + +-void +-fw_core_remove_descriptor(struct fw_descriptor *desc) ++void fw_core_remove_descriptor(struct fw_descriptor *desc) + { + mutex_lock(&card_mutex); + +@@ -189,8 +185,7 @@ static const char gap_count_table[] = { + 63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40 + }; + +-void +-fw_schedule_bm_work(struct fw_card *card, unsigned long delay) ++void fw_schedule_bm_work(struct fw_card *card, unsigned long delay) + { + int scheduled; + +@@ -200,8 +195,7 @@ fw_schedule_bm_work(struct fw_card *card + fw_card_put(card); + } + +-static void +-fw_card_bm_work(struct work_struct *work) ++static void fw_card_bm_work(struct work_struct *work) + { + struct fw_card *card = container_of(work, struct fw_card, work.work); + struct fw_device *root_device; +@@ -371,17 +365,16 @@ fw_card_bm_work(struct work_struct *work + fw_card_put(card); + } + +-static void +-flush_timer_callback(unsigned long data) ++static void flush_timer_callback(unsigned long data) + { + struct fw_card *card = (struct fw_card *)data; + + fw_flush_transactions(card); + } + +-void +-fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver, +- struct device *device) ++void fw_card_initialize(struct fw_card *card, ++ const struct fw_card_driver *driver, ++ struct device *device) + { + static atomic_t index = ATOMIC_INIT(-1); + +@@ -406,9 +399,8 @@ fw_card_initialize(struct fw_card *card, + } + EXPORT_SYMBOL(fw_card_initialize); + +-int +-fw_card_add(struct fw_card *card, +- u32 max_receive, u32 link_speed, u64 guid) ++int fw_card_add(struct fw_card *card, ++ u32 max_receive, u32 link_speed, u64 guid) + { + u32 *config_rom; + size_t length; +@@ -435,23 +427,20 @@ EXPORT_SYMBOL(fw_card_add); + * dummy driver just fails all IO. + */ + +-static int +-dummy_enable(struct fw_card *card, u32 *config_rom, size_t length) ++static int dummy_enable(struct fw_card *card, u32 *config_rom, size_t length) + { + BUG(); + return -1; + } + +-static int +-dummy_update_phy_reg(struct fw_card *card, int address, +- int clear_bits, int set_bits) ++static int dummy_update_phy_reg(struct fw_card *card, int address, ++ int clear_bits, int set_bits) + { + return -ENODEV; + } + +-static int +-dummy_set_config_rom(struct fw_card *card, +- u32 *config_rom, size_t length) ++static int dummy_set_config_rom(struct fw_card *card, ++ u32 *config_rom, size_t length) + { + /* + * We take the card out of card_list before setting the dummy +@@ -461,27 +450,23 @@ dummy_set_config_rom(struct fw_card *car + return -1; + } + +-static void +-dummy_send_request(struct fw_card *card, struct fw_packet *packet) ++static void dummy_send_request(struct fw_card *card, struct fw_packet *packet) + { + packet->callback(packet, card, -ENODEV); + } + +-static void +-dummy_send_response(struct fw_card *card, struct fw_packet *packet) ++static void dummy_send_response(struct fw_card *card, struct fw_packet *packet) + { + packet->callback(packet, card, -ENODEV); + } + +-static int +-dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet) ++static int dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet) + { + return -ENOENT; + } + +-static int +-dummy_enable_phys_dma(struct fw_card *card, +- int node_id, int generation) ++static int dummy_enable_phys_dma(struct fw_card *card, ++ int node_id, int generation) + { + return -ENODEV; + } +@@ -496,16 +481,14 @@ static struct fw_card_driver dummy_drive + .enable_phys_dma = dummy_enable_phys_dma, + }; + +-void +-fw_card_release(struct kref *kref) ++void fw_card_release(struct kref *kref) + { + struct fw_card *card = container_of(kref, struct fw_card, kref); + + complete(&card->done); + } + +-void +-fw_core_remove_card(struct fw_card *card) ++void fw_core_remove_card(struct fw_card *card) + { + card->driver->update_phy_reg(card, 4, + PHY_LINK_ACTIVE | PHY_CONTENDER, 0); +@@ -529,8 +512,7 @@ fw_core_remove_card(struct fw_card *card + } + EXPORT_SYMBOL(fw_core_remove_card); + +-int +-fw_core_initiate_bus_reset(struct fw_card *card, int short_reset) ++int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset) + { + int reg = short_reset ? 5 : 1; + int bit = short_reset ? PHY_BUS_SHORT_RESET : PHY_BUS_RESET; +diff -Naurp linux-2.6-git/drivers/firewire/fw-cdev.c firewire-git/drivers/firewire/fw-cdev.c +--- linux-2.6-git/drivers/firewire/fw-cdev.c 2008-11-04 11:19:19.000000000 -0500 ++++ firewire-git/drivers/firewire/fw-cdev.c 2009-01-30 13:35:51.860646788 -0500 +@@ -18,87 +18,162 @@ + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +-#include <linux/module.h> +-#include <linux/kernel.h> +-#include <linux/wait.h> +-#include <linux/errno.h> ++#include <linux/compat.h> ++#include <linux/delay.h> + #include <linux/device.h> +-#include <linux/vmalloc.h> ++#include <linux/errno.h> ++#include <linux/firewire-cdev.h> ++#include <linux/idr.h> ++#include <linux/jiffies.h> ++#include <linux/kernel.h> ++#include <linux/kref.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/mutex.h> + #include <linux/poll.h> + #include <linux/preempt.h> ++#include <linux/spinlock.h> + #include <linux/time.h> +-#include <linux/delay.h> +-#include <linux/mm.h> +-#include <linux/idr.h> +-#include <linux/compat.h> +-#include <linux/firewire-cdev.h> ++#include <linux/vmalloc.h> ++#include <linux/wait.h> ++#include <linux/workqueue.h> ++ + #include <asm/system.h> + #include <asm/uaccess.h> +-#include "fw-transaction.h" +-#include "fw-topology.h" ++ + #include "fw-device.h" ++#include "fw-topology.h" ++#include "fw-transaction.h" ++ ++struct client { ++ u32 version; ++ struct fw_device *device; ++ ++ spinlock_t lock; ++ bool in_shutdown; ++ struct idr resource_idr; ++ struct list_head event_list; ++ wait_queue_head_t wait; ++ u64 bus_reset_closure; ++ ++ struct fw_iso_context *iso_context; ++ u64 iso_closure; ++ struct fw_iso_buffer buffer; ++ unsigned long vm_start; + +-struct client; +-struct client_resource { + struct list_head link; +- void (*release)(struct client *client, struct client_resource *r); +- u32 handle; ++ struct kref kref; ++}; ++ ++static inline void client_get(struct client *client) ++{ ++ kref_get(&client->kref); ++} ++ ++static void client_release(struct kref *kref) ++{ ++ struct client *client = container_of(kref, struct client, kref); ++ ++ fw_device_put(client->device); ++ kfree(client); ++} ++ ++static void client_put(struct client *client) ++{ ++ kref_put(&client->kref, client_release); ++} ++ ++struct client_resource; ++typedef void (*client_resource_release_fn_t)(struct client *, ++ struct client_resource *); ++struct client_resource { ++ client_resource_release_fn_t release; ++ int handle; ++}; ++ ++struct address_handler_resource { ++ struct client_resource resource; ++ struct fw_address_handler handler; ++ __u64 closure; ++ struct client *client; ++}; ++ ++struct outbound_transaction_resource { ++ struct client_resource resource; ++ struct fw_transaction transaction; ++}; ++ ++struct inbound_transaction_resource { ++ struct client_resource resource; ++ struct fw_request *request; ++ void *data; ++ size_t length; + }; + ++struct descriptor_resource { ++ struct client_resource resource; ++ struct fw_descriptor descriptor; ++ u32 data[0]; ++}; ++ ++struct iso_resource { ++ struct client_resource resource; ++ struct client *client; ++ /* Schedule work and access todo only with client->lock held. */ ++ struct delayed_work work; ++ enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC, ++ ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo; ++ int generation; ++ u64 channels; ++ s32 bandwidth; ++ struct iso_resource_event *e_alloc, *e_dealloc; ++}; ++ ++static void schedule_iso_resource(struct iso_resource *); ++static void release_iso_resource(struct client *, struct client_resource *); ++ + /* + * dequeue_event() just kfree()'s the event, so the event has to be +- * the first field in the struct. ++ * the first field in a struct XYZ_event. + */ +- + struct event { + struct { void *data; size_t size; } v[2]; + struct list_head link; + }; + +-struct bus_reset { ++struct bus_reset_event { + struct event event; + struct fw_cdev_event_bus_reset reset; + }; + +-struct response { ++struct outbound_transaction_event { + struct event event; +- struct fw_transaction transaction; + struct client *client; +- struct client_resource resource; ++ struct outbound_transaction_resource r; + struct fw_cdev_event_response response; + }; + +-struct iso_interrupt { ++struct inbound_transaction_event { + struct event event; +- struct fw_cdev_event_iso_interrupt interrupt; ++ struct fw_cdev_event_request request; + }; + +-struct client { +- u32 version; +- struct fw_device *device; +- spinlock_t lock; +- u32 resource_handle; +- struct list_head resource_list; +- struct list_head event_list; +- wait_queue_head_t wait; +- u64 bus_reset_closure; +- +- struct fw_iso_context *iso_context; +- u64 iso_closure; +- struct fw_iso_buffer buffer; +- unsigned long vm_start; ++struct iso_interrupt_event { ++ struct event event; ++ struct fw_cdev_event_iso_interrupt interrupt; ++}; + +- struct list_head link; ++struct iso_resource_event { ++ struct event event; ++ struct fw_cdev_event_iso_resource resource; + }; + +-static inline void __user * +-u64_to_uptr(__u64 value) ++static inline void __user *u64_to_uptr(__u64 value) + { + return (void __user *)(unsigned long)value; + } + +-static inline __u64 +-uptr_to_u64(void __user *ptr) ++static inline __u64 uptr_to_u64(void __user *ptr) + { + return (__u64)(unsigned long)ptr; + } +@@ -107,7 +182,6 @@ static int fw_device_op_open(struct inod + { + struct fw_device *device; + struct client *client; +- unsigned long flags; + + device = fw_device_get_by_devt(inode->i_rdev); + if (device == NULL) +@@ -125,16 +199,17 @@ static int fw_device_op_open(struct inod + } + + client->device = device; +- INIT_LIST_HEAD(&client->event_list); +- INIT_LIST_HEAD(&client->resource_list); + spin_lock_init(&client->lock); ++ idr_init(&client->resource_idr); ++ INIT_LIST_HEAD(&client->event_list); + init_waitqueue_head(&client->wait); ++ kref_init(&client->kref); + + file->private_data = client; + +- spin_lock_irqsave(&device->card->lock, flags); ++ mutex_lock(&device->client_list_mutex); + list_add_tail(&client->link, &device->client_list); +- spin_unlock_irqrestore(&device->card->lock, flags); ++ mutex_unlock(&device->client_list_mutex); + + return 0; + } +@@ -150,68 +225,69 @@ static void queue_event(struct client *c + event->v[1].size = size1; + + spin_lock_irqsave(&client->lock, flags); +- list_add_tail(&event->link, &client->event_list); ++ if (client->in_shutdown) ++ kfree(event); ++ else ++ list_add_tail(&event->link, &client->event_list); + spin_unlock_irqrestore(&client->lock, flags); + + wake_up_interruptible(&client->wait); + } + +-static int +-dequeue_event(struct client *client, char __user *buffer, size_t count) ++static int dequeue_event(struct client *client, ++ char __user *buffer, size_t count) + { +- unsigned long flags; + struct event *event; + size_t size, total; +- int i, retval; ++ int i, ret; + +- retval = wait_event_interruptible(client->wait, +- !list_empty(&client->event_list) || +- fw_device_is_shutdown(client->device)); +- if (retval < 0) +- return retval; ++ ret = wait_event_interruptible(client->wait, ++ !list_empty(&client->event_list) || ++ fw_device_is_shutdown(client->device)); ++ if (ret < 0) ++ return ret; + + if (list_empty(&client->event_list) && + fw_device_is_shutdown(client->device)) + return -ENODEV; + +- spin_lock_irqsave(&client->lock, flags); +- event = container_of(client->event_list.next, struct event, link); ++ spin_lock_irq(&client->lock); ++ event = list_first_entry(&client->event_list, struct event, link); + list_del(&event->link); +- spin_unlock_irqrestore(&client->lock, flags); ++ spin_unlock_irq(&client->lock); + + total = 0; + for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) { + size = min(event->v[i].size, count - total); + if (copy_to_user(buffer + total, event->v[i].data, size)) { +- retval = -EFAULT; ++ ret = -EFAULT; + goto out; + } + total += size; + } +- retval = total; ++ ret = total; + + out: + kfree(event); + +- return retval; ++ return ret; + } + +-static ssize_t +-fw_device_op_read(struct file *file, +- char __user *buffer, size_t count, loff_t *offset) ++static ssize_t fw_device_op_read(struct file *file, char __user *buffer, ++ size_t count, loff_t *offset) + { + struct client *client = file->private_data; + + return dequeue_event(client, buffer, count); + } + +-/* caller must hold card->lock so that node pointers can be dereferenced here */ +-static void +-fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, +- struct client *client) ++static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, ++ struct client *client) + { + struct fw_card *card = client->device->card; + ++ spin_lock_irq(&card->lock); ++ + event->closure = client->bus_reset_closure; + event->type = FW_CDEV_EVENT_BUS_RESET; + event->generation = client->device->generation; +@@ -220,39 +296,49 @@ fill_bus_reset_event(struct fw_cdev_even + event->bm_node_id = 0; /* FIXME: We don't track the BM. */ + event->irm_node_id = card->irm_node->node_id; + event->root_node_id = card->root_node->node_id; ++ ++ spin_unlock_irq(&card->lock); + } + +-static void +-for_each_client(struct fw_device *device, +- void (*callback)(struct client *client)) ++static void for_each_client(struct fw_device *device, ++ void (*callback)(struct client *client)) + { +- struct fw_card *card = device->card; + struct client *c; +- unsigned long flags; +- +- spin_lock_irqsave(&card->lock, flags); + ++ mutex_lock(&device->client_list_mutex); + list_for_each_entry(c, &device->client_list, link) + callback(c); ++ mutex_unlock(&device->client_list_mutex); ++} ++ ++static int schedule_reallocations(int id, void *p, void *data) ++{ ++ struct client_resource *r = p; + +- spin_unlock_irqrestore(&card->lock, flags); ++ if (r->release == release_iso_resource) ++ schedule_iso_resource(container_of(r, ++ struct iso_resource, resource)); ++ return 0; + } + +-static void +-queue_bus_reset_event(struct client *client) ++static void queue_bus_reset_event(struct client *client) + { +- struct bus_reset *bus_reset; ++ struct bus_reset_event *e; + +- bus_reset = kzalloc(sizeof(*bus_reset), GFP_ATOMIC); +- if (bus_reset == NULL) { ++ e = kzalloc(sizeof(*e), GFP_KERNEL); ++ if (e == NULL) { + fw_notify("Out of memory when allocating bus reset event\n"); + return; + } + +- fill_bus_reset_event(&bus_reset->reset, client); ++ fill_bus_reset_event(&e->reset, client); ++ ++ queue_event(client, &e->event, ++ &e->reset, sizeof(e->reset), NULL, 0); + +- queue_event(client, &bus_reset->event, +- &bus_reset->reset, sizeof(bus_reset->reset), NULL, 0); ++ spin_lock_irq(&client->lock); ++ idr_for_each(&client->resource_idr, schedule_reallocations, client); ++ spin_unlock_irq(&client->lock); + } + + void fw_device_cdev_update(struct fw_device *device) +@@ -274,11 +360,11 @@ static int ioctl_get_info(struct client + { + struct fw_cdev_get_info *get_info = buffer; + struct fw_cdev_event_bus_reset bus_reset; +- struct fw_card *card = client->device->card; + unsigned long ret = 0; + + client->version = get_info->version; + get_info->version = FW_CDEV_VERSION; ++ get_info->card = client->device->card->index; + + down_read(&fw_device_rwsem); + +@@ -300,49 +386,61 @@ static int ioctl_get_info(struct client + client->bus_reset_closure = get_info->bus_reset_closure; + if (get_info->bus_reset != 0) { + void __user *uptr = u64_to_uptr(get_info->bus_reset); +- unsigned long flags; + +- spin_lock_irqsave(&card->lock, flags); + fill_bus_reset_event(&bus_reset, client); +- spin_unlock_irqrestore(&card->lock, flags); +- + if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset))) + return -EFAULT; + } + +- get_info->card = card->index; +- + return 0; + } + +-static void +-add_client_resource(struct client *client, struct client_resource *resource) ++static int add_client_resource(struct client *client, ++ struct client_resource *resource, gfp_t gfp_mask) + { + unsigned long flags; ++ int ret; ++ ++ retry: ++ if (idr_pre_get(&client->resource_idr, gfp_mask | __GFP_ZERO) == 0) ++ return -ENOMEM; + + spin_lock_irqsave(&client->lock, flags); +- list_add_tail(&resource->link, &client->resource_list); +- resource->handle = client->resource_handle++; ++ if (client->in_shutdown) ++ ret = -ECANCELED; ++ else ++ ret = idr_get_new(&client->resource_idr, resource, ++ &resource->handle); ++ if (ret >= 0) { ++ client_get(client); ++ if (resource->release == release_iso_resource) ++ schedule_iso_resource(container_of(resource, ++ struct iso_resource, resource)); ++ } + spin_unlock_irqrestore(&client->lock, flags); ++ ++ if (ret == -EAGAIN) ++ goto retry; ++ ++ return ret < 0 ? ret : 0; + } + +-static int +-release_client_resource(struct client *client, u32 handle, +- struct client_resource **resource) ++static int release_client_resource(struct client *client, u32 handle, ++ client_resource_release_fn_t release, ++ struct client_resource **resource) + { + struct client_resource *r; +- unsigned long flags; + +- spin_lock_irqsave(&client->lock, flags); +- list_for_each_entry(r, &client->resource_list, link) { +- if (r->handle == handle) { +- list_del(&r->link); +- break; +- } +- } +- spin_unlock_irqrestore(&client->lock, flags); ++ spin_lock_irq(&client->lock); ++ if (client->in_shutdown) ++ r = NULL; ++ else ++ r = idr_find(&client->resource_idr, handle); ++ if (r && r->release == release) ++ idr_remove(&client->resource_idr, handle); ++ spin_unlock_irq(&client->lock); + +- if (&r->link == &client->resource_list) ++ if (!(r && r->release == release)) + return -EINVAL; + + if (resource) +@@ -350,203 +448,242 @@ release_client_resource(struct client *c + else + r->release(client, r); + ++ client_put(client); ++ + return 0; + } + +-static void +-release_transaction(struct client *client, struct client_resource *resource) ++static void release_transaction(struct client *client, ++ struct client_resource *resource) + { +- struct response *response = +- container_of(resource, struct response, resource); ++ struct outbound_transaction_resource *r = container_of(resource, ++ struct outbound_transaction_resource, resource); + +- fw_cancel_transaction(client->device->card, &response->transaction); ++ fw_cancel_transaction(client->device->card, &r->transaction); + } + +-static void +-complete_transaction(struct fw_card *card, int rcode, +- void *payload, size_t length, void *data) ++static void complete_transaction(struct fw_card *card, int rcode, ++ void *payload, size_t length, void *data) + { +- struct response *response = data; +- struct client *client = response->client; ++ struct outbound_transaction_event *e = data; ++ struct fw_cdev_event_response *rsp = &e->response; ++ struct client *client = e->client; + unsigned long flags; +- struct fw_cdev_event_response *r = &response->response; + +- if (length < r->length) +- r->length = length; ++ if (length < rsp->length) ++ rsp->length = length; + if (rcode == RCODE_COMPLETE) +- memcpy(r->data, payload, r->length); ++ memcpy(rsp->data, payload, rsp->length); + + spin_lock_irqsave(&client->lock, flags); +- list_del(&response->resource.link); ++ /* ++ * 1. If called while in shutdown, the idr tree must be left untouched. ++ * The idr handle will be removed and the client reference will be ++ * dropped later. ++ * 2. If the call chain was release_client_resource -> ++ * release_transaction -> complete_transaction (instead of a normal ++ * conclusion of the transaction), i.e. if this resource was already ++ * unregistered from the idr, the client reference will be dropped ++ * by release_client_resource and we must not drop it here. ++ */ ++ if (!client->in_shutdown && ++ idr_find(&client->resource_idr, e->r.resource.handle)) { ++ idr_remove(&client->resource_idr, e->r.resource.handle); ++ /* Drop the idr's reference */ ++ client_put(client); ++ } + spin_unlock_irqrestore(&client->lock, flags); + +- r->type = FW_CDEV_EVENT_RESPONSE; +- r->rcode = rcode; ++ rsp->type = FW_CDEV_EVENT_RESPONSE; ++ rsp->rcode = rcode; + + /* +- * In the case that sizeof(*r) doesn't align with the position of the ++ * In the case that sizeof(*rsp) doesn't align with the position of the + * data, and the read is short, preserve an extra copy of the data + * to stay compatible with a pre-2.6.27 bug. Since the bug is harmless + * for short reads and some apps depended on it, this is both safe + * and prudent for compatibility. + */ +- if (r->length <= sizeof(*r) - offsetof(typeof(*r), data)) +- queue_event(client, &response->event, r, sizeof(*r), +- r->data, r->length); ++ if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data)) ++ queue_event(client, &e->event, rsp, sizeof(*rsp), ++ rsp->data, rsp->length); + else +- queue_event(client, &response->event, r, sizeof(*r) + r->length, ++ queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, + NULL, 0); ++ ++ /* Drop the transaction callback's reference */ ++ client_put(client); + } + +-static int ioctl_send_request(struct client *client, void *buffer) ++static int init_request(struct client *client, ++ struct fw_cdev_send_request *request, ++ int destination_id, int speed) + { +- struct fw_device *device = client->device; +- struct fw_cdev_send_request *request = buffer; +- struct response *response; ++ struct outbound_transaction_event *e; ++ int ret; + +- /* What is the biggest size we'll accept, really? */ +- if (request->length > 4096) +- return -EINVAL; ++ if (request->length > 4096 || request->length > 512 << speed) ++ return -EIO; + +- response = kmalloc(sizeof(*response) + request->length, GFP_KERNEL); +- if (response == NULL) ++ e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL); ++ if (e == NULL) + return -ENOMEM; + +- response->client = client; +- response->response.length = request->length; +- response->response.closure = request->closure; ++ e->client = client; ++ e->response.length = request->length; ++ e->response.closure = request->closure; + + if (request->data && +- copy_from_user(response->response.data, ++ copy_from_user(e->response.data, + u64_to_uptr(request->data), request->length)) { +- kfree(response); +- return -EFAULT; ++ ret = -EFAULT; ++ goto failed; + } + +- response->resource.release = release_transaction; +- add_client_resource(client, &response->resource); +- +- fw_send_request(device->card, &response->transaction, +- request->tcode & 0x1f, +- device->node->node_id, +- request->generation, +- device->max_speed, +- request->offset, +- response->response.data, request->length, +- complete_transaction, response); ++ e->r.resource.release = release_transaction; ++ ret = add_client_resource(client, &e->r.resource, GFP_KERNEL); ++ if (ret < 0) ++ goto failed; ++ ++ /* Get a reference for the transaction callback */ ++ client_get(client); ++ ++ fw_send_request(client->device->card, &e->r.transaction, ++ request->tcode & 0x1f, destination_id, ++ request->generation, speed, request->offset, ++ e->response.data, request->length, ++ complete_transaction, e); + + if (request->data) + return sizeof(request) + request->length; + else + return sizeof(request); ++ failed: ++ kfree(e); ++ ++ return ret; + } + +-struct address_handler { +- struct fw_address_handler handler; +- __u64 closure; +- struct client *client; +- struct client_resource resource; +-}; ++static int ioctl_send_request(struct client *client, void *buffer) ++{ ++ struct fw_cdev_send_request *request = buffer; + +-struct request { +- struct fw_request *request; +- void *data; +- size_t length; +- struct client_resource resource; +-}; ++ switch (request->tcode) { ++ case TCODE_WRITE_QUADLET_REQUEST: ++ case TCODE_WRITE_BLOCK_REQUEST: ++ case TCODE_READ_QUADLET_REQUEST: ++ case TCODE_READ_BLOCK_REQUEST: ++ case TCODE_LOCK_MASK_SWAP: ++ case TCODE_LOCK_COMPARE_SWAP: ++ case TCODE_LOCK_FETCH_ADD: ++ case TCODE_LOCK_LITTLE_ADD: ++ case TCODE_LOCK_BOUNDED_ADD: ++ case TCODE_LOCK_WRAP_ADD: ++ case TCODE_LOCK_VENDOR_DEPENDENT: ++ break; ++ default: ++ return -EINVAL; ++ } + +-struct request_event { +- struct event event; +- struct fw_cdev_event_request request; +-}; ++ return init_request(client, request, client->device->node->node_id, ++ client->device->max_speed); ++} + +-static void +-release_request(struct client *client, struct client_resource *resource) ++static void release_request(struct client *client, ++ struct client_resource *resource) + { +- struct request *request = +- container_of(resource, struct request, resource); ++ struct inbound_transaction_resource *r = container_of(resource, ++ struct inbound_transaction_resource, resource); + +- fw_send_response(client->device->card, request->request, ++ fw_send_response(client->device->card, r->request, + RCODE_CONFLICT_ERROR); +- kfree(request); ++ kfree(r); + } + +-static void +-handle_request(struct fw_card *card, struct fw_request *r, +- int tcode, int destination, int source, +- int generation, int speed, +- unsigned long long offset, +- void *payload, size_t length, void *callback_data) +-{ +- struct address_handler *handler = callback_data; +- struct request *request; +- struct request_event *e; +- struct client *client = handler->client; ++static void handle_request(struct fw_card *card, struct fw_request *request, ++ int tcode, int destination, int source, ++ int generation, int speed, ++ unsigned long long offset, ++ void *payload, size_t length, void *callback_data) ++{ ++ struct address_handler_resource *handler = callback_data; ++ struct inbound_transaction_resource *r; ++ struct inbound_transaction_event *e; ++ int ret; + +- request = kmalloc(sizeof(*request), GFP_ATOMIC); ++ r = kmalloc(sizeof(*r), GFP_ATOMIC); + e = kmalloc(sizeof(*e), GFP_ATOMIC); +- if (request == NULL || e == NULL) { +- kfree(request); +- kfree(e); +- fw_send_response(card, r, RCODE_CONFLICT_ERROR); +- return; +- } +- +- request->request = r; +- request->data = payload; +- request->length = length; ++ if (r == NULL || e == NULL) ++ goto failed; + +- request->resource.release = release_request; +- add_client_resource(client, &request->resource); ++ r->request = request; ++ r->data = payload; ++ r->length = length; ++ ++ r->resource.release = release_request; ++ ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC); ++ if (ret < 0) ++ goto failed; + + e->request.type = FW_CDEV_EVENT_REQUEST; + e->request.tcode = tcode; + e->request.offset = offset; + e->request.length = length; +- e->request.handle = request->resource.handle; ++ e->request.handle = r->resource.handle; + e->request.closure = handler->closure; + +- queue_event(client, &e->event, ++ queue_event(handler->client, &e->event, + &e->request, sizeof(e->request), payload, length); ++ return; ++ ++ failed: ++ kfree(r); ++ kfree(e); ++ fw_send_response(card, request, RCODE_CONFLICT_ERROR); + } + +-static void +-release_address_handler(struct client *client, +- struct client_resource *resource) ++static void release_address_handler(struct client *client, ++ struct client_resource *resource) + { +- struct address_handler *handler = +- container_of(resource, struct address_handler, resource); ++ struct address_handler_resource *r = ++ container_of(resource, struct address_handler_resource, resource); + +- fw_core_remove_address_handler(&handler->handler); +- kfree(handler); ++ fw_core_remove_address_handler(&r->handler); ++ kfree(r); + } + + static int ioctl_allocate(struct client *client, void *buffer) + { + struct fw_cdev_allocate *request = buffer; +- struct address_handler *handler; ++ struct address_handler_resource *r; + struct fw_address_region region; ++ int ret; + +- handler = kmalloc(sizeof(*handler), GFP_KERNEL); +- if (handler == NULL) ++ r = kmalloc(sizeof(*r), GFP_KERNEL); ++ if (r == NULL) + return -ENOMEM; + + region.start = request->offset; + region.end = request->offset + request->length; +- handler->handler.length = request->length; +- handler->handler.address_callback = handle_request; +- handler->handler.callback_data = handler; +- handler->closure = request->closure; +- handler->client = client; +- +- if (fw_core_add_address_handler(&handler->handler, ®ion) < 0) { +- kfree(handler); +- return -EBUSY; ++ r->handler.length = request->length; ++ r->handler.address_callback = handle_request; ++ r->handler.callback_data = r; ++ r->closure = request->closure; ++ r->client = client; ++ ++ ret = fw_core_add_address_handler(&r->handler, ®ion); ++ if (ret < 0) { ++ kfree(r); ++ return ret; + } + +- handler->resource.release = release_address_handler; +- add_client_resource(client, &handler->resource); +- request->handle = handler->resource.handle; ++ r->resource.release = release_address_handler; ++ ret = add_client_resource(client, &r->resource, GFP_KERNEL); ++ if (ret < 0) { ++ release_address_handler(client, &r->resource); ++ return ret; ++ } ++ request->handle = r->resource.handle; + + return 0; + } +@@ -555,18 +692,22 @@ static int ioctl_deallocate(struct clien + { + struct fw_cdev_deallocate *request = buffer; + +- return release_client_resource(client, request->handle, NULL); ++ return release_client_resource(client, request->handle, ++ release_address_handler, NULL); + } + + static int ioctl_send_response(struct client *client, void *buffer) + { + struct fw_cdev_send_response *request = buffer; + struct client_resource *resource; +- struct request *r; ++ struct inbound_transaction_resource *r; + +- if (release_client_resource(client, request->handle, &resource) < 0) ++ if (release_client_resource(client, request->handle, ++ release_request, &resource) < 0) + return -EINVAL; +- r = container_of(resource, struct request, resource); ++ ++ r = container_of(resource, struct inbound_transaction_resource, ++ resource); + if (request->length < r->length) + r->length = request->length; + if (copy_from_user(r->data, u64_to_uptr(request->data), r->length)) +@@ -588,85 +729,84 @@ static int ioctl_initiate_bus_reset(stru + return fw_core_initiate_bus_reset(client->device->card, short_reset); + } + +-struct descriptor { +- struct fw_descriptor d; +- struct client_resource resource; +- u32 data[0]; +-}; +- + static void release_descriptor(struct client *client, + struct client_resource *resource) + { +- struct descriptor *descriptor = +- container_of(resource, struct descriptor, resource); ++ struct descriptor_resource *r = ++ container_of(resource, struct descriptor_resource, resource); + +- fw_core_remove_descriptor(&descriptor->d); +- kfree(descriptor); ++ fw_core_remove_descriptor(&r->descriptor); ++ kfree(r); + } + + static int ioctl_add_descriptor(struct client *client, void *buffer) + { + struct fw_cdev_add_descriptor *request = buffer; +- struct descriptor *descriptor; +- int retval; ++ struct descriptor_resource *r; ++ int ret; + + if (request->length > 256) + return -EINVAL; + +- descriptor = +- kmalloc(sizeof(*descriptor) + request->length * 4, GFP_KERNEL); +- if (descriptor == NULL) ++ r = kmalloc(sizeof(*r) + request->length * 4, GFP_KERNEL); ++ if (r == NULL) + return -ENOMEM; + +- if (copy_from_user(descriptor->data, ++ if (copy_from_user(r->data, + u64_to_uptr(request->data), request->length * 4)) { +- kfree(descriptor); +- return -EFAULT; ++ ret = -EFAULT; ++ goto failed; + } + +- descriptor->d.length = request->length; +- descriptor->d.immediate = request->immediate; +- descriptor->d.key = request->key; +- descriptor->d.data = descriptor->data; +- +- retval = fw_core_add_descriptor(&descriptor->d); +- if (retval < 0) { +- kfree(descriptor); +- return retval; ++ r->descriptor.length = request->length; ++ r->descriptor.immediate = request->immediate; ++ r->descriptor.key = request->key; ++ r->descriptor.data = r->data; ++ ++ ret = fw_core_add_descriptor(&r->descriptor); ++ if (ret < 0) ++ goto failed; ++ ++ r->resource.release = release_descriptor; ++ ret = add_client_resource(client, &r->resource, GFP_KERNEL); ++ if (ret < 0) { ++ fw_core_remove_descriptor(&r->descriptor); ++ goto failed; + } +- +- descriptor->resource.release = release_descriptor; +- add_client_resource(client, &descriptor->resource); +- request->handle = descriptor->resource.handle; ++ request->handle = r->resource.handle; + + return 0; ++ failed: ++ kfree(r); ++ ++ return ret; + } + + static int ioctl_remove_descriptor(struct client *client, void *buffer) + { + struct fw_cdev_remove_descriptor *request = buffer; + +- return release_client_resource(client, request->handle, NULL); ++ return release_client_resource(client, request->handle, ++ release_descriptor, NULL); + } + +-static void +-iso_callback(struct fw_iso_context *context, u32 cycle, +- size_t header_length, void *header, void *data) ++static void iso_callback(struct fw_iso_context *context, u32 cycle, ++ size_t header_length, void *header, void *data) + { + struct client *client = data; +- struct iso_interrupt *irq; ++ struct iso_interrupt_event *e; + +- irq = kzalloc(sizeof(*irq) + header_length, GFP_ATOMIC); +- if (irq == NULL) ++ e = kzalloc(sizeof(*e) + header_length, GFP_ATOMIC); ++ if (e == NULL) + return; + +- irq->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; +- irq->interrupt.closure = client->iso_closure; +- irq->interrupt.cycle = cycle; +- irq->interrupt.header_length = header_length; +- memcpy(irq->interrupt.header, header, header_length); +- queue_event(client, &irq->event, &irq->interrupt, +- sizeof(irq->interrupt) + header_length, NULL, 0); ++ e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; ++ e->interrupt.closure = client->iso_closure; ++ e->interrupt.cycle = cycle; ++ e->interrupt.header_length = header_length; ++ memcpy(e->interrupt.header, header, header_length); ++ queue_event(client, &e->event, &e->interrupt, ++ sizeof(e->interrupt) + header_length, NULL, 0); + } + + static int ioctl_create_iso_context(struct client *client, void *buffer) +@@ -871,6 +1011,237 @@ static int ioctl_get_cycle_timer(struct + return 0; + } + ++static void iso_resource_work(struct work_struct *work) ++{ ++ struct iso_resource_event *e; ++ struct iso_resource *r = ++ container_of(work, struct iso_resource, work.work); ++ struct client *client = r->client; ++ int generation, channel, bandwidth, todo; ++ bool skip, free, success; ++ ++ spin_lock_irq(&client->lock); ++ generation = client->device->generation; ++ todo = r->todo; ++ /* Allow 1000ms grace period for other reallocations. */ ++ if (todo == ISO_RES_ALLOC && ++ time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) { ++ if (schedule_delayed_work(&r->work, DIV_ROUND_UP(HZ, 3))) ++ client_get(client); ++ skip = true; ++ } else { ++ /* We could be called twice within the same generation. */ ++ skip = todo == ISO_RES_REALLOC && ++ r->generation == generation; ++ } ++ free = todo == ISO_RES_DEALLOC || ++ todo == ISO_RES_ALLOC_ONCE || ++ todo == ISO_RES_DEALLOC_ONCE; ++ r->generation = generation; ++ spin_unlock_irq(&client->lock); ++ ++ if (skip) ++ goto out; ++ ++ bandwidth = r->bandwidth; ++ ++ fw_iso_resource_manage(client->device->card, generation, ++ r->channels, &channel, &bandwidth, ++ todo == ISO_RES_ALLOC || ++ todo == ISO_RES_REALLOC || ++ todo == ISO_RES_ALLOC_ONCE); ++ /* ++ * Is this generation outdated already? As long as this resource sticks ++ * in the idr, it will be scheduled again for a newer generation or at ++ * shutdown. ++ */ ++ if (channel == -EAGAIN && ++ (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC)) ++ goto out; ++ ++ success = channel >= 0 || bandwidth > 0; ++ ++ spin_lock_irq(&client->lock); ++ /* ++ * Transit from allocation to reallocation, except if the client ++ * requested deallocation in the meantime. ++ */ ++ if (r->todo == ISO_RES_ALLOC) ++ r->todo = ISO_RES_REALLOC; ++ /* ++ * Allocation or reallocation failure? Pull this resource out of the ++ * idr and prepare for deletion, unless the client is shutting down. ++ */ ++ if (r->todo == ISO_RES_REALLOC && !success && ++ !client->in_shutdown && ++ idr_find(&client->resource_idr, r->resource.handle)) { ++ idr_remove(&client->resource_idr, r->resource.handle); ++ client_put(client); ++ free = true; ++ } ++ spin_unlock_irq(&client->lock); ++ ++ if (todo == ISO_RES_ALLOC && channel >= 0) ++ r->channels = 1ULL << channel; ++ ++ if (todo == ISO_RES_REALLOC && success) ++ goto out; ++ ++ if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) { ++ e = r->e_alloc; ++ r->e_alloc = NULL; ++ } else { ++ e = r->e_dealloc; ++ r->e_dealloc = NULL; ++ } ++ e->resource.handle = r->resource.handle; ++ e->resource.channel = channel; ++ e->resource.bandwidth = bandwidth; ++ ++ queue_event(client, &e->event, ++ &e->resource, sizeof(e->resource), NULL, 0); ++ ++ if (free) { ++ cancel_delayed_work(&r->work); ++ kfree(r->e_alloc); ++ kfree(r->e_dealloc); ++ kfree(r); ++ } ++ out: ++ client_put(client); ++} ++ ++static void schedule_iso_resource(struct iso_resource *r) ++{ ++ client_get(r->client); ++ if (!schedule_delayed_work(&r->work, 0)) ++ client_put(r->client); ++} ++ ++static void release_iso_resource(struct client *client, ++ struct client_resource *resource) ++{ ++ struct iso_resource *r = ++ container_of(resource, struct iso_resource, resource); ++ ++ spin_lock_irq(&client->lock); ++ r->todo = ISO_RES_DEALLOC; ++ schedule_iso_resource(r); ++ spin_unlock_irq(&client->lock); ++} ++ ++static int init_iso_resource(struct client *client, ++ struct fw_cdev_allocate_iso_resource *request, int todo) ++{ ++ struct iso_resource_event *e1, *e2; ++ struct iso_resource *r; ++ int ret; ++ ++ if ((request->channels == 0 && request->bandwidth == 0) || ++ request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL || ++ request->bandwidth < 0) ++ return -EINVAL; ++ ++ r = kmalloc(sizeof(*r), GFP_KERNEL); ++ e1 = kmalloc(sizeof(*e1), GFP_KERNEL); ++ e2 = kmalloc(sizeof(*e2), GFP_KERNEL); ++ if (r == NULL || e1 == NULL || e2 == NULL) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ INIT_DELAYED_WORK(&r->work, iso_resource_work); ++ r->client = client; ++ r->todo = todo; ++ r->generation = -1; ++ r->channels = request->channels; ++ r->bandwidth = request->bandwidth; ++ r->e_alloc = e1; ++ r->e_dealloc = e2; ++ ++ e1->resource.closure = request->closure; ++ e1->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; ++ e2->resource.closure = request->closure; ++ e2->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; ++ ++ if (todo == ISO_RES_ALLOC) { ++ r->resource.release = release_iso_resource; ++ ret = add_client_resource(client, &r->resource, GFP_KERNEL); ++ if (ret < 0) ++ goto fail; ++ } else { ++ r->resource.release = NULL; ++ r->resource.handle = -1; ++ schedule_iso_resource(r); ++ } ++ request->handle = r->resource.handle; ++ ++ return 0; ++ fail: ++ kfree(r); ++ kfree(e1); ++ kfree(e2); ++ ++ return ret; ++} ++ ++static int ioctl_allocate_iso_resource(struct client *client, void *buffer) ++{ ++ struct fw_cdev_allocate_iso_resource *request = buffer; ++ ++ return init_iso_resource(client, request, ISO_RES_ALLOC); ++} ++ ++static int ioctl_deallocate_iso_resource(struct client *client, void *buffer) ++{ ++ struct fw_cdev_deallocate *request = buffer; ++ ++ return release_client_resource(client, request->handle, ++ release_iso_resource, NULL); ++} ++ ++static int ioctl_allocate_iso_resource_once(struct client *client, void *buffer) ++{ ++ struct fw_cdev_allocate_iso_resource *request = buffer; ++ ++ return init_iso_resource(client, request, ISO_RES_ALLOC_ONCE); ++} ++ ++static int ioctl_deallocate_iso_resource_once(struct client *client, void *buffer) ++{ ++ struct fw_cdev_allocate_iso_resource *request = buffer; ++ ++ return init_iso_resource(client, request, ISO_RES_DEALLOC_ONCE); ++} ++ ++static int ioctl_get_speed(struct client *client, void *buffer) ++{ ++ struct fw_cdev_get_speed *request = buffer; ++ ++ request->max_speed = client->device->max_speed; ++ ++ return 0; ++} ++ ++static int ioctl_send_broadcast_request(struct client *client, void *buffer) ++{ ++ struct fw_cdev_send_request *request = buffer; ++ ++ switch (request->tcode) { ++ case TCODE_WRITE_QUADLET_REQUEST: ++ case TCODE_WRITE_BLOCK_REQUEST: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* Security policy: Only allow accesses to Units Space. */ ++ if (request->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END) ++ return -EACCES; ++ ++ return init_request(client, request, LOCAL_BUS | 0x3f, SCODE_100); ++} ++ + static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { + ioctl_get_info, + ioctl_send_request, +@@ -885,13 +1256,19 @@ static int (* const ioctl_handlers[])(st + ioctl_start_iso, + ioctl_stop_iso, + ioctl_get_cycle_timer, ++ ioctl_allocate_iso_resource, ++ ioctl_deallocate_iso_resource, ++ ioctl_allocate_iso_resource_once, ++ ioctl_deallocate_iso_resource_once, ++ ioctl_get_speed, ++ ioctl_send_broadcast_request, + }; + +-static int +-dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg) ++static int dispatch_ioctl(struct client *client, ++ unsigned int cmd, void __user *arg) + { + char buffer[256]; +- int retval; ++ int ret; + + if (_IOC_TYPE(cmd) != '#' || + _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers)) +@@ -903,9 +1280,9 @@ dispatch_ioctl(struct client *client, un + return -EFAULT; + } + +- retval = ioctl_handlers[_IOC_NR(cmd)](client, buffer); +- if (retval < 0) +- return retval; ++ ret = ioctl_handlers[_IOC_NR(cmd)](client, buffer); ++ if (ret < 0) ++ return ret; + + if (_IOC_DIR(cmd) & _IOC_READ) { + if (_IOC_SIZE(cmd) > sizeof(buffer) || +@@ -913,12 +1290,11 @@ dispatch_ioctl(struct client *client, un + return -EFAULT; + } + +- return retval; ++ return ret; + } + +-static long +-fw_device_op_ioctl(struct file *file, +- unsigned int cmd, unsigned long arg) ++static long fw_device_op_ioctl(struct file *file, ++ unsigned int cmd, unsigned long arg) + { + struct client *client = file->private_data; + +@@ -929,9 +1305,8 @@ fw_device_op_ioctl(struct file *file, + } + + #ifdef CONFIG_COMPAT +-static long +-fw_device_op_compat_ioctl(struct file *file, +- unsigned int cmd, unsigned long arg) ++static long fw_device_op_compat_ioctl(struct file *file, ++ unsigned int cmd, unsigned long arg) + { + struct client *client = file->private_data; + +@@ -947,7 +1322,7 @@ static int fw_device_op_mmap(struct file + struct client *client = file->private_data; + enum dma_data_direction direction; + unsigned long size; +- int page_count, retval; ++ int page_count, ret; + + if (fw_device_is_shutdown(client->device)) + return -ENODEV; +@@ -973,48 +1348,57 @@ static int fw_device_op_mmap(struct file + else + direction = DMA_FROM_DEVICE; + +- retval = fw_iso_buffer_init(&client->buffer, client->device->card, +- page_count, direction); +- if (retval < 0) +- return retval; ++ ret = fw_iso_buffer_init(&client->buffer, client->device->card, ++ page_count, direction); ++ if (ret < 0) ++ return ret; + +- retval = fw_iso_buffer_map(&client->buffer, vma); +- if (retval < 0) ++ ret = fw_iso_buffer_map(&client->buffer, vma); ++ if (ret < 0) + fw_iso_buffer_destroy(&client->buffer, client->device->card); + +- return retval; ++ return ret; ++} ++ ++static int shutdown_resource(int id, void *p, void *data) ++{ ++ struct client_resource *r = p; ++ struct client *client = data; ++ ++ r->release(client, r); ++ client_put(client); ++ ++ return 0; + } + + static int fw_device_op_release(struct inode *inode, struct file *file) + { + struct client *client = file->private_data; + struct event *e, *next_e; +- struct client_resource *r, *next_r; +- unsigned long flags; + +- if (client->buffer.pages) +- fw_iso_buffer_destroy(&client->buffer, client->device->card); ++ mutex_lock(&client->device->client_list_mutex); ++ list_del(&client->link); ++ mutex_unlock(&client->device->client_list_mutex); + + if (client->iso_context) + fw_iso_context_destroy(client->iso_context); + +- list_for_each_entry_safe(r, next_r, &client->resource_list, link) +- r->release(client, r); ++ if (client->buffer.pages) ++ fw_iso_buffer_destroy(&client->buffer, client->device->card); + +- /* +- * FIXME: We should wait for the async tasklets to stop +- * running before freeing the memory. +- */ ++ /* Freeze client->resource_idr and client->event_list */ ++ spin_lock_irq(&client->lock); ++ client->in_shutdown = true; ++ spin_unlock_irq(&client->lock); ++ ++ idr_for_each(&client->resource_idr, shutdown_resource, client); ++ idr_remove_all(&client->resource_idr); ++ idr_destroy(&client->resource_idr); + + list_for_each_entry_safe(e, next_e, &client->event_list, link) + kfree(e); + +- spin_lock_irqsave(&client->device->card->lock, flags); +- list_del(&client->link); +- spin_unlock_irqrestore(&client->device->card->lock, flags); +- +- fw_device_put(client->device); +- kfree(client); ++ client_put(client); + + return 0; + } +diff -Naurp linux-2.6-git/drivers/firewire/fw-device.c firewire-git/drivers/firewire/fw-device.c +--- linux-2.6-git/drivers/firewire/fw-device.c 2009-01-30 13:39:02.989651512 -0500 ++++ firewire-git/drivers/firewire/fw-device.c 2009-01-30 13:35:51.860646788 -0500 +@@ -27,8 +27,10 @@ + #include <linux/idr.h> + #include <linux/jiffies.h> + #include <linux/string.h> ++#include <linux/mutex.h> + #include <linux/rwsem.h> + #include <linux/semaphore.h> ++#include <linux/spinlock.h> + #include <asm/system.h> + #include <linux/ctype.h> + #include "fw-transaction.h" +@@ -132,8 +134,7 @@ static int get_modalias(struct fw_unit * + vendor, model, specifier_id, version); + } + +-static int +-fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env) ++static int fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env) + { + struct fw_unit *unit = fw_unit(dev); + char modalias[64]; +@@ -191,8 +192,8 @@ struct config_rom_attribute { + u32 key; + }; + +-static ssize_t +-show_immediate(struct device *dev, struct device_attribute *dattr, char *buf) ++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); +@@ -223,8 +224,8 @@ show_immediate(struct device *dev, struc + #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) ++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); +@@ -293,10 +294,9 @@ static struct config_rom_attribute confi + 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) ++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; +@@ -319,9 +319,8 @@ init_fw_attribute_group(struct device *d + dev->groups = group->groups; + } + +-static ssize_t +-modalias_show(struct device *dev, +- struct device_attribute *attr, char *buf) ++static ssize_t modalias_show(struct device *dev, ++ struct device_attribute *attr, char *buf) + { + struct fw_unit *unit = fw_unit(dev); + int length; +@@ -332,9 +331,8 @@ modalias_show(struct device *dev, + return length + 1; + } + +-static ssize_t +-rom_index_show(struct device *dev, +- struct device_attribute *attr, char *buf) ++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); +@@ -349,8 +347,8 @@ static struct device_attribute fw_unit_a + __ATTR_NULL, + }; + +-static ssize_t +-config_rom_show(struct device *dev, struct device_attribute *attr, char *buf) ++static ssize_t config_rom_show(struct device *dev, ++ struct device_attribute *attr, char *buf) + { + struct fw_device *device = fw_device(dev); + size_t length; +@@ -363,8 +361,8 @@ config_rom_show(struct device *dev, stru + return length; + } + +-static ssize_t +-guid_show(struct device *dev, struct device_attribute *attr, char *buf) ++static ssize_t guid_show(struct device *dev, ++ struct device_attribute *attr, char *buf) + { + struct fw_device *device = fw_device(dev); + int ret; +@@ -383,8 +381,8 @@ static struct device_attribute fw_device + __ATTR_NULL, + }; + +-static int +-read_rom(struct fw_device *device, int generation, int index, u32 *data) ++static int read_rom(struct fw_device *device, ++ int generation, int index, u32 *data) + { + int rcode; + +@@ -1004,6 +1002,7 @@ void fw_node_event(struct fw_card *card, + device->node = fw_node_get(node); + device->node_id = node->node_id; + device->generation = card->generation; ++ mutex_init(&device->client_list_mutex); + INIT_LIST_HEAD(&device->client_list); + + /* +diff -Naurp linux-2.6-git/drivers/firewire/fw-device.h firewire-git/drivers/firewire/fw-device.h +--- linux-2.6-git/drivers/firewire/fw-device.h 2009-01-30 13:39:02.989651512 -0500 ++++ firewire-git/drivers/firewire/fw-device.h 2009-01-30 13:35:51.860646788 -0500 +@@ -23,6 +23,7 @@ + #include <linux/cdev.h> + #include <linux/idr.h> + #include <linux/rwsem.h> ++#include <linux/mutex.h> + #include <asm/atomic.h> + + enum fw_device_state { +@@ -64,7 +65,10 @@ struct fw_device { + bool cmc; + struct fw_card *card; + struct device device; ++ ++ struct mutex client_list_mutex; + struct list_head client_list; ++ + u32 *config_rom; + size_t config_rom_length; + int config_rom_retries; +@@ -176,8 +180,7 @@ struct fw_driver { + const struct fw_device_id *id_table; + }; + +-static inline struct fw_driver * +-fw_driver(struct device_driver *drv) ++static inline struct fw_driver *fw_driver(struct device_driver *drv) + { + return container_of(drv, struct fw_driver, driver); + } +diff -Naurp linux-2.6-git/drivers/firewire/fw-iso.c firewire-git/drivers/firewire/fw-iso.c +--- linux-2.6-git/drivers/firewire/fw-iso.c 2008-11-04 11:18:33.000000000 -0500 ++++ firewire-git/drivers/firewire/fw-iso.c 2009-01-30 13:35:51.860646788 -0500 +@@ -1,5 +1,7 @@ + /* +- * Isochronous IO functionality ++ * Isochronous I/O functionality: ++ * - Isochronous DMA context management ++ * - Isochronous bus resource management (channels, bandwidth), client side + * + * Copyright (C) 2006 Kristian Hoegsberg <krh@bitplanet.net> + * +@@ -18,21 +20,25 @@ + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +-#include <linux/kernel.h> +-#include <linux/module.h> + #include <linux/dma-mapping.h> +-#include <linux/vmalloc.h> ++#include <linux/errno.h> ++#include <linux/firewire-constants.h> ++#include <linux/kernel.h> + #include <linux/mm.h> ++#include <linux/spinlock.h> ++#include <linux/vmalloc.h> + +-#include "fw-transaction.h" + #include "fw-topology.h" +-#include "fw-device.h" ++#include "fw-transaction.h" + +-int +-fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, +- int page_count, enum dma_data_direction direction) ++/* ++ * Isochronous DMA context management ++ */ ++ ++int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, ++ int page_count, enum dma_data_direction direction) + { +- int i, j, retval = -ENOMEM; ++ int i, j; + dma_addr_t address; + + buffer->page_count = page_count; +@@ -69,19 +75,19 @@ fw_iso_buffer_init(struct fw_iso_buffer + kfree(buffer->pages); + out: + buffer->pages = NULL; +- return retval; ++ return -ENOMEM; + } + + int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma) + { + unsigned long uaddr; +- int i, retval; ++ int i, ret; + + uaddr = vma->vm_start; + for (i = 0; i < buffer->page_count; i++) { +- retval = vm_insert_page(vma, uaddr, buffer->pages[i]); +- if (retval) +- return retval; ++ ret = vm_insert_page(vma, uaddr, buffer->pages[i]); ++ if (ret) ++ return ret; + uaddr += PAGE_SIZE; + } + +@@ -105,14 +111,14 @@ void fw_iso_buffer_destroy(struct fw_iso + buffer->pages = NULL; + } + +-struct fw_iso_context * +-fw_iso_context_create(struct fw_card *card, int type, +- int channel, int speed, size_t header_size, +- fw_iso_callback_t callback, void *callback_data) ++struct fw_iso_context *fw_iso_context_create(struct fw_card *card, ++ int type, int channel, int speed, size_t header_size, ++ fw_iso_callback_t callback, void *callback_data) + { + struct fw_iso_context *ctx; + +- ctx = card->driver->allocate_iso_context(card, type, header_size); ++ ctx = card->driver->allocate_iso_context(card, ++ type, channel, header_size); + if (IS_ERR(ctx)) + return ctx; + +@@ -134,25 +140,186 @@ void fw_iso_context_destroy(struct fw_is + card->driver->free_iso_context(ctx); + } + +-int +-fw_iso_context_start(struct fw_iso_context *ctx, int cycle, int sync, int tags) ++int fw_iso_context_start(struct fw_iso_context *ctx, ++ int cycle, int sync, int tags) + { + return ctx->card->driver->start_iso(ctx, cycle, sync, tags); + } + +-int +-fw_iso_context_queue(struct fw_iso_context *ctx, +- struct fw_iso_packet *packet, +- struct fw_iso_buffer *buffer, +- unsigned long payload) ++int fw_iso_context_queue(struct fw_iso_context *ctx, ++ struct fw_iso_packet *packet, ++ struct fw_iso_buffer *buffer, ++ unsigned long payload) + { + struct fw_card *card = ctx->card; + + return card->driver->queue_iso(ctx, packet, buffer, payload); + } + +-int +-fw_iso_context_stop(struct fw_iso_context *ctx) ++int fw_iso_context_stop(struct fw_iso_context *ctx) + { + return ctx->card->driver->stop_iso(ctx); + } ++ ++/* ++ * Isochronous bus resource management (channels, bandwidth), client side ++ */ ++ ++static int manage_bandwidth(struct fw_card *card, int irm_id, int generation, ++ int bandwidth, bool allocate) ++{ ++ __be32 data[2]; ++ int try, new, old = allocate ? BANDWIDTH_AVAILABLE_INITIAL : 0; ++ ++ /* ++ * On a 1394a IRM with low contention, try < 1 is enough. ++ * On a 1394-1995 IRM, we need at least try < 2. ++ * Let's just do try < 5. ++ */ ++ for (try = 0; try < 5; try++) { ++ new = allocate ? old - bandwidth : old + bandwidth; ++ if (new < 0 || new > BANDWIDTH_AVAILABLE_INITIAL) ++ break; ++ ++ data[0] = cpu_to_be32(old); ++ data[1] = cpu_to_be32(new); ++ switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP, ++ irm_id, generation, SCODE_100, ++ CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE, ++ data, sizeof(data))) { ++ case RCODE_GENERATION: ++ /* A generation change frees all bandwidth. */ ++ return allocate ? -EAGAIN : bandwidth; ++ ++ case RCODE_COMPLETE: ++ if (be32_to_cpup(data) == old) ++ return bandwidth; ++ ++ old = be32_to_cpup(data); ++ /* Fall through. */ ++ } ++ } ++ ++ return -EIO; ++} ++ ++static int manage_channel(struct fw_card *card, int irm_id, int generation, ++ u32 channels_mask, u64 offset, bool allocate) ++{ ++ __be32 data[2], c, all, old; ++ int i, retry = 5; ++ ++ old = all = allocate ? cpu_to_be32(~0) : 0; ++ ++ for (i = 0; i < 32; i++) { ++ if (!(channels_mask & 1 << i)) ++ continue; ++ ++ c = cpu_to_be32(1 << (31 - i)); ++ if ((old & c) != (all & c)) ++ continue; ++ ++ data[0] = old; ++ data[1] = old ^ c; ++ switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP, ++ irm_id, generation, SCODE_100, ++ offset, data, sizeof(data))) { ++ case RCODE_GENERATION: ++ /* A generation change frees all channels. */ ++ return allocate ? -EAGAIN : i; ++ ++ case RCODE_COMPLETE: ++ if (data[0] == old) ++ return i; ++ ++ old = data[0]; ++ ++ /* Is the IRM 1394a-2000 compliant? */ ++ if ((data[0] & c) == (data[1] & c)) ++ continue; ++ ++ /* 1394-1995 IRM, fall through to retry. */ ++ default: ++ if (retry--) ++ i--; ++ } ++ } ++ ++ return -EIO; ++} ++ ++static void deallocate_channel(struct fw_card *card, int irm_id, ++ int generation, int channel) ++{ ++ u32 mask; ++ u64 offset; ++ ++ mask = channel < 32 ? 1 << channel : 1 << (channel - 32); ++ offset = channel < 32 ? CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI : ++ CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO; ++ ++ manage_channel(card, irm_id, generation, mask, offset, false); ++} ++ ++/** ++ * fw_iso_resource_manage - Allocate or deallocate a channel and/or bandwidth ++ * ++ * In parameters: card, generation, channels_mask, bandwidth, allocate ++ * Out parameters: channel, bandwidth ++ * This function blocks (sleeps) during communication with the IRM. ++ * ++ * Allocates or deallocates at most one channel out of channels_mask. ++ * channels_mask is a bitfield with MSB for channel 63 and LSB for channel 0. ++ * (Note, the IRM's CHANNELS_AVAILABLE is a big-endian bitfield with MSB for ++ * channel 0 and LSB for channel 63.) ++ * Allocates or deallocates as many bandwidth allocation units as specified. ++ * ++ * Returns channel < 0 if no channel was allocated or deallocated. ++ * Returns bandwidth = 0 if no bandwidth was allocated or deallocated. ++ * ++ * If generation is stale, deallocations succeed but allocations fail with ++ * channel = -EAGAIN. ++ * ++ * If channel allocation fails, no bandwidth will be allocated either. ++ * If bandwidth allocation fails, no channel will be allocated either. ++ * But deallocations of channel and bandwidth are tried independently ++ * of each other's success. ++ */ ++void fw_iso_resource_manage(struct fw_card *card, int generation, ++ u64 channels_mask, int *channel, int *bandwidth, ++ bool allocate) ++{ ++ u32 channels_hi = channels_mask; /* channels 31...0 */ ++ u32 channels_lo = channels_mask >> 32; /* channels 63...32 */ ++ int irm_id, ret, c = -EINVAL; ++ ++ spin_lock_irq(&card->lock); ++ irm_id = card->irm_node->node_id; ++ spin_unlock_irq(&card->lock); ++ ++ if (channels_hi) ++ c = manage_channel(card, irm_id, generation, channels_hi, ++ CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI, allocate); ++ if (channels_lo && c < 0) { ++ c = manage_channel(card, irm_id, generation, channels_lo, ++ CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO, allocate); ++ if (c >= 0) ++ c += 32; ++ } ++ *channel = c; ++ ++ if (allocate && channels_mask != 0 && c < 0) ++ *bandwidth = 0; ++ ++ if (*bandwidth == 0) ++ return; ++ ++ ret = manage_bandwidth(card, irm_id, generation, *bandwidth, allocate); ++ if (ret < 0) ++ *bandwidth = 0; ++ ++ if (allocate && ret < 0 && c >= 0) { ++ deallocate_channel(card, irm_id, generation, c); ++ *channel = ret; ++ } ++} +diff -Naurp linux-2.6-git/drivers/firewire/fw-ohci.c firewire-git/drivers/firewire/fw-ohci.c +--- linux-2.6-git/drivers/firewire/fw-ohci.c 2009-01-30 13:39:02.990772025 -0500 ++++ firewire-git/drivers/firewire/fw-ohci.c 2009-01-30 13:35:51.861646907 -0500 +@@ -205,6 +205,7 @@ struct fw_ohci { + + u32 it_context_mask; + struct iso_context *it_context_list; ++ u64 ir_context_channels; + u32 ir_context_mask; + struct iso_context *ir_context_list; + }; +@@ -441,9 +442,8 @@ static inline void flush_writes(const st + reg_read(ohci, OHCI1394_Version); + } + +-static int +-ohci_update_phy_reg(struct fw_card *card, int addr, +- int clear_bits, int set_bits) ++static int ohci_update_phy_reg(struct fw_card *card, int addr, ++ int clear_bits, int set_bits) + { + struct fw_ohci *ohci = fw_ohci(card); + u32 val, old; +@@ -658,8 +658,8 @@ static void ar_context_tasklet(unsigned + } + } + +-static int +-ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci, u32 regs) ++static int ar_context_init(struct ar_context *ctx, ++ struct fw_ohci *ohci, u32 regs) + { + struct ar_buffer ab; + +@@ -690,8 +690,7 @@ static void ar_context_run(struct ar_con + flush_writes(ctx->ohci); + } + +-static struct descriptor * +-find_branch_descriptor(struct descriptor *d, int z) ++static struct descriptor *find_branch_descriptor(struct descriptor *d, int z) + { + int b, key; + +@@ -751,8 +750,7 @@ static void context_tasklet(unsigned lon + * Allocate a new buffer and add it to the list of free buffers for this + * context. Must be called with ohci->lock held. + */ +-static int +-context_add_buffer(struct context *ctx) ++static int context_add_buffer(struct context *ctx) + { + struct descriptor_buffer *desc; + dma_addr_t uninitialized_var(bus_addr); +@@ -781,9 +779,8 @@ context_add_buffer(struct context *ctx) + return 0; + } + +-static int +-context_init(struct context *ctx, struct fw_ohci *ohci, +- u32 regs, descriptor_callback_t callback) ++static int context_init(struct context *ctx, struct fw_ohci *ohci, ++ u32 regs, descriptor_callback_t callback) + { + ctx->ohci = ohci; + ctx->regs = regs; +@@ -814,8 +811,7 @@ context_init(struct context *ctx, struct + return 0; + } + +-static void +-context_release(struct context *ctx) ++static void context_release(struct context *ctx) + { + struct fw_card *card = &ctx->ohci->card; + struct descriptor_buffer *desc, *tmp; +@@ -827,8 +823,8 @@ context_release(struct context *ctx) + } + + /* Must be called with ohci->lock held */ +-static struct descriptor * +-context_get_descriptors(struct context *ctx, int z, dma_addr_t *d_bus) ++static struct descriptor *context_get_descriptors(struct context *ctx, ++ int z, dma_addr_t *d_bus) + { + struct descriptor *d = NULL; + struct descriptor_buffer *desc = ctx->buffer_tail; +@@ -912,8 +908,8 @@ struct driver_data { + * Must always be called with the ochi->lock held to ensure proper + * generation handling and locking around packet queue manipulation. + */ +-static int +-at_context_queue_packet(struct context *ctx, struct fw_packet *packet) ++static int at_context_queue_packet(struct context *ctx, ++ struct fw_packet *packet) + { + struct fw_ohci *ohci = ctx->ohci; + dma_addr_t d_bus, uninitialized_var(payload_bus); +@@ -1095,8 +1091,8 @@ static int handle_at_packet(struct conte + #define HEADER_GET_DATA_LENGTH(q) (((q) >> 16) & 0xffff) + #define HEADER_GET_EXTENDED_TCODE(q) (((q) >> 0) & 0xffff) + +-static void +-handle_local_rom(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr) ++static void handle_local_rom(struct fw_ohci *ohci, ++ struct fw_packet *packet, u32 csr) + { + struct fw_packet response; + int tcode, length, i; +@@ -1122,8 +1118,8 @@ handle_local_rom(struct fw_ohci *ohci, s + fw_core_handle_response(&ohci->card, &response); + } + +-static void +-handle_local_lock(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr) ++static void handle_local_lock(struct fw_ohci *ohci, ++ struct fw_packet *packet, u32 csr) + { + struct fw_packet response; + int tcode, length, ext_tcode, sel; +@@ -1164,8 +1160,7 @@ handle_local_lock(struct fw_ohci *ohci, + fw_core_handle_response(&ohci->card, &response); + } + +-static void +-handle_local_request(struct context *ctx, struct fw_packet *packet) ++static void handle_local_request(struct context *ctx, struct fw_packet *packet) + { + u64 offset; + u32 csr; +@@ -1205,11 +1200,10 @@ handle_local_request(struct context *ctx + } + } + +-static void +-at_context_transmit(struct context *ctx, struct fw_packet *packet) ++static void at_context_transmit(struct context *ctx, struct fw_packet *packet) + { + unsigned long flags; +- int retval; ++ int ret; + + spin_lock_irqsave(&ctx->ohci->lock, flags); + +@@ -1220,10 +1214,10 @@ at_context_transmit(struct context *ctx, + return; + } + +- retval = at_context_queue_packet(ctx, packet); ++ ret = at_context_queue_packet(ctx, packet); + spin_unlock_irqrestore(&ctx->ohci->lock, flags); + +- if (retval < 0) ++ if (ret < 0) + packet->callback(packet, &ctx->ohci->card, packet->ack); + + } +@@ -1590,12 +1584,12 @@ static int ohci_enable(struct fw_card *c + return 0; + } + +-static int +-ohci_set_config_rom(struct fw_card *card, u32 *config_rom, size_t length) ++static int ohci_set_config_rom(struct fw_card *card, ++ u32 *config_rom, size_t length) + { + struct fw_ohci *ohci; + unsigned long flags; +- int retval = -EBUSY; ++ int ret = -EBUSY; + __be32 *next_config_rom; + dma_addr_t uninitialized_var(next_config_rom_bus); + +@@ -1649,7 +1643,7 @@ ohci_set_config_rom(struct fw_card *card + + reg_write(ohci, OHCI1394_ConfigROMmap, + ohci->next_config_rom_bus); +- retval = 0; ++ ret = 0; + } + + spin_unlock_irqrestore(&ohci->lock, flags); +@@ -1661,13 +1655,13 @@ ohci_set_config_rom(struct fw_card *card + * controller could need to access it before the bus reset + * takes effect. + */ +- if (retval == 0) ++ if (ret == 0) + fw_core_initiate_bus_reset(&ohci->card, 1); + else + dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, + next_config_rom, next_config_rom_bus); + +- return retval; ++ return ret; + } + + static void ohci_send_request(struct fw_card *card, struct fw_packet *packet) +@@ -1689,7 +1683,7 @@ static int ohci_cancel_packet(struct fw_ + struct fw_ohci *ohci = fw_ohci(card); + struct context *ctx = &ohci->at_request_ctx; + struct driver_data *driver_data = packet->driver_data; +- int retval = -ENOENT; ++ int ret = -ENOENT; + + tasklet_disable(&ctx->tasklet); + +@@ -1704,23 +1698,22 @@ static int ohci_cancel_packet(struct fw_ + driver_data->packet = NULL; + packet->ack = RCODE_CANCELLED; + packet->callback(packet, &ohci->card, packet->ack); +- retval = 0; +- ++ ret = 0; + out: + tasklet_enable(&ctx->tasklet); + +- return retval; ++ return ret; + } + +-static int +-ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation) ++static int ohci_enable_phys_dma(struct fw_card *card, ++ int node_id, int generation) + { + #ifdef CONFIG_FIREWIRE_OHCI_REMOTE_DMA + return 0; + #else + struct fw_ohci *ohci = fw_ohci(card); + unsigned long flags; +- int n, retval = 0; ++ int n, ret = 0; + + /* + * FIXME: Make sure this bitmask is cleared when we clear the busReset +@@ -1730,7 +1723,7 @@ ohci_enable_phys_dma(struct fw_card *car + spin_lock_irqsave(&ohci->lock, flags); + + if (ohci->generation != generation) { +- retval = -ESTALE; ++ ret = -ESTALE; + goto out; + } + +@@ -1748,12 +1741,12 @@ ohci_enable_phys_dma(struct fw_card *car + flush_writes(ohci); + out: + spin_unlock_irqrestore(&ohci->lock, flags); +- return retval; ++ ++ return ret; + #endif /* CONFIG_FIREWIRE_OHCI_REMOTE_DMA */ + } + +-static u64 +-ohci_get_bus_time(struct fw_card *card) ++static u64 ohci_get_bus_time(struct fw_card *card) + { + struct fw_ohci *ohci = fw_ohci(card); + u32 cycle_time; +@@ -1765,6 +1758,28 @@ ohci_get_bus_time(struct fw_card *card) + return bus_time; + } + ++static void copy_iso_headers(struct iso_context *ctx, void *p) ++{ ++ int i = ctx->header_length; ++ ++ if (i + ctx->base.header_size > PAGE_SIZE) ++ return; ++ ++ /* ++ * The iso header is byteswapped to little endian by ++ * the controller, but the remaining header quadlets ++ * are big endian. We want to present all the headers ++ * as big endian, so we have to swap the first quadlet. ++ */ ++ if (ctx->base.header_size > 0) ++ *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4)); ++ if (ctx->base.header_size > 4) ++ *(u32 *) (ctx->header + i + 4) = __swab32(*(u32 *) p); ++ if (ctx->base.header_size > 8) ++ memcpy(ctx->header + i + 8, p + 8, ctx->base.header_size - 8); ++ ctx->header_length += ctx->base.header_size; ++} ++ + static int handle_ir_dualbuffer_packet(struct context *context, + struct descriptor *d, + struct descriptor *last) +@@ -1775,7 +1790,6 @@ static int handle_ir_dualbuffer_packet(s + __le32 *ir_header; + size_t header_length; + void *p, *end; +- int i; + + if (db->first_res_count != 0 && db->second_res_count != 0) { + if (ctx->excess_bytes <= le16_to_cpu(db->second_req_count)) { +@@ -1788,25 +1802,14 @@ static int handle_ir_dualbuffer_packet(s + header_length = le16_to_cpu(db->first_req_count) - + le16_to_cpu(db->first_res_count); + +- i = ctx->header_length; + p = db + 1; + end = p + header_length; +- while (p < end && i + ctx->base.header_size <= PAGE_SIZE) { +- /* +- * The iso header is byteswapped to little endian by +- * the controller, but the remaining header quadlets +- * are big endian. We want to present all the headers +- * as big endian, so we have to swap the first +- * quadlet. +- */ +- *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4)); +- memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4); +- i += ctx->base.header_size; ++ while (p < end) { ++ copy_iso_headers(ctx, p); + ctx->excess_bytes += + (le32_to_cpu(*(__le32 *)(p + 4)) >> 16) & 0xffff; +- p += ctx->base.header_size + 4; ++ p += max(ctx->base.header_size, (size_t)8); + } +- ctx->header_length = i; + + ctx->excess_bytes -= le16_to_cpu(db->second_req_count) - + le16_to_cpu(db->second_res_count); +@@ -1832,7 +1835,6 @@ static int handle_ir_packet_per_buffer(s + struct descriptor *pd; + __le32 *ir_header; + void *p; +- int i; + + for (pd = d; pd <= last; pd++) { + if (pd->transfer_status) +@@ -1842,21 +1844,8 @@ static int handle_ir_packet_per_buffer(s + /* Descriptor(s) not done yet, stop iteration */ + return 0; + +- i = ctx->header_length; +- p = last + 1; +- +- if (ctx->base.header_size > 0 && +- i + ctx->base.header_size <= PAGE_SIZE) { +- /* +- * The iso header is byteswapped to little endian by +- * the controller, but the remaining header quadlets +- * are big endian. We want to present all the headers +- * as big endian, so we have to swap the first quadlet. +- */ +- *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4)); +- memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4); +- ctx->header_length += ctx->base.header_size; +- } ++ p = last + 1; ++ copy_iso_headers(ctx, p); + + if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) { + ir_header = (__le32 *) p; +@@ -1888,21 +1877,24 @@ static int handle_it_packet(struct conte + return 1; + } + +-static struct fw_iso_context * +-ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size) ++static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, ++ int type, int channel, size_t header_size) + { + struct fw_ohci *ohci = fw_ohci(card); + struct iso_context *ctx, *list; + descriptor_callback_t callback; ++ u64 *channels, dont_care = ~0ULL; + u32 *mask, regs; + unsigned long flags; +- int index, retval = -ENOMEM; ++ int index, ret = -ENOMEM; + + if (type == FW_ISO_CONTEXT_TRANSMIT) { ++ channels = &dont_care; + mask = &ohci->it_context_mask; + list = ohci->it_context_list; + callback = handle_it_packet; + } else { ++ channels = &ohci->ir_context_channels; + mask = &ohci->ir_context_mask; + list = ohci->ir_context_list; + if (ohci->use_dualbuffer) +@@ -1912,9 +1904,11 @@ ohci_allocate_iso_context(struct fw_card + } + + spin_lock_irqsave(&ohci->lock, flags); +- index = ffs(*mask) - 1; +- if (index >= 0) ++ index = *channels & 1ULL << channel ? ffs(*mask) - 1 : -1; ++ if (index >= 0) { ++ *channels &= ~(1ULL << channel); + *mask &= ~(1 << index); ++ } + spin_unlock_irqrestore(&ohci->lock, flags); + + if (index < 0) +@@ -1932,8 +1926,8 @@ ohci_allocate_iso_context(struct fw_card + if (ctx->header == NULL) + goto out; + +- retval = context_init(&ctx->context, ohci, regs, callback); +- if (retval < 0) ++ ret = context_init(&ctx->context, ohci, regs, callback); ++ if (ret < 0) + goto out_with_header; + + return &ctx->base; +@@ -1945,7 +1939,7 @@ ohci_allocate_iso_context(struct fw_card + *mask |= 1 << index; + spin_unlock_irqrestore(&ohci->lock, flags); + +- return ERR_PTR(retval); ++ return ERR_PTR(ret); + } + + static int ohci_start_iso(struct fw_iso_context *base, +@@ -2024,16 +2018,16 @@ static void ohci_free_iso_context(struct + } else { + index = ctx - ohci->ir_context_list; + ohci->ir_context_mask |= 1 << index; ++ ohci->ir_context_channels |= 1ULL << base->channel; + } + + spin_unlock_irqrestore(&ohci->lock, flags); + } + +-static int +-ohci_queue_iso_transmit(struct fw_iso_context *base, +- struct fw_iso_packet *packet, +- struct fw_iso_buffer *buffer, +- unsigned long payload) ++static int ohci_queue_iso_transmit(struct fw_iso_context *base, ++ struct fw_iso_packet *packet, ++ struct fw_iso_buffer *buffer, ++ unsigned long payload) + { + struct iso_context *ctx = container_of(base, struct iso_context, base); + struct descriptor *d, *last, *pd; +@@ -2128,11 +2122,10 @@ ohci_queue_iso_transmit(struct fw_iso_co + return 0; + } + +-static int +-ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base, +- struct fw_iso_packet *packet, +- struct fw_iso_buffer *buffer, +- unsigned long payload) ++static int ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base, ++ struct fw_iso_packet *packet, ++ struct fw_iso_buffer *buffer, ++ unsigned long payload) + { + struct iso_context *ctx = container_of(base, struct iso_context, base); + struct db_descriptor *db = NULL; +@@ -2151,11 +2144,11 @@ ohci_queue_iso_receive_dualbuffer(struct + z = 2; + + /* +- * The OHCI controller puts the status word in the header +- * buffer too, so we need 4 extra bytes per packet. ++ * The OHCI controller puts the isochronous header and trailer in the ++ * buffer, so we need at least 8 bytes. + */ + packet_count = p->header_length / ctx->base.header_size; +- header_size = packet_count * (ctx->base.header_size + 4); ++ header_size = packet_count * max(ctx->base.header_size, (size_t)8); + + /* Get header size in number of descriptors. */ + header_z = DIV_ROUND_UP(header_size, sizeof(*d)); +@@ -2173,7 +2166,8 @@ ohci_queue_iso_receive_dualbuffer(struct + db = (struct db_descriptor *) d; + db->control = cpu_to_le16(DESCRIPTOR_STATUS | + DESCRIPTOR_BRANCH_ALWAYS); +- db->first_size = cpu_to_le16(ctx->base.header_size + 4); ++ db->first_size = ++ cpu_to_le16(max(ctx->base.header_size, (size_t)8)); + if (p->skip && rest == p->payload_length) { + db->control |= cpu_to_le16(DESCRIPTOR_WAIT); + db->first_req_count = db->first_size; +@@ -2208,11 +2202,10 @@ ohci_queue_iso_receive_dualbuffer(struct + return 0; + } + +-static int +-ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base, +- struct fw_iso_packet *packet, +- struct fw_iso_buffer *buffer, +- unsigned long payload) ++static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base, ++ struct fw_iso_packet *packet, ++ struct fw_iso_buffer *buffer, ++ unsigned long payload) + { + struct iso_context *ctx = container_of(base, struct iso_context, base); + struct descriptor *d = NULL, *pd = NULL; +@@ -2223,11 +2216,11 @@ ohci_queue_iso_receive_packet_per_buffer + int page, offset, packet_count, header_size, payload_per_buffer; + + /* +- * The OHCI controller puts the status word in the +- * buffer too, so we need 4 extra bytes per packet. ++ * The OHCI controller puts the isochronous header and trailer in the ++ * buffer, so we need at least 8 bytes. + */ + packet_count = p->header_length / ctx->base.header_size; +- header_size = ctx->base.header_size + 4; ++ header_size = max(ctx->base.header_size, (size_t)8); + + /* Get header size in number of descriptors. */ + header_z = DIV_ROUND_UP(header_size, sizeof(*d)); +@@ -2286,29 +2279,27 @@ ohci_queue_iso_receive_packet_per_buffer + return 0; + } + +-static int +-ohci_queue_iso(struct fw_iso_context *base, +- struct fw_iso_packet *packet, +- struct fw_iso_buffer *buffer, +- unsigned long payload) ++static int ohci_queue_iso(struct fw_iso_context *base, ++ struct fw_iso_packet *packet, ++ struct fw_iso_buffer *buffer, ++ unsigned long payload) + { + struct iso_context *ctx = container_of(base, struct iso_context, base); + unsigned long flags; +- int retval; ++ int ret; + + spin_lock_irqsave(&ctx->context.ohci->lock, flags); + if (base->type == FW_ISO_CONTEXT_TRANSMIT) +- retval = ohci_queue_iso_transmit(base, packet, buffer, payload); ++ ret = ohci_queue_iso_transmit(base, packet, buffer, payload); + else if (ctx->context.ohci->use_dualbuffer) +- retval = ohci_queue_iso_receive_dualbuffer(base, packet, +- buffer, payload); ++ ret = ohci_queue_iso_receive_dualbuffer(base, packet, ++ buffer, payload); + else +- retval = ohci_queue_iso_receive_packet_per_buffer(base, packet, +- buffer, +- payload); ++ ret = ohci_queue_iso_receive_packet_per_buffer(base, packet, ++ buffer, payload); + spin_unlock_irqrestore(&ctx->context.ohci->lock, flags); + +- return retval; ++ return ret; + } + + static const struct fw_card_driver ohci_driver = { +@@ -2357,8 +2348,8 @@ static void ohci_pmac_off(struct pci_dev + #define ohci_pmac_off(dev) + #endif /* CONFIG_PPC_PMAC */ + +-static int __devinit +-pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) ++static int __devinit pci_probe(struct pci_dev *dev, ++ const struct pci_device_id *ent) + { + struct fw_ohci *ohci; + u32 bus_options, max_receive, link_speed, version; +@@ -2440,6 +2431,7 @@ pci_probe(struct pci_dev *dev, const str + ohci->it_context_list = kzalloc(size, GFP_KERNEL); + + reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, ~0); ++ ohci->ir_context_channels = ~0ULL; + ohci->ir_context_mask = reg_read(ohci, OHCI1394_IsoXmitIntMaskSet); + reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, ~0); + size = sizeof(struct iso_context) * hweight32(ohci->ir_context_mask); +diff -Naurp linux-2.6-git/drivers/firewire/fw-sbp2.c firewire-git/drivers/firewire/fw-sbp2.c +--- linux-2.6-git/drivers/firewire/fw-sbp2.c 2009-01-30 13:39:02.991771976 -0500 ++++ firewire-git/drivers/firewire/fw-sbp2.c 2009-01-30 13:35:51.861646907 -0500 +@@ -392,20 +392,18 @@ static const struct { + } + }; + +-static void +-free_orb(struct kref *kref) ++static void free_orb(struct kref *kref) + { + struct sbp2_orb *orb = container_of(kref, struct sbp2_orb, kref); + + kfree(orb); + } + +-static void +-sbp2_status_write(struct fw_card *card, struct fw_request *request, +- int tcode, int destination, int source, +- int generation, int speed, +- unsigned long long offset, +- void *payload, size_t length, void *callback_data) ++static void sbp2_status_write(struct fw_card *card, struct fw_request *request, ++ int tcode, int destination, int source, ++ int generation, int speed, ++ unsigned long long offset, ++ void *payload, size_t length, void *callback_data) + { + struct sbp2_logical_unit *lu = callback_data; + struct sbp2_orb *orb; +@@ -451,9 +449,8 @@ sbp2_status_write(struct fw_card *card, + fw_send_response(card, request, RCODE_COMPLETE); + } + +-static void +-complete_transaction(struct fw_card *card, int rcode, +- void *payload, size_t length, void *data) ++static void complete_transaction(struct fw_card *card, int rcode, ++ void *payload, size_t length, void *data) + { + struct sbp2_orb *orb = data; + unsigned long flags; +@@ -482,9 +479,8 @@ complete_transaction(struct fw_card *car + kref_put(&orb->kref, free_orb); + } + +-static void +-sbp2_send_orb(struct sbp2_orb *orb, struct sbp2_logical_unit *lu, +- int node_id, int generation, u64 offset) ++static void sbp2_send_orb(struct sbp2_orb *orb, struct sbp2_logical_unit *lu, ++ int node_id, int generation, u64 offset) + { + struct fw_device *device = fw_device(lu->tgt->unit->device.parent); + unsigned long flags; +@@ -531,8 +527,8 @@ static int sbp2_cancel_orbs(struct sbp2_ + return retval; + } + +-static void +-complete_management_orb(struct sbp2_orb *base_orb, struct sbp2_status *status) ++static void complete_management_orb(struct sbp2_orb *base_orb, ++ struct sbp2_status *status) + { + struct sbp2_management_orb *orb = + container_of(base_orb, struct sbp2_management_orb, base); +@@ -542,10 +538,9 @@ complete_management_orb(struct sbp2_orb + complete(&orb->done); + } + +-static int +-sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, +- int generation, int function, int lun_or_login_id, +- void *response) ++static int sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id, ++ int generation, int function, ++ int lun_or_login_id, void *response) + { + struct fw_device *device = fw_device(lu->tgt->unit->device.parent); + struct sbp2_management_orb *orb; +@@ -652,9 +647,8 @@ static void sbp2_agent_reset(struct sbp2 + &d, sizeof(d)); + } + +-static void +-complete_agent_reset_write_no_wait(struct fw_card *card, int rcode, +- void *payload, size_t length, void *data) ++static void complete_agent_reset_write_no_wait(struct fw_card *card, ++ int rcode, void *payload, size_t length, void *data) + { + kfree(data); + } +@@ -1299,8 +1293,7 @@ static void sbp2_unmap_scatterlist(struc + sizeof(orb->page_table), DMA_TO_DEVICE); + } + +-static unsigned int +-sbp2_status_to_sense_data(u8 *sbp2_status, u8 *sense_data) ++static unsigned int sbp2_status_to_sense_data(u8 *sbp2_status, u8 *sense_data) + { + int sam_status; + +@@ -1337,8 +1330,8 @@ sbp2_status_to_sense_data(u8 *sbp2_statu + } + } + +-static void +-complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status) ++static void complete_command_orb(struct sbp2_orb *base_orb, ++ struct sbp2_status *status) + { + struct sbp2_command_orb *orb = + container_of(base_orb, struct sbp2_command_orb, base); +@@ -1384,9 +1377,8 @@ complete_command_orb(struct sbp2_orb *ba + orb->done(orb->cmd); + } + +-static int +-sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device, +- struct sbp2_logical_unit *lu) ++static int sbp2_map_scatterlist(struct sbp2_command_orb *orb, ++ struct fw_device *device, struct sbp2_logical_unit *lu) + { + struct scatterlist *sg = scsi_sglist(orb->cmd); + int i, n; +@@ -1584,9 +1576,8 @@ static int sbp2_scsi_abort(struct scsi_c + * This is the concatenation of target port identifier and logical unit + * identifier as per SAM-2...SAM-4 annex A. + */ +-static ssize_t +-sbp2_sysfs_ieee1394_id_show(struct device *dev, struct device_attribute *attr, +- char *buf) ++static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *dev, ++ struct device_attribute *attr, char *buf) + { + struct scsi_device *sdev = to_scsi_device(dev); + struct sbp2_logical_unit *lu; +diff -Naurp linux-2.6-git/drivers/firewire/fw-topology.c firewire-git/drivers/firewire/fw-topology.c +--- linux-2.6-git/drivers/firewire/fw-topology.c 2009-01-30 13:39:02.991771976 -0500 ++++ firewire-git/drivers/firewire/fw-topology.c 2009-01-30 13:35:51.862647087 -0500 +@@ -314,9 +314,8 @@ typedef void (*fw_node_callback_t)(struc + struct fw_node * node, + struct fw_node * parent); + +-static void +-for_each_fw_node(struct fw_card *card, struct fw_node *root, +- fw_node_callback_t callback) ++static void for_each_fw_node(struct fw_card *card, struct fw_node *root, ++ fw_node_callback_t callback) + { + struct list_head list; + struct fw_node *node, *next, *child, *parent; +@@ -349,9 +348,8 @@ for_each_fw_node(struct fw_card *card, s + fw_node_put(node); + } + +-static void +-report_lost_node(struct fw_card *card, +- struct fw_node *node, struct fw_node *parent) ++static void report_lost_node(struct fw_card *card, ++ struct fw_node *node, struct fw_node *parent) + { + fw_node_event(card, node, FW_NODE_DESTROYED); + fw_node_put(node); +@@ -360,9 +358,8 @@ report_lost_node(struct fw_card *card, + card->bm_retries = 0; + } + +-static void +-report_found_node(struct fw_card *card, +- struct fw_node *node, struct fw_node *parent) ++static void report_found_node(struct fw_card *card, ++ struct fw_node *node, struct fw_node *parent) + { + int b_path = (node->phy_speed == SCODE_BETA); + +@@ -415,8 +412,7 @@ static void move_tree(struct fw_node *no + * found, lost or updated. Update the nodes in the card topology tree + * as we go. + */ +-static void +-update_tree(struct fw_card *card, struct fw_node *root) ++static void update_tree(struct fw_card *card, struct fw_node *root) + { + struct list_head list0, list1; + struct fw_node *node0, *node1, *next1; +@@ -497,8 +493,8 @@ update_tree(struct fw_card *card, struct + } + } + +-static void +-update_topology_map(struct fw_card *card, u32 *self_ids, int self_id_count) ++static void update_topology_map(struct fw_card *card, ++ u32 *self_ids, int self_id_count) + { + int node_count; + +@@ -510,10 +506,8 @@ update_topology_map(struct fw_card *card + fw_compute_block_crc(card->topology_map); + } + +-void +-fw_core_handle_bus_reset(struct fw_card *card, +- int node_id, int generation, +- int self_id_count, u32 * self_ids) ++void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, ++ int self_id_count, u32 *self_ids) + { + struct fw_node *local_node; + unsigned long flags; +diff -Naurp linux-2.6-git/drivers/firewire/fw-topology.h firewire-git/drivers/firewire/fw-topology.h +--- linux-2.6-git/drivers/firewire/fw-topology.h 2008-11-04 11:18:33.000000000 -0500 ++++ firewire-git/drivers/firewire/fw-topology.h 2009-01-30 13:35:51.862647087 -0500 +@@ -19,6 +19,11 @@ + #ifndef __fw_topology_h + #define __fw_topology_h + ++#include <linux/list.h> ++#include <linux/slab.h> ++ ++#include <asm/atomic.h> ++ + enum { + FW_NODE_CREATED, + FW_NODE_UPDATED, +@@ -51,26 +56,22 @@ struct fw_node { + struct fw_node *ports[0]; + }; + +-static inline struct fw_node * +-fw_node_get(struct fw_node *node) ++static inline struct fw_node *fw_node_get(struct fw_node *node) + { + atomic_inc(&node->ref_count); + + return node; + } + +-static inline void +-fw_node_put(struct fw_node *node) ++static inline void fw_node_put(struct fw_node *node) + { + if (atomic_dec_and_test(&node->ref_count)) + kfree(node); + } + +-void +-fw_destroy_nodes(struct fw_card *card); +- +-int +-fw_compute_block_crc(u32 *block); ++struct fw_card; ++void fw_destroy_nodes(struct fw_card *card); + ++int fw_compute_block_crc(u32 *block); + + #endif /* __fw_topology_h */ +diff -Naurp linux-2.6-git/drivers/firewire/fw-transaction.c firewire-git/drivers/firewire/fw-transaction.c +--- linux-2.6-git/drivers/firewire/fw-transaction.c 2009-01-30 13:39:02.991771976 -0500 ++++ firewire-git/drivers/firewire/fw-transaction.c 2009-01-30 13:35:51.862647087 -0500 +@@ -64,10 +64,9 @@ + #define PHY_CONFIG_ROOT_ID(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23)) + #define PHY_IDENTIFIER(id) ((id) << 30) + +-static int +-close_transaction(struct fw_transaction *transaction, +- struct fw_card *card, int rcode, +- u32 *payload, size_t length) ++static int close_transaction(struct fw_transaction *transaction, ++ struct fw_card *card, int rcode, ++ u32 *payload, size_t length) + { + struct fw_transaction *t; + unsigned long flags; +@@ -94,9 +93,8 @@ close_transaction(struct fw_transaction + * Only valid for transactions that are potentially pending (ie have + * been sent). + */ +-int +-fw_cancel_transaction(struct fw_card *card, +- struct fw_transaction *transaction) ++int fw_cancel_transaction(struct fw_card *card, ++ struct fw_transaction *transaction) + { + /* + * Cancel the packet transmission if it's still queued. That +@@ -116,9 +114,8 @@ fw_cancel_transaction(struct fw_card *ca + } + EXPORT_SYMBOL(fw_cancel_transaction); + +-static void +-transmit_complete_callback(struct fw_packet *packet, +- struct fw_card *card, int status) ++static void transmit_complete_callback(struct fw_packet *packet, ++ struct fw_card *card, int status) + { + struct fw_transaction *t = + container_of(packet, struct fw_transaction, packet); +@@ -151,8 +148,7 @@ transmit_complete_callback(struct fw_pac + } + } + +-static void +-fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, ++static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, + int destination_id, int source_id, int generation, int speed, + unsigned long long offset, void *payload, size_t length) + { +@@ -247,12 +243,10 @@ fw_fill_request(struct fw_packet *packet + * @param callback_data pointer to arbitrary data, which will be + * passed to the callback + */ +-void +-fw_send_request(struct fw_card *card, struct fw_transaction *t, +- int tcode, int destination_id, int generation, int speed, +- unsigned long long offset, +- void *payload, size_t length, +- fw_transaction_callback_t callback, void *callback_data) ++void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode, ++ int destination_id, int generation, int speed, ++ unsigned long long offset, void *payload, size_t length, ++ fw_transaction_callback_t callback, void *callback_data) + { + unsigned long flags; + int tlabel; +@@ -322,8 +316,8 @@ static void transaction_callback(struct + * Returns the RCODE. + */ + int fw_run_transaction(struct fw_card *card, int tcode, int destination_id, +- int generation, int speed, unsigned long long offset, +- void *data, size_t length) ++ int generation, int speed, unsigned long long offset, ++ void *data, size_t length) + { + struct transaction_callback_data d; + struct fw_transaction t; +@@ -399,9 +393,8 @@ void fw_flush_transactions(struct fw_car + } + } + +-static struct fw_address_handler * +-lookup_overlapping_address_handler(struct list_head *list, +- unsigned long long offset, size_t length) ++static struct fw_address_handler *lookup_overlapping_address_handler( ++ struct list_head *list, unsigned long long offset, size_t length) + { + struct fw_address_handler *handler; + +@@ -414,9 +407,8 @@ lookup_overlapping_address_handler(struc + return NULL; + } + +-static struct fw_address_handler * +-lookup_enclosing_address_handler(struct list_head *list, +- unsigned long long offset, size_t length) ++static struct fw_address_handler *lookup_enclosing_address_handler( ++ struct list_head *list, unsigned long long offset, size_t length) + { + struct fw_address_handler *handler; + +@@ -449,36 +441,44 @@ const struct fw_address_region fw_unit_s + #endif /* 0 */ + + /** +- * Allocate a range of addresses in the node space of the OHCI +- * controller. When a request is received that falls within the +- * specified address range, the specified callback is invoked. The +- * parameters passed to the callback give the details of the +- * particular request. ++ * fw_core_add_address_handler - register for incoming requests ++ * @handler: callback ++ * @region: region in the IEEE 1212 node space address range ++ * ++ * region->start, ->end, and handler->length have to be quadlet-aligned. ++ * ++ * When a request is received that falls within the specified address range, ++ * the specified callback is invoked. The parameters passed to the callback ++ * give the details of the particular request. + * + * Return value: 0 on success, non-zero otherwise. + * The start offset of the handler's address region is determined by + * fw_core_add_address_handler() and is returned in handler->offset. +- * The offset is quadlet-aligned. + */ +-int +-fw_core_add_address_handler(struct fw_address_handler *handler, +- const struct fw_address_region *region) ++int fw_core_add_address_handler(struct fw_address_handler *handler, ++ const struct fw_address_region *region) + { + struct fw_address_handler *other; + unsigned long flags; + int ret = -EBUSY; + ++ if (region->start & 0xffff000000000003ULL || ++ region->end & 0xffff000000000003ULL || ++ region->start >= region->end || ++ handler->length & 3 || ++ handler->length == 0) ++ return -EINVAL; ++ + spin_lock_irqsave(&address_handler_lock, flags); + +- handler->offset = roundup(region->start, 4); ++ handler->offset = region->start; + while (handler->offset + handler->length <= region->end) { + other = + lookup_overlapping_address_handler(&address_handler_list, + handler->offset, + handler->length); + if (other != NULL) { +- handler->offset = +- roundup(other->offset + other->length, 4); ++ handler->offset += other->length; + } else { + list_add_tail(&handler->link, &address_handler_list); + ret = 0; +@@ -493,12 +493,7 @@ fw_core_add_address_handler(struct fw_ad + EXPORT_SYMBOL(fw_core_add_address_handler); + + /** +- * Deallocate a range of addresses allocated with fw_allocate. This +- * will call the associated callback one last time with a the special +- * tcode TCODE_DEALLOCATE, to let the client destroy the registered +- * callback data. For convenience, the callback parameters offset and +- * length are set to the start and the length respectively for the +- * deallocated region, payload is set to NULL. ++ * fw_core_remove_address_handler - unregister an address handler + */ + void fw_core_remove_address_handler(struct fw_address_handler *handler) + { +@@ -518,9 +513,8 @@ struct fw_request { + u32 data[0]; + }; + +-static void +-free_response_callback(struct fw_packet *packet, +- struct fw_card *card, int status) ++static void free_response_callback(struct fw_packet *packet, ++ struct fw_card *card, int status) + { + struct fw_request *request; + +@@ -528,9 +522,8 @@ free_response_callback(struct fw_packet + kfree(request); + } + +-void +-fw_fill_response(struct fw_packet *response, u32 *request_header, +- int rcode, void *payload, size_t length) ++void fw_fill_response(struct fw_packet *response, u32 *request_header, ++ int rcode, void *payload, size_t length) + { + int tcode, tlabel, extended_tcode, source, destination; + +@@ -588,8 +581,7 @@ fw_fill_response(struct fw_packet *respo + } + EXPORT_SYMBOL(fw_fill_response); + +-static struct fw_request * +-allocate_request(struct fw_packet *p) ++static struct fw_request *allocate_request(struct fw_packet *p) + { + struct fw_request *request; + u32 *data, length; +@@ -649,8 +641,8 @@ allocate_request(struct fw_packet *p) + return request; + } + +-void +-fw_send_response(struct fw_card *card, struct fw_request *request, int rcode) ++void fw_send_response(struct fw_card *card, ++ struct fw_request *request, int rcode) + { + /* unified transaction or broadcast transaction: don't respond */ + if (request->ack != ACK_PENDING || +@@ -670,8 +662,7 @@ fw_send_response(struct fw_card *card, s + } + EXPORT_SYMBOL(fw_send_response); + +-void +-fw_core_handle_request(struct fw_card *card, struct fw_packet *p) ++void fw_core_handle_request(struct fw_card *card, struct fw_packet *p) + { + struct fw_address_handler *handler; + struct fw_request *request; +@@ -719,8 +710,7 @@ fw_core_handle_request(struct fw_card *c + } + EXPORT_SYMBOL(fw_core_handle_request); + +-void +-fw_core_handle_response(struct fw_card *card, struct fw_packet *p) ++void fw_core_handle_response(struct fw_card *card, struct fw_packet *p) + { + struct fw_transaction *t; + unsigned long flags; +@@ -793,12 +783,10 @@ static const struct fw_address_region to + { .start = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP, + .end = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP_END, }; + +-static void +-handle_topology_map(struct fw_card *card, struct fw_request *request, +- int tcode, int destination, int source, +- int generation, int speed, +- unsigned long long offset, +- void *payload, size_t length, void *callback_data) ++static void handle_topology_map(struct fw_card *card, struct fw_request *request, ++ int tcode, int destination, int source, int generation, ++ int speed, unsigned long long offset, ++ void *payload, size_t length, void *callback_data) + { + int i, start, end; + __be32 *map; +@@ -832,12 +820,10 @@ static const struct fw_address_region re + { .start = CSR_REGISTER_BASE, + .end = CSR_REGISTER_BASE | CSR_CONFIG_ROM, }; + +-static void +-handle_registers(struct fw_card *card, struct fw_request *request, +- int tcode, int destination, int source, +- int generation, int speed, +- unsigned long long offset, +- void *payload, size_t length, void *callback_data) ++static void handle_registers(struct fw_card *card, struct fw_request *request, ++ int tcode, int destination, int source, int generation, ++ int speed, unsigned long long offset, ++ void *payload, size_t length, void *callback_data) + { + int reg = offset & ~CSR_REGISTER_BASE; + unsigned long long bus_time; +@@ -939,11 +925,11 @@ static struct fw_descriptor model_id_des + + static int __init fw_core_init(void) + { +- int retval; ++ int ret; + +- retval = bus_register(&fw_bus_type); +- if (retval < 0) +- return retval; ++ ret = bus_register(&fw_bus_type); ++ if (ret < 0) ++ return ret; + + fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops); + if (fw_cdev_major < 0) { +@@ -951,19 +937,10 @@ static int __init fw_core_init(void) + return fw_cdev_major; + } + +- retval = fw_core_add_address_handler(&topology_map, +- &topology_map_region); +- BUG_ON(retval < 0); +- +- retval = fw_core_add_address_handler(®isters, +- ®isters_region); +- BUG_ON(retval < 0); +- +- /* Add the vendor textual descriptor. */ +- retval = fw_core_add_descriptor(&vendor_id_descriptor); +- BUG_ON(retval < 0); +- retval = fw_core_add_descriptor(&model_id_descriptor); +- BUG_ON(retval < 0); ++ fw_core_add_address_handler(&topology_map, &topology_map_region); ++ fw_core_add_address_handler(®isters, ®isters_region); ++ fw_core_add_descriptor(&vendor_id_descriptor); ++ fw_core_add_descriptor(&model_id_descriptor); + + return 0; + } +diff -Naurp linux-2.6-git/drivers/firewire/fw-transaction.h firewire-git/drivers/firewire/fw-transaction.h +--- linux-2.6-git/drivers/firewire/fw-transaction.h 2009-01-30 13:39:02.992772636 -0500 ++++ firewire-git/drivers/firewire/fw-transaction.h 2009-01-30 13:35:51.862647087 -0500 +@@ -82,14 +82,14 @@ + #define CSR_SPEED_MAP 0x2000 + #define CSR_SPEED_MAP_END 0x3000 + ++#define BANDWIDTH_AVAILABLE_INITIAL 4915 + #define BROADCAST_CHANNEL_INITIAL (1 << 31 | 31) + #define BROADCAST_CHANNEL_VALID (1 << 30) + + #define fw_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, ## args) + #define fw_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args) + +-static inline void +-fw_memcpy_from_be32(void *_dst, void *_src, size_t size) ++static inline void fw_memcpy_from_be32(void *_dst, void *_src, size_t size) + { + u32 *dst = _dst; + __be32 *src = _src; +@@ -99,8 +99,7 @@ fw_memcpy_from_be32(void *_dst, void *_s + dst[i] = be32_to_cpu(src[i]); + } + +-static inline void +-fw_memcpy_to_be32(void *_dst, void *_src, size_t size) ++static inline void fw_memcpy_to_be32(void *_dst, void *_src, size_t size) + { + fw_memcpy_from_be32(_dst, _src, size); + } +@@ -125,8 +124,7 @@ typedef void (*fw_packet_callback_t)(str + struct fw_card *card, int status); + + typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode, +- void *data, +- size_t length, ++ void *data, size_t length, + void *callback_data); + + /* +@@ -141,12 +139,6 @@ typedef void (*fw_address_callback_t)(st + void *data, size_t length, + void *callback_data); + +-typedef void (*fw_bus_reset_callback_t)(struct fw_card *handle, +- int node_id, int generation, +- u32 *self_ids, +- int self_id_count, +- void *callback_data); +- + struct fw_packet { + int speed; + int generation; +@@ -187,12 +179,6 @@ struct fw_transaction { + void *callback_data; + }; + +-static inline struct fw_packet * +-fw_packet(struct list_head *l) +-{ +- return list_entry(l, struct fw_packet, link); +-} +- + struct fw_address_handler { + u64 offset; + size_t length; +@@ -201,7 +187,6 @@ struct fw_address_handler { + struct list_head link; + }; + +- + struct fw_address_region { + u64 start; + u64 end; +@@ -315,10 +300,8 @@ struct fw_iso_packet { + struct fw_iso_context; + + typedef void (*fw_iso_callback_t)(struct fw_iso_context *context, +- u32 cycle, +- size_t header_length, +- void *header, +- void *data); ++ u32 cycle, size_t header_length, ++ void *header, void *data); + + /* + * An iso buffer is just a set of pages mapped for DMA in the +@@ -344,36 +327,25 @@ struct fw_iso_context { + void *callback_data; + }; + +-int +-fw_iso_buffer_init(struct fw_iso_buffer *buffer, +- struct fw_card *card, +- int page_count, +- enum dma_data_direction direction); +-int +-fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma); +-void +-fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card); +- +-struct fw_iso_context * +-fw_iso_context_create(struct fw_card *card, int type, +- int channel, int speed, size_t header_size, +- fw_iso_callback_t callback, void *callback_data); +- +-void +-fw_iso_context_destroy(struct fw_iso_context *ctx); +- +-int +-fw_iso_context_queue(struct fw_iso_context *ctx, +- struct fw_iso_packet *packet, +- struct fw_iso_buffer *buffer, +- unsigned long payload); +- +-int +-fw_iso_context_start(struct fw_iso_context *ctx, +- int cycle, int sync, int tags); ++int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, ++ int page_count, enum dma_data_direction direction); ++int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma); ++void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card); ++ ++struct fw_iso_context *fw_iso_context_create(struct fw_card *card, ++ int type, int channel, int speed, size_t header_size, ++ fw_iso_callback_t callback, void *callback_data); ++int fw_iso_context_queue(struct fw_iso_context *ctx, ++ struct fw_iso_packet *packet, ++ struct fw_iso_buffer *buffer, ++ unsigned long payload); ++int fw_iso_context_start(struct fw_iso_context *ctx, ++ int cycle, int sync, int tags); ++int fw_iso_context_stop(struct fw_iso_context *ctx); ++void fw_iso_context_destroy(struct fw_iso_context *ctx); + +-int +-fw_iso_context_stop(struct fw_iso_context *ctx); ++void fw_iso_resource_manage(struct fw_card *card, int generation, ++ u64 channels_mask, int *channel, int *bandwidth, bool allocate); + + struct fw_card_driver { + /* +@@ -415,7 +387,7 @@ struct fw_card_driver { + + struct fw_iso_context * + (*allocate_iso_context)(struct fw_card *card, +- int type, size_t header_size); ++ int type, int channel, size_t header_size); + void (*free_iso_context)(struct fw_iso_context *ctx); + + int (*start_iso)(struct fw_iso_context *ctx, +@@ -429,24 +401,18 @@ struct fw_card_driver { + int (*stop_iso)(struct fw_iso_context *ctx); + }; + +-int +-fw_core_initiate_bus_reset(struct fw_card *card, int short_reset); ++int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset); + +-void +-fw_send_request(struct fw_card *card, struct fw_transaction *t, ++void fw_send_request(struct fw_card *card, struct fw_transaction *t, + int tcode, int destination_id, int generation, int speed, + unsigned long long offset, void *data, size_t length, + fw_transaction_callback_t callback, void *callback_data); +- +-int fw_run_transaction(struct fw_card *card, int tcode, int destination_id, +- int generation, int speed, unsigned long long offset, +- void *data, size_t length); +- + int fw_cancel_transaction(struct fw_card *card, + struct fw_transaction *transaction); +- + void fw_flush_transactions(struct fw_card *card); +- ++int fw_run_transaction(struct fw_card *card, int tcode, int destination_id, ++ int generation, int speed, unsigned long long offset, ++ void *data, size_t length); + void fw_send_phy_config(struct fw_card *card, + int node_id, int generation, int gap_count); + +@@ -454,29 +420,18 @@ void fw_send_phy_config(struct fw_card * + * Called by the topology code to inform the device code of node + * activity; found, lost, or updated nodes. + */ +-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); + + /* API used by card level drivers */ + +-void +-fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver, +- struct device *device); +-int +-fw_card_add(struct fw_card *card, +- u32 max_receive, u32 link_speed, u64 guid); +- +-void +-fw_core_remove_card(struct fw_card *card); +- +-void +-fw_core_handle_bus_reset(struct fw_card *card, +- int node_id, int generation, +- int self_id_count, u32 *self_ids); +-void +-fw_core_handle_request(struct fw_card *card, struct fw_packet *request); +- +-void +-fw_core_handle_response(struct fw_card *card, struct fw_packet *packet); ++void fw_card_initialize(struct fw_card *card, ++ const struct fw_card_driver *driver, struct device *device); ++int fw_card_add(struct fw_card *card, ++ u32 max_receive, u32 link_speed, u64 guid); ++void fw_core_remove_card(struct fw_card *card); ++void fw_core_handle_bus_reset(struct fw_card *card, int node_id, ++ int generation, int self_id_count, u32 *self_ids); ++void fw_core_handle_request(struct fw_card *card, struct fw_packet *request); ++void fw_core_handle_response(struct fw_card *card, struct fw_packet *packet); + + #endif /* __fw_transaction_h */ +--- linux-2.6-git/include/linux/firewire-cdev.h 2008-11-04 11:19:21.000000000 -0500 ++++ firewire-git/include/linux/firewire-cdev.h 2009-01-30 13:35:54.327647015 -0500 +@@ -25,10 +25,12 @@ + #include <linux/types.h> + #include <linux/firewire-constants.h> + +-#define FW_CDEV_EVENT_BUS_RESET 0x00 +-#define FW_CDEV_EVENT_RESPONSE 0x01 +-#define FW_CDEV_EVENT_REQUEST 0x02 +-#define FW_CDEV_EVENT_ISO_INTERRUPT 0x03 ++#define FW_CDEV_EVENT_BUS_RESET 0x00 ++#define FW_CDEV_EVENT_RESPONSE 0x01 ++#define FW_CDEV_EVENT_REQUEST 0x02 ++#define FW_CDEV_EVENT_ISO_INTERRUPT 0x03 ++#define FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED 0x04 ++#define FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED 0x05 + + /** + * struct fw_cdev_event_common - Common part of all fw_cdev_event_ types +@@ -136,7 +138,24 @@ struct fw_cdev_event_request { + * This event is sent when the controller has completed an &fw_cdev_iso_packet + * with the %FW_CDEV_ISO_INTERRUPT bit set. In the receive case, the headers + * stripped of all packets up until and including the interrupt packet are +- * returned in the @header field. ++ * returned in the @header field. The amount of header data per packet is as ++ * specified at iso context creation by &fw_cdev_create_iso_context.header_size. ++ * ++ * In version 1 of this ABI, header data consisted of the 1394 isochronous ++ * packet header, followed by quadlets from the packet payload if ++ * &fw_cdev_create_iso_context.header_size > 4. ++ * ++ * In version 2 of this ABI, header data consist of the 1394 isochronous ++ * packet header, followed by a timestamp quadlet if ++ * &fw_cdev_create_iso_context.header_size > 4, followed by quadlets from the ++ * packet payload if &fw_cdev_create_iso_context.header_size > 8. ++ * ++ * Behaviour of ver. 1 of this ABI is no longer available since ABI ver. 2. ++ * ++ * Format of 1394 iso packet header: 16 bits len, 2 bits tag, 6 bits channel, ++ * 4 bits tcode, 4 bits sy, in big endian byte order. Format of timestamp: ++ * 16 bits invalid, 3 bits cycleSeconds, 13 bits cycleCount, in big endian byte ++ * order. + */ + struct fw_cdev_event_iso_interrupt { + __u64 closure; +@@ -147,12 +166,44 @@ struct fw_cdev_event_iso_interrupt { + }; + + /** ++ * struct fw_cdev_event_iso_resource - Iso resources were allocated or freed ++ * @closure: See &fw_cdev_event_common; ++ * set by %FW_CDEV_IOC_(DE)ALLOCATE_ISO_RESOURCE(_ONCE) ioctl ++ * @type: %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or ++ * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED ++ * @handle: Reference by which an allocated resource can be deallocated ++ * @channel: Isochronous channel which was (de)allocated, if any ++ * @bandwidth: Bandwidth allocation units which were (de)allocated, if any ++ * ++ * An %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED event is sent after an isochronous ++ * resource was allocated at the IRM. The client has to check @channel and ++ * @bandwidth for whether the allocation actually succeeded. ++ * ++ * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event is sent after an isochronous ++ * resource was deallocated at the IRM. It is also sent when automatic ++ * reallocation after a bus reset failed. ++ * ++ * @channel is <0 if no channel was (de)allocated or if reallocation failed. ++ * @bandwidth is 0 if no bandwidth was (de)allocated or if reallocation failed. ++ */ ++struct fw_cdev_event_iso_resource { ++ __u64 closure; ++ __u32 type; ++ __u32 handle; ++ __s32 channel; ++ __s32 bandwidth; ++}; ++ ++/** + * union fw_cdev_event - Convenience union of fw_cdev_event_ types + * @common: Valid for all types + * @bus_reset: Valid if @common.type == %FW_CDEV_EVENT_BUS_RESET + * @response: Valid if @common.type == %FW_CDEV_EVENT_RESPONSE + * @request: Valid if @common.type == %FW_CDEV_EVENT_REQUEST + * @iso_interrupt: Valid if @common.type == %FW_CDEV_EVENT_ISO_INTERRUPT ++ * @iso_resource: Valid if @common.type == ++ * %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or ++ * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED + * + * Convenience union for userspace use. Events could be read(2) into an + * appropriately aligned char buffer and then cast to this union for further +@@ -163,13 +214,15 @@ struct fw_cdev_event_iso_interrupt { + * not fit will be discarded so that the next read(2) will return a new event. + */ + union fw_cdev_event { +- struct fw_cdev_event_common common; +- struct fw_cdev_event_bus_reset bus_reset; +- struct fw_cdev_event_response response; +- struct fw_cdev_event_request request; +- struct fw_cdev_event_iso_interrupt iso_interrupt; ++ struct fw_cdev_event_common common; ++ struct fw_cdev_event_bus_reset bus_reset; ++ struct fw_cdev_event_response response; ++ struct fw_cdev_event_request request; ++ struct fw_cdev_event_iso_interrupt iso_interrupt; ++ struct fw_cdev_event_iso_resource iso_resource; + }; + ++/* available since kernel version 2.6.22 */ + #define FW_CDEV_IOC_GET_INFO _IOWR('#', 0x00, struct fw_cdev_get_info) + #define FW_CDEV_IOC_SEND_REQUEST _IOW('#', 0x01, struct fw_cdev_send_request) + #define FW_CDEV_IOC_ALLOCATE _IOWR('#', 0x02, struct fw_cdev_allocate) +@@ -178,18 +231,29 @@ union fw_cdev_event { + #define FW_CDEV_IOC_INITIATE_BUS_RESET _IOW('#', 0x05, struct fw_cdev_initiate_bus_reset) + #define FW_CDEV_IOC_ADD_DESCRIPTOR _IOWR('#', 0x06, struct fw_cdev_add_descriptor) + #define FW_CDEV_IOC_REMOVE_DESCRIPTOR _IOW('#', 0x07, struct fw_cdev_remove_descriptor) +- + #define FW_CDEV_IOC_CREATE_ISO_CONTEXT _IOWR('#', 0x08, struct fw_cdev_create_iso_context) + #define FW_CDEV_IOC_QUEUE_ISO _IOWR('#', 0x09, struct fw_cdev_queue_iso) + #define FW_CDEV_IOC_START_ISO _IOW('#', 0x0a, struct fw_cdev_start_iso) + #define FW_CDEV_IOC_STOP_ISO _IOW('#', 0x0b, struct fw_cdev_stop_iso) ++ ++/* available since kernel version 2.6.24 */ + #define FW_CDEV_IOC_GET_CYCLE_TIMER _IOR('#', 0x0c, struct fw_cdev_get_cycle_timer) + +-/* FW_CDEV_VERSION History +- * +- * 1 Feb 18, 2007: Initial version. ++/* available since kernel version 2.6.30 */ ++#define FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE _IOWR('#', 0x0d, struct fw_cdev_allocate_iso_resource) ++#define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE _IOW('#', 0x0e, struct fw_cdev_deallocate) ++#define FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x0f, struct fw_cdev_allocate_iso_resource) ++#define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x10, struct fw_cdev_allocate_iso_resource) ++#define FW_CDEV_IOC_GET_SPEED _IOR('#', 0x11, struct fw_cdev_get_speed) ++#define FW_CDEV_IOC_SEND_BROADCAST_REQUEST _IOW('#', 0x12, struct fw_cdev_send_request) ++ ++/* ++ * FW_CDEV_VERSION History ++ * 1 (2.6.22) - initial version ++ * 2 (2.6.30) - changed &fw_cdev_event_iso_interrupt.header if ++ * &fw_cdev_create_iso_context.header_size is 8 or more + */ +-#define FW_CDEV_VERSION 1 ++#define FW_CDEV_VERSION 2 + + /** + * struct fw_cdev_get_info - General purpose information ioctl +@@ -201,7 +265,7 @@ union fw_cdev_event { + * case, @rom_length is updated with the actual length of the + * configuration ROM. + * @rom: If non-zero, address of a buffer to be filled by a copy of the +- * local node's configuration ROM ++ * device's configuration ROM + * @bus_reset: If non-zero, address of a buffer to be filled by a + * &struct fw_cdev_event_bus_reset with the current state + * of the bus. This does not cause a bus reset to happen. +@@ -229,7 +293,7 @@ struct fw_cdev_get_info { + * Send a request to the device. This ioctl implements all outgoing requests. + * Both quadlet and block request specify the payload as a pointer to the data + * in the @data field. Once the transaction completes, the kernel writes an +- * &fw_cdev_event_request event back. The @closure field is passed back to ++ * &fw_cdev_event_response event back. The @closure field is passed back to + * user space in the response event. + */ + struct fw_cdev_send_request { +@@ -284,9 +348,9 @@ struct fw_cdev_allocate { + }; + + /** +- * struct fw_cdev_deallocate - Free an address range allocation +- * @handle: Handle to the address range, as returned by the kernel when the +- * range was allocated ++ * struct fw_cdev_deallocate - Free a CSR address range or isochronous resource ++ * @handle: Handle to the address range or iso resource, as returned by the ++ * kernel when the range or resource was allocated + */ + struct fw_cdev_deallocate { + __u32 handle; +@@ -370,6 +434,9 @@ struct fw_cdev_remove_descriptor { + * + * If a context was successfully created, the kernel writes back a handle to the + * context, which must be passed in for subsequent operations on that context. ++ * ++ * Note that the effect of a @header_size > 4 depends on ++ * &fw_cdev_get_info.version, as documented at &fw_cdev_event_iso_interrupt. + */ + struct fw_cdev_create_iso_context { + __u32 type; +@@ -473,10 +540,73 @@ struct fw_cdev_stop_iso { + * The %FW_CDEV_IOC_GET_CYCLE_TIMER ioctl reads the isochronous cycle timer + * and also the system clock. This allows to express the receive time of an + * isochronous packet as a system time with microsecond accuracy. ++ * ++ * @cycle_timer consists of 7 bits cycleSeconds, 13 bits cycleCount, and ++ * 12 bits cycleOffset, in host byte order. + */ + struct fw_cdev_get_cycle_timer { + __u64 local_time; + __u32 cycle_timer; + }; + ++/** ++ * struct fw_cdev_allocate_iso_resource - (De)allocate a channel or bandwidth ++ * @closure: Passed back to userspace in correponding iso resource events ++ * @channels: Isochronous channels of which one is to be (de)allocated ++ * @bandwidth: Isochronous bandwidth units to be (de)allocated ++ * @handle: Handle to the allocation, written by the kernel (only valid in ++ * case of %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctls) ++ * ++ * The %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctl initiates allocation of an ++ * isochronous channel and/or of isochronous bandwidth at the isochronous ++ * resource manager (IRM). Only one of the channels specified in @channels is ++ * allocated. An %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED is sent after ++ * communication with the IRM, indicating success or failure in the event data. ++ * The kernel will automatically reallocate the resources after bus resets. ++ * Should a reallocation fail, an %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event ++ * will be sent. The kernel will also automatically deallocate the resources ++ * when the file descriptor is closed. ++ * ++ * The %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE ioctl can be used to initiate ++ * deallocation of resources which were allocated as described above. ++ * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event concludes this operation. ++ * ++ * The %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE ioctl is a variant of allocation ++ * without automatic re- or deallocation. ++ * An %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED event concludes this operation, ++ * indicating success or failure in its data. ++ * ++ * The %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE ioctl works like ++ * %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE except that resources are freed ++ * instead of allocated. ++ * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event concludes this operation. ++ * ++ * To summarize, %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE allocates iso resources ++ * for the lifetime of the fd or handle. ++ * In contrast, %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE allocates iso resources ++ * for the duration of a bus generation. ++ * ++ * @channels is a host-endian bitfield with the least significant bit ++ * representing channel 0 and the most significant bit representing channel 63: ++ * 1ULL << c for each channel c that is a candidate for (de)allocation. ++ * ++ * @bandwidth is expressed in bandwidth allocation units, i.e. the time to send ++ * one quadlet of data (payload or header data) at speed S1600. ++ */ ++struct fw_cdev_allocate_iso_resource { ++ __u64 closure; ++ __u64 channels; ++ __u32 bandwidth; ++ __u32 handle; ++}; ++ ++/** ++ * struct fw_cdev_get_speed - Query maximum speed to or from this device ++ * @max_speed: Speed code; minimum of the device's link speed, the local node's ++ * link speed, and all PHY port speeds between the two links ++ */ ++struct fw_cdev_get_speed { ++ __u32 max_speed; ++}; ++ + #endif /* _LINUX_FIREWIRE_CDEV_H */ |