summaryrefslogtreecommitdiffstats
path: root/drivers/firewire/core-cdev.c
diff options
context:
space:
mode:
authorStefan Richter <stefanr@s5r6.in-berlin.de>2010-07-23 13:05:39 +0200
committerStefan Richter <stefanr@s5r6.in-berlin.de>2010-07-23 13:36:28 +0200
commit8e2b2b46ea4ca5ef790dddf78b360ed736a62d7c (patch)
tree35fb2be1225c5a4733d4be8c03bc5725f79b43c2 /drivers/firewire/core-cdev.c
parent0c9ae701ae1caf657326db22d61074b40a747c9d (diff)
downloadlinux-8e2b2b46ea4ca5ef790dddf78b360ed736a62d7c.tar.bz2
firewire: cdev: improve FW_CDEV_IOC_ALLOCATE
In both the ieee1394 stack and the firewire stack, the core treats kernelspace drivers better than userspace drivers when it comes to CSR address range allocation: The former may request a register to be placed automatically at a free spot anywhere inside a specified address range. The latter may only request a register at a fixed offset. Hence, userspace drivers which do not require a fixed offset potentially need to implement a retry loop with incremented offset in each retry until the kernel does not fail allocation with EBUSY. This awkward procedure is not fundamentally necessary as the core already provides a superior allocation API to kernelspace drivers. Therefore change the ioctl() ABI by addition of a region_end member in the existing struct fw_cdev_allocate. Userspace and kernelspace APIs work the same way now. There is a small cost to pay by clients though: If client source code is required to compile with older kernel headers too, then any use of the new member fw_cdev_allocate.region_end needs to be enclosed by #ifdef/#endif directives. However, any client program that seriously wants to use address range allocations will require a kernel of cdev ABI version >= 4 at runtime and a linux/firewire-cdev.h header of >= 4 anyway. This is because v4 brings FW_CDEV_EVENT_REQUEST2. The only client program in which build-time compatibility with struct fw_cdev_allocate as found in older kernel headers makes sense is libraw1394. (libraw1394 uses the older broken FW_CDEV_EVENT_REQUEST to implement a makeshift, incorrect transaction responder that does at least work somewhat in many simple scenarios, relying on guesswork by libraw1394 and by libraw1394 based applications. Plus, address range allocation and transaction responder is only one of many features that libraw1394 needs to provide, and these other features need to work with kernel and kernel-headers as old as possible. Any new linux/firewire-cdev.h based client that implements a transaction responder should never attempt to do it like libraw1394; instead it should make a header and kernel of v4 or later a hard requirement.) While we are at it, update the struct fw_cdev_allocate documentation to better reflect the recent fw_cdev_event_request2 ABI addition. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire/core-cdev.c')
-rw-r--r--drivers/firewire/core-cdev.c12
1 files changed, 9 insertions, 3 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index 31863cf8b6c4..f40098dec14b 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -50,8 +50,9 @@
/*
* ABI version history is documented in linux/firewire-cdev.h.
*/
-#define FW_CDEV_KERNEL_VERSION 4
-#define FW_CDEV_VERSION_EVENT_REQUEST2 4
+#define FW_CDEV_KERNEL_VERSION 4
+#define FW_CDEV_VERSION_EVENT_REQUEST2 4
+#define FW_CDEV_VERSION_ALLOCATE_REGION_END 4
struct client {
u32 version;
@@ -773,7 +774,11 @@ static int ioctl_allocate(struct client *client, union ioctl_arg *arg)
return -ENOMEM;
region.start = a->offset;
- region.end = a->offset + a->length;
+ if (client->version < FW_CDEV_VERSION_ALLOCATE_REGION_END)
+ region.end = a->offset + a->length;
+ else
+ region.end = a->region_end;
+
r->handler.length = a->length;
r->handler.address_callback = handle_request;
r->handler.callback_data = r;
@@ -785,6 +790,7 @@ static int ioctl_allocate(struct client *client, union ioctl_arg *arg)
kfree(r);
return ret;
}
+ a->offset = r->handler.offset;
r->resource.release = release_address_handler;
ret = add_client_resource(client, &r->resource, GFP_KERNEL);