firewire: Acummulate received iso headers and send them back to user space.
authorKristian Høgsberg <krh@redhat.com>
Fri, 16 Feb 2007 22:34:44 +0000 (17:34 -0500)
committerStefan Richter <stefanr@s5r6.in-berlin.de>
Fri, 9 Mar 2007 21:03:00 +0000 (22:03 +0100)
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
drivers/firewire/fw-device-cdev.c
drivers/firewire/fw-device-cdev.h
drivers/firewire/fw-ohci.c
drivers/firewire/fw-transaction.h

index 1ce33d4f91a32bf0dac7d0c1f9effc412d84e62b..6545fb8214d823d6b212228af84b9fd095827920 100644 (file)
@@ -383,20 +383,24 @@ static int ioctl_send_response(struct client *client, void __user *arg)
 }
 
 static void
-iso_callback(struct fw_iso_context *context, int status, u32 cycle, void *data)
+iso_callback(struct fw_iso_context *context, u32 cycle,
+            size_t header_length, void *header, void *data)
 {
        struct client *client = data;
        struct iso_interrupt *interrupt;
 
-       interrupt = kzalloc(sizeof *interrupt, GFP_ATOMIC);
+       interrupt = kzalloc(sizeof *interrupt + header_length, GFP_ATOMIC);
        if (interrupt == NULL)
                return;
 
        interrupt->interrupt.type      = FW_CDEV_EVENT_ISO_INTERRUPT;
        interrupt->interrupt.closure   = 0;
        interrupt->interrupt.cycle     = cycle;
+       interrupt->interrupt.header_length = header_length;
+       memcpy(interrupt->interrupt.header, header, header_length);
        queue_event(client, &interrupt->event,
-                   &interrupt->interrupt, sizeof interrupt->interrupt, NULL, 0);
+                   &interrupt->interrupt,
+                   sizeof interrupt->interrupt + header_length, NULL, 0);
 }
 
 static int ioctl_create_iso_context(struct client *client, void __user *arg)
@@ -423,6 +427,7 @@ static int ioctl_queue_iso(struct client *client, void __user *arg)
 {
        struct fw_cdev_queue_iso request;
        struct fw_cdev_iso_packet __user *p, *end, *next;
+       struct fw_iso_context *ctx = client->iso_context;
        unsigned long payload, payload_end, header_length;
        int count;
        struct {
@@ -430,7 +435,7 @@ static int ioctl_queue_iso(struct client *client, void __user *arg)
                u8 header[256];
        } u;
 
-       if (client->iso_context == NULL)
+       if (ctx == NULL)
                return -EINVAL;
        if (copy_from_user(&request, arg, sizeof request))
                return -EFAULT;
@@ -461,13 +466,17 @@ static int ioctl_queue_iso(struct client *client, void __user *arg)
                if (__copy_from_user(&u.packet, p, sizeof *p))
                        return -EFAULT;
 
-               if (client->iso_context->type == FW_ISO_CONTEXT_TRANSMIT) {
+               if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) {
                        header_length = u.packet.header_length;
                } else {
                        /* We require that header_length is a multiple of
                         * the fixed header size, ctx->header_size */
-                       if (u.packet.header_length % client->iso_context->header_size != 0)
+                       if (ctx->header_size == 0) {
+                               if (u.packet.header_length > 0)
+                                       return -EINVAL;
+                       } else if (u.packet.header_length % ctx->header_size != 0) {
                                return -EINVAL;
+                       }
                        header_length = 0;
                }
 
@@ -484,8 +493,8 @@ static int ioctl_queue_iso(struct client *client, void __user *arg)
                if (payload + u.packet.payload_length > payload_end)
                        return -EINVAL;
 
-               if (fw_iso_context_queue(client->iso_context,
-                                        &u.packet, &client->buffer, payload))
+               if (fw_iso_context_queue(ctx, &u.packet,
+                                        &client->buffer, payload))
                        break;
 
                p = next;
index 257dc872f46d411355c96ffed56f17a861f6006d..e32b39dc7e744c8fc2ec6dba069cb1128d2ccbb8 100644 (file)
@@ -89,6 +89,8 @@ struct fw_cdev_event_iso_interrupt {
        __u32 type;
        __u32 cycle;
        __u64 closure;
+       __u32 header_length;    /* Length in bytes of following headers. */
+       __u32 header[0];
 };
 
 #define FW_CDEV_IOC_GET_CONFIG_ROM     _IOR('#', 0x00, struct fw_cdev_get_config_rom)
index d601ec7ff4d5b00f23aa2a705fdbc29a7a8e24fd..b5a154583e0d87ebf52988c91817f7fc916c2644 100644 (file)
@@ -143,6 +143,8 @@ struct at_context {
 struct iso_context {
        struct fw_iso_context base;
        struct context context;
+       void *header;
+       size_t header_length;
 };
 
 #define CONFIG_ROM_SIZE 1024
@@ -501,7 +503,7 @@ context_init(struct context *ctx, struct fw_ohci *ohci,
        return 0;
 }
 
- static void
+static void
 context_release(struct context *ctx)
 {
        struct fw_card *card = &ctx->ohci->card;
@@ -1273,16 +1275,23 @@ static int handle_ir_packet(struct context *context,
        struct iso_context *ctx =
                container_of(context, struct iso_context, context);
        struct db_descriptor *db = (struct db_descriptor *) d;
+       size_t header_length;
  
        if (db->first_res_count > 0 && db->second_res_count > 0)
                /* This descriptor isn't done yet, stop iteration. */
                return 0;
 
-       if (le16_to_cpu(db->control) & descriptor_irq_always)
-               /* FIXME: we should pass payload address here. */
-               ctx->base.callback(&ctx->base,
-                                  0, 0,
+       header_length = db->first_req_count - db->first_res_count;
+       if (ctx->header_length + header_length <= PAGE_SIZE)
+               memcpy(ctx->header + ctx->header_length, db + 1, header_length);
+       ctx->header_length += header_length;
+
+       if (le16_to_cpu(db->control) & descriptor_irq_always) {
+               ctx->base.callback(&ctx->base, 0,
+                                  ctx->header_length, ctx->header,
                                   ctx->base.callback_data);
+               ctx->header_length = 0;
+       }
 
        return 1;
 }
@@ -1301,9 +1310,8 @@ static int handle_it_packet(struct context *context,
                return 0;
 
        if (le16_to_cpu(last->control) & descriptor_irq_always)
-               ctx->base.callback(&ctx->base,
-                                  0, le16_to_cpu(last->res_count),
-                                  ctx->base.callback_data);
+               ctx->base.callback(&ctx->base, le16_to_cpu(last->res_count),
+                                  0, NULL, ctx->base.callback_data);
 
        return 1;
 }
@@ -1316,7 +1324,7 @@ ohci_allocate_iso_context(struct fw_card *card, int type)
        descriptor_callback_t callback;
        u32 *mask, regs;
        unsigned long flags;
-       int index, retval;
+       int index, retval = -ENOMEM;
 
        if (type == FW_ISO_CONTEXT_TRANSMIT) {
                mask = &ohci->it_context_mask;
@@ -1344,16 +1352,26 @@ ohci_allocate_iso_context(struct fw_card *card, int type)
  
        ctx = &list[index];
        memset(ctx, 0, sizeof *ctx);
+       ctx->header_length = 0;
+       ctx->header = (void *) __get_free_page(GFP_KERNEL);
+       if (ctx->header == NULL)
+               goto out;
+
        retval = context_init(&ctx->context, ohci, ISO_BUFFER_SIZE,
                              regs, callback);
-       if (retval < 0) {
-               spin_lock_irqsave(&ohci->lock, flags);
-               *mask |= 1 << index;
-               spin_unlock_irqrestore(&ohci->lock, flags);
-               return ERR_PTR(retval);
-       }
+       if (retval < 0)
+               goto out_with_header;
 
        return &ctx->base;
+
+ out_with_header:
+       free_page((unsigned long)ctx->header);
+ out:
+       spin_lock_irqsave(&ohci->lock, flags);
+       *mask |= 1 << index;
+       spin_unlock_irqrestore(&ohci->lock, flags);
+
+       return ERR_PTR(retval);
 }
 
 static int ohci_start_iso(struct fw_iso_context *base, s32 cycle)
@@ -1413,6 +1431,7 @@ static void ohci_free_iso_context(struct fw_iso_context *base)
 
        ohci_stop_iso(base);
        context_release(&ctx->context);
+       free_page((unsigned long)ctx->header);
 
        spin_lock_irqsave(&ohci->lock, flags);
 
index b2a0a030c0fda0cf317aa2be4faaf52631910bfd..7942e914b8f173110528a27fe1ca0356b4d64f45 100644 (file)
@@ -335,7 +335,10 @@ struct fw_iso_packet {
 struct fw_iso_context;
 
 typedef void (*fw_iso_callback_t) (struct fw_iso_context *context,
-                                  int status, u32 cycle, 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
  * specified direction.  Since the pages are to be used for DMA, they