diff options
Diffstat (limited to 'drivers/firewire/fw-device-cdev.c')
-rw-r--r-- | drivers/firewire/fw-device-cdev.c | 65 |
1 files changed, 46 insertions, 19 deletions
diff --git a/drivers/firewire/fw-device-cdev.c b/drivers/firewire/fw-device-cdev.c index 1b9e5f7c0129..6284375c6390 100644 --- a/drivers/firewire/fw-device-cdev.c +++ b/drivers/firewire/fw-device-cdev.c | |||
@@ -71,8 +71,10 @@ struct client { | |||
71 | struct list_head event_list; | 71 | struct list_head event_list; |
72 | struct semaphore event_list_sem; | 72 | struct semaphore event_list_sem; |
73 | wait_queue_head_t wait; | 73 | wait_queue_head_t wait; |
74 | unsigned long vm_start; | 74 | |
75 | struct fw_iso_context *iso_context; | 75 | struct fw_iso_context *iso_context; |
76 | struct fw_iso_buffer buffer; | ||
77 | unsigned long vm_start; | ||
76 | }; | 78 | }; |
77 | 79 | ||
78 | static inline void __user * | 80 | static inline void __user * |
@@ -406,7 +408,6 @@ static int ioctl_create_iso_context(struct client *client, void __user *arg) | |||
406 | 408 | ||
407 | client->iso_context = fw_iso_context_create(client->device->card, | 409 | client->iso_context = fw_iso_context_create(client->device->card, |
408 | FW_ISO_CONTEXT_TRANSMIT, | 410 | FW_ISO_CONTEXT_TRANSMIT, |
409 | request.buffer_size, | ||
410 | iso_callback, client); | 411 | iso_callback, client); |
411 | if (IS_ERR(client->iso_context)) | 412 | if (IS_ERR(client->iso_context)) |
412 | return PTR_ERR(client->iso_context); | 413 | return PTR_ERR(client->iso_context); |
@@ -418,8 +419,7 @@ static int ioctl_queue_iso(struct client *client, void __user *arg) | |||
418 | { | 419 | { |
419 | struct fw_cdev_queue_iso request; | 420 | struct fw_cdev_queue_iso request; |
420 | struct fw_cdev_iso_packet __user *p, *end, *next; | 421 | struct fw_cdev_iso_packet __user *p, *end, *next; |
421 | void *payload, *payload_end; | 422 | unsigned long payload, payload_end; |
422 | unsigned long index; | ||
423 | int count; | 423 | int count; |
424 | struct { | 424 | struct { |
425 | struct fw_iso_packet packet; | 425 | struct fw_iso_packet packet; |
@@ -434,20 +434,17 @@ static int ioctl_queue_iso(struct client *client, void __user *arg) | |||
434 | /* If the user passes a non-NULL data pointer, has mmap()'ed | 434 | /* If the user passes a non-NULL data pointer, has mmap()'ed |
435 | * the iso buffer, and the pointer points inside the buffer, | 435 | * the iso buffer, and the pointer points inside the buffer, |
436 | * we setup the payload pointers accordingly. Otherwise we | 436 | * we setup the payload pointers accordingly. Otherwise we |
437 | * set them both to NULL, which will still let packets with | 437 | * set them both to 0, which will still let packets with |
438 | * payload_length == 0 through. In other words, if no packets | 438 | * payload_length == 0 through. In other words, if no packets |
439 | * use the indirect payload, the iso buffer need not be mapped | 439 | * use the indirect payload, the iso buffer need not be mapped |
440 | * and the request.data pointer is ignored.*/ | 440 | * and the request.data pointer is ignored.*/ |
441 | 441 | ||
442 | index = (unsigned long)request.data - client->vm_start; | 442 | payload = (unsigned long)request.data - client->vm_start; |
443 | if (request.data != 0 && client->vm_start != 0 && | 443 | payload_end = payload + (client->buffer.page_count << PAGE_SHIFT); |
444 | index <= client->iso_context->buffer_size) { | 444 | if (request.data == 0 || client->buffer.pages == NULL || |
445 | payload = client->iso_context->buffer + index; | 445 | payload >= payload_end) { |
446 | payload_end = client->iso_context->buffer + | 446 | payload = 0; |
447 | client->iso_context->buffer_size; | 447 | payload_end = 0; |
448 | } else { | ||
449 | payload = NULL; | ||
450 | payload_end = NULL; | ||
451 | } | 448 | } |
452 | 449 | ||
453 | if (!access_ok(VERIFY_READ, request.packets, request.size)) | 450 | if (!access_ok(VERIFY_READ, request.packets, request.size)) |
@@ -473,7 +470,7 @@ static int ioctl_queue_iso(struct client *client, void __user *arg) | |||
473 | return -EINVAL; | 470 | return -EINVAL; |
474 | 471 | ||
475 | if (fw_iso_context_queue(client->iso_context, | 472 | if (fw_iso_context_queue(client->iso_context, |
476 | &u.packet, payload)) | 473 | &u.packet, &client->buffer, payload)) |
477 | break; | 474 | break; |
478 | 475 | ||
479 | p = next; | 476 | p = next; |
@@ -483,8 +480,7 @@ static int ioctl_queue_iso(struct client *client, void __user *arg) | |||
483 | 480 | ||
484 | request.size -= uptr_to_u64(p) - request.packets; | 481 | request.size -= uptr_to_u64(p) - request.packets; |
485 | request.packets = uptr_to_u64(p); | 482 | request.packets = uptr_to_u64(p); |
486 | request.data = | 483 | request.data = client->vm_start + payload; |
487 | client->vm_start + (payload - client->iso_context->buffer); | ||
488 | 484 | ||
489 | if (copy_to_user(arg, &request, sizeof request)) | 485 | if (copy_to_user(arg, &request, sizeof request)) |
490 | return -EFAULT; | 486 | return -EFAULT; |
@@ -549,13 +545,41 @@ fw_device_op_compat_ioctl(struct file *file, | |||
549 | static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) | 545 | static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) |
550 | { | 546 | { |
551 | struct client *client = file->private_data; | 547 | struct client *client = file->private_data; |
548 | enum dma_data_direction direction; | ||
549 | unsigned long size; | ||
550 | int page_count, retval; | ||
551 | |||
552 | /* FIXME: We could support multiple buffers, but we don't. */ | ||
553 | if (client->buffer.pages != NULL) | ||
554 | return -EBUSY; | ||
555 | |||
556 | if (!(vma->vm_flags & VM_SHARED)) | ||
557 | return -EINVAL; | ||
552 | 558 | ||
553 | if (client->iso_context->buffer == NULL) | 559 | if (vma->vm_start & ~PAGE_MASK) |
554 | return -EINVAL; | 560 | return -EINVAL; |
555 | 561 | ||
556 | client->vm_start = vma->vm_start; | 562 | client->vm_start = vma->vm_start; |
563 | size = vma->vm_end - vma->vm_start; | ||
564 | page_count = size >> PAGE_SHIFT; | ||
565 | if (size & ~PAGE_MASK) | ||
566 | return -EINVAL; | ||
567 | |||
568 | if (vma->vm_flags & VM_WRITE) | ||
569 | direction = DMA_TO_DEVICE; | ||
570 | else | ||
571 | direction = DMA_FROM_DEVICE; | ||
572 | |||
573 | retval = fw_iso_buffer_init(&client->buffer, client->device->card, | ||
574 | page_count, direction); | ||
575 | if (retval < 0) | ||
576 | return retval; | ||
557 | 577 | ||
558 | return remap_vmalloc_range(vma, client->iso_context->buffer, 0); | 578 | retval = fw_iso_buffer_map(&client->buffer, vma); |
579 | if (retval < 0) | ||
580 | fw_iso_buffer_destroy(&client->buffer, client->device->card); | ||
581 | |||
582 | return retval; | ||
559 | } | 583 | } |
560 | 584 | ||
561 | static int fw_device_op_release(struct inode *inode, struct file *file) | 585 | static int fw_device_op_release(struct inode *inode, struct file *file) |
@@ -564,6 +588,9 @@ static int fw_device_op_release(struct inode *inode, struct file *file) | |||
564 | struct address_handler *h, *next; | 588 | struct address_handler *h, *next; |
565 | struct request *r, *next_r; | 589 | struct request *r, *next_r; |
566 | 590 | ||
591 | if (client->buffer.pages) | ||
592 | fw_iso_buffer_destroy(&client->buffer, client->device->card); | ||
593 | |||
567 | if (client->iso_context) | 594 | if (client->iso_context) |
568 | fw_iso_context_destroy(client->iso_context); | 595 | fw_iso_context_destroy(client->iso_context); |
569 | 596 | ||