Commit 9b32d5f3 authored by Kristian Høgsberg's avatar Kristian Høgsberg Committed by Stefan Richter

firewire: Acummulate received iso headers and send them back to user space.

Signed-off-by: default avatarKristian Høgsberg <krh@redhat.com>
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent 500be725
...@@ -383,20 +383,24 @@ static int ioctl_send_response(struct client *client, void __user *arg) ...@@ -383,20 +383,24 @@ static int ioctl_send_response(struct client *client, void __user *arg)
} }
static void 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 client *client = data;
struct iso_interrupt *interrupt; struct iso_interrupt *interrupt;
interrupt = kzalloc(sizeof *interrupt, GFP_ATOMIC); interrupt = kzalloc(sizeof *interrupt + header_length, GFP_ATOMIC);
if (interrupt == NULL) if (interrupt == NULL)
return; return;
interrupt->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; interrupt->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT;
interrupt->interrupt.closure = 0; interrupt->interrupt.closure = 0;
interrupt->interrupt.cycle = cycle; interrupt->interrupt.cycle = cycle;
interrupt->interrupt.header_length = header_length;
memcpy(interrupt->interrupt.header, header, header_length);
queue_event(client, &interrupt->event, 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) 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) ...@@ -423,6 +427,7 @@ static int ioctl_queue_iso(struct client *client, void __user *arg)
{ {
struct fw_cdev_queue_iso request; struct fw_cdev_queue_iso request;
struct fw_cdev_iso_packet __user *p, *end, *next; struct fw_cdev_iso_packet __user *p, *end, *next;
struct fw_iso_context *ctx = client->iso_context;
unsigned long payload, payload_end, header_length; unsigned long payload, payload_end, header_length;
int count; int count;
struct { struct {
...@@ -430,7 +435,7 @@ static int ioctl_queue_iso(struct client *client, void __user *arg) ...@@ -430,7 +435,7 @@ static int ioctl_queue_iso(struct client *client, void __user *arg)
u8 header[256]; u8 header[256];
} u; } u;
if (client->iso_context == NULL) if (ctx == NULL)
return -EINVAL; return -EINVAL;
if (copy_from_user(&request, arg, sizeof request)) if (copy_from_user(&request, arg, sizeof request))
return -EFAULT; return -EFAULT;
...@@ -461,13 +466,17 @@ static int ioctl_queue_iso(struct client *client, void __user *arg) ...@@ -461,13 +466,17 @@ static int ioctl_queue_iso(struct client *client, void __user *arg)
if (__copy_from_user(&u.packet, p, sizeof *p)) if (__copy_from_user(&u.packet, p, sizeof *p))
return -EFAULT; return -EFAULT;
if (client->iso_context->type == FW_ISO_CONTEXT_TRANSMIT) { if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) {
header_length = u.packet.header_length; header_length = u.packet.header_length;
} else { } else {
/* We require that header_length is a multiple of /* We require that header_length is a multiple of
* the fixed header size, ctx->header_size */ * 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; return -EINVAL;
}
header_length = 0; header_length = 0;
} }
...@@ -484,8 +493,8 @@ static int ioctl_queue_iso(struct client *client, void __user *arg) ...@@ -484,8 +493,8 @@ static int ioctl_queue_iso(struct client *client, void __user *arg)
if (payload + u.packet.payload_length > payload_end) if (payload + u.packet.payload_length > payload_end)
return -EINVAL; return -EINVAL;
if (fw_iso_context_queue(client->iso_context, if (fw_iso_context_queue(ctx, &u.packet,
&u.packet, &client->buffer, payload)) &client->buffer, payload))
break; break;
p = next; p = next;
......
...@@ -89,6 +89,8 @@ struct fw_cdev_event_iso_interrupt { ...@@ -89,6 +89,8 @@ struct fw_cdev_event_iso_interrupt {
__u32 type; __u32 type;
__u32 cycle; __u32 cycle;
__u64 closure; __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) #define FW_CDEV_IOC_GET_CONFIG_ROM _IOR('#', 0x00, struct fw_cdev_get_config_rom)
......
...@@ -143,6 +143,8 @@ struct at_context { ...@@ -143,6 +143,8 @@ struct at_context {
struct iso_context { struct iso_context {
struct fw_iso_context base; struct fw_iso_context base;
struct context context; struct context context;
void *header;
size_t header_length;
}; };
#define CONFIG_ROM_SIZE 1024 #define CONFIG_ROM_SIZE 1024
...@@ -501,7 +503,7 @@ context_init(struct context *ctx, struct fw_ohci *ohci, ...@@ -501,7 +503,7 @@ context_init(struct context *ctx, struct fw_ohci *ohci,
return 0; return 0;
} }
static void static void
context_release(struct context *ctx) context_release(struct context *ctx)
{ {
struct fw_card *card = &ctx->ohci->card; struct fw_card *card = &ctx->ohci->card;
...@@ -1273,16 +1275,23 @@ static int handle_ir_packet(struct context *context, ...@@ -1273,16 +1275,23 @@ static int handle_ir_packet(struct context *context,
struct iso_context *ctx = struct iso_context *ctx =
container_of(context, struct iso_context, context); container_of(context, struct iso_context, context);
struct db_descriptor *db = (struct db_descriptor *) d; struct db_descriptor *db = (struct db_descriptor *) d;
size_t header_length;
if (db->first_res_count > 0 && db->second_res_count > 0) if (db->first_res_count > 0 && db->second_res_count > 0)
/* This descriptor isn't done yet, stop iteration. */ /* This descriptor isn't done yet, stop iteration. */
return 0; return 0;
if (le16_to_cpu(db->control) & descriptor_irq_always) header_length = db->first_req_count - db->first_res_count;
/* FIXME: we should pass payload address here. */ if (ctx->header_length + header_length <= PAGE_SIZE)
ctx->base.callback(&ctx->base, memcpy(ctx->header + ctx->header_length, db + 1, header_length);
0, 0, 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->base.callback_data);
ctx->header_length = 0;
}
return 1; return 1;
} }
...@@ -1301,9 +1310,8 @@ static int handle_it_packet(struct context *context, ...@@ -1301,9 +1310,8 @@ static int handle_it_packet(struct context *context,
return 0; return 0;
if (le16_to_cpu(last->control) & descriptor_irq_always) if (le16_to_cpu(last->control) & descriptor_irq_always)
ctx->base.callback(&ctx->base, ctx->base.callback(&ctx->base, le16_to_cpu(last->res_count),
0, le16_to_cpu(last->res_count), 0, NULL, ctx->base.callback_data);
ctx->base.callback_data);
return 1; return 1;
} }
...@@ -1316,7 +1324,7 @@ ohci_allocate_iso_context(struct fw_card *card, int type) ...@@ -1316,7 +1324,7 @@ ohci_allocate_iso_context(struct fw_card *card, int type)
descriptor_callback_t callback; descriptor_callback_t callback;
u32 *mask, regs; u32 *mask, regs;
unsigned long flags; unsigned long flags;
int index, retval; int index, retval = -ENOMEM;
if (type == FW_ISO_CONTEXT_TRANSMIT) { if (type == FW_ISO_CONTEXT_TRANSMIT) {
mask = &ohci->it_context_mask; mask = &ohci->it_context_mask;
...@@ -1344,16 +1352,26 @@ ohci_allocate_iso_context(struct fw_card *card, int type) ...@@ -1344,16 +1352,26 @@ ohci_allocate_iso_context(struct fw_card *card, int type)
ctx = &list[index]; ctx = &list[index];
memset(ctx, 0, sizeof *ctx); 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, retval = context_init(&ctx->context, ohci, ISO_BUFFER_SIZE,
regs, callback); regs, callback);
if (retval < 0) { if (retval < 0)
spin_lock_irqsave(&ohci->lock, flags); goto out_with_header;
*mask |= 1 << index;
spin_unlock_irqrestore(&ohci->lock, flags);
return ERR_PTR(retval);
}
return &ctx->base; 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) 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) ...@@ -1413,6 +1431,7 @@ static void ohci_free_iso_context(struct fw_iso_context *base)
ohci_stop_iso(base); ohci_stop_iso(base);
context_release(&ctx->context); context_release(&ctx->context);
free_page((unsigned long)ctx->header);
spin_lock_irqsave(&ohci->lock, flags); spin_lock_irqsave(&ohci->lock, flags);
......
...@@ -335,7 +335,10 @@ struct fw_iso_packet { ...@@ -335,7 +335,10 @@ struct fw_iso_packet {
struct fw_iso_context; struct fw_iso_context;
typedef void (*fw_iso_callback_t) (struct fw_iso_context *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 /* 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 * specified direction. Since the pages are to be used for DMA, they
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment