diff options
author | Kristian Høgsberg <krh@redhat.com> | 2007-02-16 17:34:44 -0500 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2007-03-09 16:03:00 -0500 |
commit | 9b32d5f3074e9b1afaa39a360a59fd77a2214783 (patch) | |
tree | 441cde033cc8a55b7bc9715684c42554fa259cc4 | |
parent | 500be7251a4af1a87aa48285a23a741f74a97a89 (diff) |
firewire: Acummulate received iso headers and send them back to user space.
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
-rw-r--r-- | drivers/firewire/fw-device-cdev.c | 25 | ||||
-rw-r--r-- | drivers/firewire/fw-device-cdev.h | 2 | ||||
-rw-r--r-- | drivers/firewire/fw-ohci.c | 49 | ||||
-rw-r--r-- | drivers/firewire/fw-transaction.h | 5 |
4 files changed, 57 insertions, 24 deletions
diff --git a/drivers/firewire/fw-device-cdev.c b/drivers/firewire/fw-device-cdev.c index 1ce33d4f91a3..6545fb8214d8 100644 --- a/drivers/firewire/fw-device-cdev.c +++ b/drivers/firewire/fw-device-cdev.c | |||
@@ -383,20 +383,24 @@ static int ioctl_send_response(struct client *client, void __user *arg) | |||
383 | } | 383 | } |
384 | 384 | ||
385 | static void | 385 | static void |
386 | iso_callback(struct fw_iso_context *context, int status, u32 cycle, void *data) | 386 | iso_callback(struct fw_iso_context *context, u32 cycle, |
387 | size_t header_length, void *header, void *data) | ||
387 | { | 388 | { |
388 | struct client *client = data; | 389 | struct client *client = data; |
389 | struct iso_interrupt *interrupt; | 390 | struct iso_interrupt *interrupt; |
390 | 391 | ||
391 | interrupt = kzalloc(sizeof *interrupt, GFP_ATOMIC); | 392 | interrupt = kzalloc(sizeof *interrupt + header_length, GFP_ATOMIC); |
392 | if (interrupt == NULL) | 393 | if (interrupt == NULL) |
393 | return; | 394 | return; |
394 | 395 | ||
395 | interrupt->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; | 396 | interrupt->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; |
396 | interrupt->interrupt.closure = 0; | 397 | interrupt->interrupt.closure = 0; |
397 | interrupt->interrupt.cycle = cycle; | 398 | interrupt->interrupt.cycle = cycle; |
399 | interrupt->interrupt.header_length = header_length; | ||
400 | memcpy(interrupt->interrupt.header, header, header_length); | ||
398 | queue_event(client, &interrupt->event, | 401 | queue_event(client, &interrupt->event, |
399 | &interrupt->interrupt, sizeof interrupt->interrupt, NULL, 0); | 402 | &interrupt->interrupt, |
403 | sizeof interrupt->interrupt + header_length, NULL, 0); | ||
400 | } | 404 | } |
401 | 405 | ||
402 | static int ioctl_create_iso_context(struct client *client, void __user *arg) | 406 | 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 | { | 427 | { |
424 | struct fw_cdev_queue_iso request; | 428 | struct fw_cdev_queue_iso request; |
425 | struct fw_cdev_iso_packet __user *p, *end, *next; | 429 | struct fw_cdev_iso_packet __user *p, *end, *next; |
430 | struct fw_iso_context *ctx = client->iso_context; | ||
426 | unsigned long payload, payload_end, header_length; | 431 | unsigned long payload, payload_end, header_length; |
427 | int count; | 432 | int count; |
428 | struct { | 433 | struct { |
@@ -430,7 +435,7 @@ static int ioctl_queue_iso(struct client *client, void __user *arg) | |||
430 | u8 header[256]; | 435 | u8 header[256]; |
431 | } u; | 436 | } u; |
432 | 437 | ||
433 | if (client->iso_context == NULL) | 438 | if (ctx == NULL) |
434 | return -EINVAL; | 439 | return -EINVAL; |
435 | if (copy_from_user(&request, arg, sizeof request)) | 440 | if (copy_from_user(&request, arg, sizeof request)) |
436 | return -EFAULT; | 441 | return -EFAULT; |
@@ -461,13 +466,17 @@ static int ioctl_queue_iso(struct client *client, void __user *arg) | |||
461 | if (__copy_from_user(&u.packet, p, sizeof *p)) | 466 | if (__copy_from_user(&u.packet, p, sizeof *p)) |
462 | return -EFAULT; | 467 | return -EFAULT; |
463 | 468 | ||
464 | if (client->iso_context->type == FW_ISO_CONTEXT_TRANSMIT) { | 469 | if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) { |
465 | header_length = u.packet.header_length; | 470 | header_length = u.packet.header_length; |
466 | } else { | 471 | } else { |
467 | /* We require that header_length is a multiple of | 472 | /* We require that header_length is a multiple of |
468 | * the fixed header size, ctx->header_size */ | 473 | * the fixed header size, ctx->header_size */ |
469 | if (u.packet.header_length % client->iso_context->header_size != 0) | 474 | if (ctx->header_size == 0) { |
475 | if (u.packet.header_length > 0) | ||
476 | return -EINVAL; | ||
477 | } else if (u.packet.header_length % ctx->header_size != 0) { | ||
470 | return -EINVAL; | 478 | return -EINVAL; |
479 | } | ||
471 | header_length = 0; | 480 | header_length = 0; |
472 | } | 481 | } |
473 | 482 | ||
@@ -484,8 +493,8 @@ static int ioctl_queue_iso(struct client *client, void __user *arg) | |||
484 | if (payload + u.packet.payload_length > payload_end) | 493 | if (payload + u.packet.payload_length > payload_end) |
485 | return -EINVAL; | 494 | return -EINVAL; |
486 | 495 | ||
487 | if (fw_iso_context_queue(client->iso_context, | 496 | if (fw_iso_context_queue(ctx, &u.packet, |
488 | &u.packet, &client->buffer, payload)) | 497 | &client->buffer, payload)) |
489 | break; | 498 | break; |
490 | 499 | ||
491 | p = next; | 500 | p = next; |
diff --git a/drivers/firewire/fw-device-cdev.h b/drivers/firewire/fw-device-cdev.h index 257dc872f46d..e32b39dc7e74 100644 --- a/drivers/firewire/fw-device-cdev.h +++ b/drivers/firewire/fw-device-cdev.h | |||
@@ -89,6 +89,8 @@ struct fw_cdev_event_iso_interrupt { | |||
89 | __u32 type; | 89 | __u32 type; |
90 | __u32 cycle; | 90 | __u32 cycle; |
91 | __u64 closure; | 91 | __u64 closure; |
92 | __u32 header_length; /* Length in bytes of following headers. */ | ||
93 | __u32 header[0]; | ||
92 | }; | 94 | }; |
93 | 95 | ||
94 | #define FW_CDEV_IOC_GET_CONFIG_ROM _IOR('#', 0x00, struct fw_cdev_get_config_rom) | 96 | #define FW_CDEV_IOC_GET_CONFIG_ROM _IOR('#', 0x00, struct fw_cdev_get_config_rom) |
diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index d601ec7ff4d5..b5a154583e0d 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c | |||
@@ -143,6 +143,8 @@ struct at_context { | |||
143 | struct iso_context { | 143 | struct iso_context { |
144 | struct fw_iso_context base; | 144 | struct fw_iso_context base; |
145 | struct context context; | 145 | struct context context; |
146 | void *header; | ||
147 | size_t header_length; | ||
146 | }; | 148 | }; |
147 | 149 | ||
148 | #define CONFIG_ROM_SIZE 1024 | 150 | #define CONFIG_ROM_SIZE 1024 |
@@ -501,7 +503,7 @@ context_init(struct context *ctx, struct fw_ohci *ohci, | |||
501 | return 0; | 503 | return 0; |
502 | } | 504 | } |
503 | 505 | ||
504 | static void | 506 | static void |
505 | context_release(struct context *ctx) | 507 | context_release(struct context *ctx) |
506 | { | 508 | { |
507 | struct fw_card *card = &ctx->ohci->card; | 509 | struct fw_card *card = &ctx->ohci->card; |
@@ -1273,16 +1275,23 @@ static int handle_ir_packet(struct context *context, | |||
1273 | struct iso_context *ctx = | 1275 | struct iso_context *ctx = |
1274 | container_of(context, struct iso_context, context); | 1276 | container_of(context, struct iso_context, context); |
1275 | struct db_descriptor *db = (struct db_descriptor *) d; | 1277 | struct db_descriptor *db = (struct db_descriptor *) d; |
1278 | size_t header_length; | ||
1276 | 1279 | ||
1277 | if (db->first_res_count > 0 && db->second_res_count > 0) | 1280 | if (db->first_res_count > 0 && db->second_res_count > 0) |
1278 | /* This descriptor isn't done yet, stop iteration. */ | 1281 | /* This descriptor isn't done yet, stop iteration. */ |
1279 | return 0; | 1282 | return 0; |
1280 | 1283 | ||
1281 | if (le16_to_cpu(db->control) & descriptor_irq_always) | 1284 | header_length = db->first_req_count - db->first_res_count; |
1282 | /* FIXME: we should pass payload address here. */ | 1285 | if (ctx->header_length + header_length <= PAGE_SIZE) |
1283 | ctx->base.callback(&ctx->base, | 1286 | memcpy(ctx->header + ctx->header_length, db + 1, header_length); |
1284 | 0, 0, | 1287 | ctx->header_length += header_length; |
1288 | |||
1289 | if (le16_to_cpu(db->control) & descriptor_irq_always) { | ||
1290 | ctx->base.callback(&ctx->base, 0, | ||
1291 | ctx->header_length, ctx->header, | ||
1285 | ctx->base.callback_data); | 1292 | ctx->base.callback_data); |
1293 | ctx->header_length = 0; | ||
1294 | } | ||
1286 | 1295 | ||
1287 | return 1; | 1296 | return 1; |
1288 | } | 1297 | } |
@@ -1301,9 +1310,8 @@ static int handle_it_packet(struct context *context, | |||
1301 | return 0; | 1310 | return 0; |
1302 | 1311 | ||
1303 | if (le16_to_cpu(last->control) & descriptor_irq_always) | 1312 | if (le16_to_cpu(last->control) & descriptor_irq_always) |
1304 | ctx->base.callback(&ctx->base, | 1313 | ctx->base.callback(&ctx->base, le16_to_cpu(last->res_count), |
1305 | 0, le16_to_cpu(last->res_count), | 1314 | 0, NULL, ctx->base.callback_data); |
1306 | ctx->base.callback_data); | ||
1307 | 1315 | ||
1308 | return 1; | 1316 | return 1; |
1309 | } | 1317 | } |
@@ -1316,7 +1324,7 @@ ohci_allocate_iso_context(struct fw_card *card, int type) | |||
1316 | descriptor_callback_t callback; | 1324 | descriptor_callback_t callback; |
1317 | u32 *mask, regs; | 1325 | u32 *mask, regs; |
1318 | unsigned long flags; | 1326 | unsigned long flags; |
1319 | int index, retval; | 1327 | int index, retval = -ENOMEM; |
1320 | 1328 | ||
1321 | if (type == FW_ISO_CONTEXT_TRANSMIT) { | 1329 | if (type == FW_ISO_CONTEXT_TRANSMIT) { |
1322 | mask = &ohci->it_context_mask; | 1330 | mask = &ohci->it_context_mask; |
@@ -1344,16 +1352,26 @@ ohci_allocate_iso_context(struct fw_card *card, int type) | |||
1344 | 1352 | ||
1345 | ctx = &list[index]; | 1353 | ctx = &list[index]; |
1346 | memset(ctx, 0, sizeof *ctx); | 1354 | memset(ctx, 0, sizeof *ctx); |
1355 | ctx->header_length = 0; | ||
1356 | ctx->header = (void *) __get_free_page(GFP_KERNEL); | ||
1357 | if (ctx->header == NULL) | ||
1358 | goto out; | ||
1359 | |||
1347 | retval = context_init(&ctx->context, ohci, ISO_BUFFER_SIZE, | 1360 | retval = context_init(&ctx->context, ohci, ISO_BUFFER_SIZE, |
1348 | regs, callback); | 1361 | regs, callback); |
1349 | if (retval < 0) { | 1362 | if (retval < 0) |
1350 | spin_lock_irqsave(&ohci->lock, flags); | 1363 | goto out_with_header; |
1351 | *mask |= 1 << index; | ||
1352 | spin_unlock_irqrestore(&ohci->lock, flags); | ||
1353 | return ERR_PTR(retval); | ||
1354 | } | ||
1355 | 1364 | ||
1356 | return &ctx->base; | 1365 | return &ctx->base; |
1366 | |||
1367 | out_with_header: | ||
1368 | free_page((unsigned long)ctx->header); | ||
1369 | out: | ||
1370 | spin_lock_irqsave(&ohci->lock, flags); | ||
1371 | *mask |= 1 << index; | ||
1372 | spin_unlock_irqrestore(&ohci->lock, flags); | ||
1373 | |||
1374 | return ERR_PTR(retval); | ||
1357 | } | 1375 | } |
1358 | 1376 | ||
1359 | static int ohci_start_iso(struct fw_iso_context *base, s32 cycle) | 1377 | 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 | 1431 | ||
1414 | ohci_stop_iso(base); | 1432 | ohci_stop_iso(base); |
1415 | context_release(&ctx->context); | 1433 | context_release(&ctx->context); |
1434 | free_page((unsigned long)ctx->header); | ||
1416 | 1435 | ||
1417 | spin_lock_irqsave(&ohci->lock, flags); | 1436 | spin_lock_irqsave(&ohci->lock, flags); |
1418 | 1437 | ||
diff --git a/drivers/firewire/fw-transaction.h b/drivers/firewire/fw-transaction.h index b2a0a030c0fd..7942e914b8f1 100644 --- a/drivers/firewire/fw-transaction.h +++ b/drivers/firewire/fw-transaction.h | |||
@@ -335,7 +335,10 @@ struct fw_iso_packet { | |||
335 | struct fw_iso_context; | 335 | struct fw_iso_context; |
336 | 336 | ||
337 | typedef void (*fw_iso_callback_t) (struct fw_iso_context *context, | 337 | typedef void (*fw_iso_callback_t) (struct fw_iso_context *context, |
338 | int status, u32 cycle, void *data); | 338 | u32 cycle, |
339 | size_t header_length, | ||
340 | void *header, | ||
341 | void *data); | ||
339 | 342 | ||
340 | /* An iso buffer is just a set of pages mapped for DMA in the | 343 | /* An iso buffer is just a set of pages mapped for DMA in the |
341 | * specified direction. Since the pages are to be used for DMA, they | 344 | * specified direction. Since the pages are to be used for DMA, they |