diff options
Diffstat (limited to 'drivers/firewire/core-cdev.c')
-rw-r--r-- | drivers/firewire/core-cdev.c | 39 |
1 files changed, 33 insertions, 6 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 231e6ee5ba4..4eeaed57e21 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/preempt.h> | 35 | #include <linux/preempt.h> |
36 | #include <linux/sched.h> | 36 | #include <linux/sched.h> |
37 | #include <linux/spinlock.h> | 37 | #include <linux/spinlock.h> |
38 | #include <linux/string.h> | ||
38 | #include <linux/time.h> | 39 | #include <linux/time.h> |
39 | #include <linux/uaccess.h> | 40 | #include <linux/uaccess.h> |
40 | #include <linux/vmalloc.h> | 41 | #include <linux/vmalloc.h> |
@@ -595,14 +596,22 @@ static int ioctl_send_request(struct client *client, void *buffer) | |||
595 | client->device->max_speed); | 596 | client->device->max_speed); |
596 | } | 597 | } |
597 | 598 | ||
599 | static inline bool is_fcp_request(struct fw_request *request) | ||
600 | { | ||
601 | return request == NULL; | ||
602 | } | ||
603 | |||
598 | static void release_request(struct client *client, | 604 | static void release_request(struct client *client, |
599 | struct client_resource *resource) | 605 | struct client_resource *resource) |
600 | { | 606 | { |
601 | struct inbound_transaction_resource *r = container_of(resource, | 607 | struct inbound_transaction_resource *r = container_of(resource, |
602 | struct inbound_transaction_resource, resource); | 608 | struct inbound_transaction_resource, resource); |
603 | 609 | ||
604 | fw_send_response(client->device->card, r->request, | 610 | if (is_fcp_request(r->request)) |
605 | RCODE_CONFLICT_ERROR); | 611 | kfree(r->data); |
612 | else | ||
613 | fw_send_response(client->device->card, r->request, | ||
614 | RCODE_CONFLICT_ERROR); | ||
606 | kfree(r); | 615 | kfree(r); |
607 | } | 616 | } |
608 | 617 | ||
@@ -615,6 +624,7 @@ static void handle_request(struct fw_card *card, struct fw_request *request, | |||
615 | struct address_handler_resource *handler = callback_data; | 624 | struct address_handler_resource *handler = callback_data; |
616 | struct inbound_transaction_resource *r; | 625 | struct inbound_transaction_resource *r; |
617 | struct inbound_transaction_event *e; | 626 | struct inbound_transaction_event *e; |
627 | void *fcp_frame = NULL; | ||
618 | int ret; | 628 | int ret; |
619 | 629 | ||
620 | r = kmalloc(sizeof(*r), GFP_ATOMIC); | 630 | r = kmalloc(sizeof(*r), GFP_ATOMIC); |
@@ -626,6 +636,18 @@ static void handle_request(struct fw_card *card, struct fw_request *request, | |||
626 | r->data = payload; | 636 | r->data = payload; |
627 | r->length = length; | 637 | r->length = length; |
628 | 638 | ||
639 | if (is_fcp_request(request)) { | ||
640 | /* | ||
641 | * FIXME: Let core-transaction.c manage a | ||
642 | * single reference-counted copy? | ||
643 | */ | ||
644 | fcp_frame = kmemdup(payload, length, GFP_ATOMIC); | ||
645 | if (fcp_frame == NULL) | ||
646 | goto failed; | ||
647 | |||
648 | r->data = fcp_frame; | ||
649 | } | ||
650 | |||
629 | r->resource.release = release_request; | 651 | r->resource.release = release_request; |
630 | ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC); | 652 | ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC); |
631 | if (ret < 0) | 653 | if (ret < 0) |
@@ -639,13 +661,16 @@ static void handle_request(struct fw_card *card, struct fw_request *request, | |||
639 | e->request.closure = handler->closure; | 661 | e->request.closure = handler->closure; |
640 | 662 | ||
641 | queue_event(handler->client, &e->event, | 663 | queue_event(handler->client, &e->event, |
642 | &e->request, sizeof(e->request), payload, length); | 664 | &e->request, sizeof(e->request), r->data, length); |
643 | return; | 665 | return; |
644 | 666 | ||
645 | failed: | 667 | failed: |
646 | kfree(r); | 668 | kfree(r); |
647 | kfree(e); | 669 | kfree(e); |
648 | fw_send_response(card, request, RCODE_CONFLICT_ERROR); | 670 | kfree(fcp_frame); |
671 | |||
672 | if (!is_fcp_request(request)) | ||
673 | fw_send_response(card, request, RCODE_CONFLICT_ERROR); | ||
649 | } | 674 | } |
650 | 675 | ||
651 | static void release_address_handler(struct client *client, | 676 | static void release_address_handler(struct client *client, |
@@ -715,14 +740,16 @@ static int ioctl_send_response(struct client *client, void *buffer) | |||
715 | 740 | ||
716 | r = container_of(resource, struct inbound_transaction_resource, | 741 | r = container_of(resource, struct inbound_transaction_resource, |
717 | resource); | 742 | resource); |
743 | if (is_fcp_request(r->request)) | ||
744 | goto out; | ||
745 | |||
718 | if (request->length < r->length) | 746 | if (request->length < r->length) |
719 | r->length = request->length; | 747 | r->length = request->length; |
720 | |||
721 | if (copy_from_user(r->data, u64_to_uptr(request->data), r->length)) { | 748 | if (copy_from_user(r->data, u64_to_uptr(request->data), r->length)) { |
722 | ret = -EFAULT; | 749 | ret = -EFAULT; |
750 | kfree(r->request); | ||
723 | goto out; | 751 | goto out; |
724 | } | 752 | } |
725 | |||
726 | fw_send_response(client->device->card, r->request, request->rcode); | 753 | fw_send_response(client->device->card, r->request, request->rcode); |
727 | out: | 754 | out: |
728 | kfree(r); | 755 | kfree(r); |