diff options
-rw-r--r-- | drivers/firewire/core-cdev.c | 26 | ||||
-rw-r--r-- | drivers/firewire/core-transaction.c | 118 | ||||
-rw-r--r-- | drivers/media/dvb/firewire/firedtv-fw.c | 12 | ||||
-rw-r--r-- | include/linux/firewire-cdev.h | 3 | ||||
-rw-r--r-- | include/linux/firewire.h | 4 |
5 files changed, 119 insertions, 44 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 231e6ee5ba43..2cb22d160f6e 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c | |||
@@ -601,8 +601,9 @@ static void release_request(struct client *client, | |||
601 | struct inbound_transaction_resource *r = container_of(resource, | 601 | struct inbound_transaction_resource *r = container_of(resource, |
602 | struct inbound_transaction_resource, resource); | 602 | struct inbound_transaction_resource, resource); |
603 | 603 | ||
604 | fw_send_response(client->device->card, r->request, | 604 | if (r->request) |
605 | RCODE_CONFLICT_ERROR); | 605 | fw_send_response(client->device->card, r->request, |
606 | RCODE_CONFLICT_ERROR); | ||
606 | kfree(r); | 607 | kfree(r); |
607 | } | 608 | } |
608 | 609 | ||
@@ -645,7 +646,8 @@ static void handle_request(struct fw_card *card, struct fw_request *request, | |||
645 | failed: | 646 | failed: |
646 | kfree(r); | 647 | kfree(r); |
647 | kfree(e); | 648 | kfree(e); |
648 | fw_send_response(card, request, RCODE_CONFLICT_ERROR); | 649 | if (request) |
650 | fw_send_response(card, request, RCODE_CONFLICT_ERROR); | ||
649 | } | 651 | } |
650 | 652 | ||
651 | static void release_address_handler(struct client *client, | 653 | static void release_address_handler(struct client *client, |
@@ -715,15 +717,17 @@ static int ioctl_send_response(struct client *client, void *buffer) | |||
715 | 717 | ||
716 | r = container_of(resource, struct inbound_transaction_resource, | 718 | r = container_of(resource, struct inbound_transaction_resource, |
717 | resource); | 719 | resource); |
718 | if (request->length < r->length) | 720 | if (r->request) { |
719 | r->length = request->length; | 721 | if (request->length < r->length) |
720 | 722 | r->length = request->length; | |
721 | if (copy_from_user(r->data, u64_to_uptr(request->data), r->length)) { | 723 | if (copy_from_user(r->data, u64_to_uptr(request->data), |
722 | ret = -EFAULT; | 724 | r->length)) { |
723 | goto out; | 725 | ret = -EFAULT; |
726 | goto out; | ||
727 | } | ||
728 | fw_send_response(client->device->card, r->request, | ||
729 | request->rcode); | ||
724 | } | 730 | } |
725 | |||
726 | fw_send_response(client->device->card, r->request, request->rcode); | ||
727 | out: | 731 | out: |
728 | kfree(r); | 732 | kfree(r); |
729 | 733 | ||
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 842739df23e2..495849eb13cc 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c | |||
@@ -432,14 +432,20 @@ static struct fw_address_handler *lookup_overlapping_address_handler( | |||
432 | return NULL; | 432 | return NULL; |
433 | } | 433 | } |
434 | 434 | ||
435 | static bool is_enclosing_handler(struct fw_address_handler *handler, | ||
436 | unsigned long long offset, size_t length) | ||
437 | { | ||
438 | return handler->offset <= offset && | ||
439 | offset + length <= handler->offset + handler->length; | ||
440 | } | ||
441 | |||
435 | static struct fw_address_handler *lookup_enclosing_address_handler( | 442 | static struct fw_address_handler *lookup_enclosing_address_handler( |
436 | struct list_head *list, unsigned long long offset, size_t length) | 443 | struct list_head *list, unsigned long long offset, size_t length) |
437 | { | 444 | { |
438 | struct fw_address_handler *handler; | 445 | struct fw_address_handler *handler; |
439 | 446 | ||
440 | list_for_each_entry(handler, list, link) { | 447 | list_for_each_entry(handler, list, link) { |
441 | if (handler->offset <= offset && | 448 | if (is_enclosing_handler(handler, offset, length)) |
442 | offset + length <= handler->offset + handler->length) | ||
443 | return handler; | 449 | return handler; |
444 | } | 450 | } |
445 | 451 | ||
@@ -465,6 +471,12 @@ const struct fw_address_region fw_unit_space_region = | |||
465 | { .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, }; | 471 | { .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, }; |
466 | #endif /* 0 */ | 472 | #endif /* 0 */ |
467 | 473 | ||
474 | static bool is_in_fcp_region(u64 offset, size_t length) | ||
475 | { | ||
476 | return offset >= (CSR_REGISTER_BASE | CSR_FCP_COMMAND) && | ||
477 | offset + length <= (CSR_REGISTER_BASE | CSR_FCP_END); | ||
478 | } | ||
479 | |||
468 | /** | 480 | /** |
469 | * fw_core_add_address_handler - register for incoming requests | 481 | * fw_core_add_address_handler - register for incoming requests |
470 | * @handler: callback | 482 | * @handler: callback |
@@ -477,8 +489,11 @@ const struct fw_address_region fw_unit_space_region = | |||
477 | * give the details of the particular request. | 489 | * give the details of the particular request. |
478 | * | 490 | * |
479 | * Return value: 0 on success, non-zero otherwise. | 491 | * Return value: 0 on success, non-zero otherwise. |
492 | * | ||
480 | * The start offset of the handler's address region is determined by | 493 | * The start offset of the handler's address region is determined by |
481 | * fw_core_add_address_handler() and is returned in handler->offset. | 494 | * fw_core_add_address_handler() and is returned in handler->offset. |
495 | * | ||
496 | * Address allocations are exclusive, except for the FCP registers. | ||
482 | */ | 497 | */ |
483 | int fw_core_add_address_handler(struct fw_address_handler *handler, | 498 | int fw_core_add_address_handler(struct fw_address_handler *handler, |
484 | const struct fw_address_region *region) | 499 | const struct fw_address_region *region) |
@@ -498,10 +513,12 @@ int fw_core_add_address_handler(struct fw_address_handler *handler, | |||
498 | 513 | ||
499 | handler->offset = region->start; | 514 | handler->offset = region->start; |
500 | while (handler->offset + handler->length <= region->end) { | 515 | while (handler->offset + handler->length <= region->end) { |
501 | other = | 516 | if (is_in_fcp_region(handler->offset, handler->length)) |
502 | lookup_overlapping_address_handler(&address_handler_list, | 517 | other = NULL; |
503 | handler->offset, | 518 | else |
504 | handler->length); | 519 | other = lookup_overlapping_address_handler |
520 | (&address_handler_list, | ||
521 | handler->offset, handler->length); | ||
505 | if (other != NULL) { | 522 | if (other != NULL) { |
506 | handler->offset += other->length; | 523 | handler->offset += other->length; |
507 | } else { | 524 | } else { |
@@ -668,6 +685,9 @@ static struct fw_request *allocate_request(struct fw_packet *p) | |||
668 | void fw_send_response(struct fw_card *card, | 685 | void fw_send_response(struct fw_card *card, |
669 | struct fw_request *request, int rcode) | 686 | struct fw_request *request, int rcode) |
670 | { | 687 | { |
688 | if (WARN_ONCE(!request, "invalid for FCP address handlers")) | ||
689 | return; | ||
690 | |||
671 | /* unified transaction or broadcast transaction: don't respond */ | 691 | /* unified transaction or broadcast transaction: don't respond */ |
672 | if (request->ack != ACK_PENDING || | 692 | if (request->ack != ACK_PENDING || |
673 | HEADER_DESTINATION_IS_BROADCAST(request->request_header[0])) { | 693 | HEADER_DESTINATION_IS_BROADCAST(request->request_header[0])) { |
@@ -686,26 +706,15 @@ void fw_send_response(struct fw_card *card, | |||
686 | } | 706 | } |
687 | EXPORT_SYMBOL(fw_send_response); | 707 | EXPORT_SYMBOL(fw_send_response); |
688 | 708 | ||
689 | void fw_core_handle_request(struct fw_card *card, struct fw_packet *p) | 709 | static void handle_exclusive_region_request(struct fw_card *card, |
710 | struct fw_packet *p, | ||
711 | struct fw_request *request, | ||
712 | unsigned long long offset) | ||
690 | { | 713 | { |
691 | struct fw_address_handler *handler; | 714 | struct fw_address_handler *handler; |
692 | struct fw_request *request; | ||
693 | unsigned long long offset; | ||
694 | unsigned long flags; | 715 | unsigned long flags; |
695 | int tcode, destination, source; | 716 | int tcode, destination, source; |
696 | 717 | ||
697 | if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE) | ||
698 | return; | ||
699 | |||
700 | request = allocate_request(p); | ||
701 | if (request == NULL) { | ||
702 | /* FIXME: send statically allocated busy packet. */ | ||
703 | return; | ||
704 | } | ||
705 | |||
706 | offset = | ||
707 | ((unsigned long long) | ||
708 | HEADER_GET_OFFSET_HIGH(p->header[1]) << 32) | p->header[2]; | ||
709 | tcode = HEADER_GET_TCODE(p->header[0]); | 718 | tcode = HEADER_GET_TCODE(p->header[0]); |
710 | destination = HEADER_GET_DESTINATION(p->header[0]); | 719 | destination = HEADER_GET_DESTINATION(p->header[0]); |
711 | source = HEADER_GET_SOURCE(p->header[1]); | 720 | source = HEADER_GET_SOURCE(p->header[1]); |
@@ -732,6 +741,73 @@ void fw_core_handle_request(struct fw_card *card, struct fw_packet *p) | |||
732 | request->data, request->length, | 741 | request->data, request->length, |
733 | handler->callback_data); | 742 | handler->callback_data); |
734 | } | 743 | } |
744 | |||
745 | static void handle_fcp_region_request(struct fw_card *card, | ||
746 | struct fw_packet *p, | ||
747 | struct fw_request *request, | ||
748 | unsigned long long offset) | ||
749 | { | ||
750 | struct fw_address_handler *handler; | ||
751 | unsigned long flags; | ||
752 | int tcode, destination, source; | ||
753 | |||
754 | if ((offset != (CSR_REGISTER_BASE | CSR_FCP_COMMAND) && | ||
755 | offset != (CSR_REGISTER_BASE | CSR_FCP_RESPONSE)) || | ||
756 | request->length > 0x200) { | ||
757 | fw_send_response(card, request, RCODE_ADDRESS_ERROR); | ||
758 | |||
759 | return; | ||
760 | } | ||
761 | |||
762 | tcode = HEADER_GET_TCODE(p->header[0]); | ||
763 | destination = HEADER_GET_DESTINATION(p->header[0]); | ||
764 | source = HEADER_GET_SOURCE(p->header[1]); | ||
765 | |||
766 | if (tcode != TCODE_WRITE_QUADLET_REQUEST && | ||
767 | tcode != TCODE_WRITE_BLOCK_REQUEST) { | ||
768 | fw_send_response(card, request, RCODE_TYPE_ERROR); | ||
769 | |||
770 | return; | ||
771 | } | ||
772 | |||
773 | spin_lock_irqsave(&address_handler_lock, flags); | ||
774 | list_for_each_entry(handler, &address_handler_list, link) { | ||
775 | if (is_enclosing_handler(handler, offset, request->length)) | ||
776 | handler->address_callback(card, NULL, tcode, | ||
777 | destination, source, | ||
778 | p->generation, p->speed, | ||
779 | offset, request->data, | ||
780 | request->length, | ||
781 | handler->callback_data); | ||
782 | } | ||
783 | spin_unlock_irqrestore(&address_handler_lock, flags); | ||
784 | |||
785 | fw_send_response(card, request, RCODE_COMPLETE); | ||
786 | } | ||
787 | |||
788 | void fw_core_handle_request(struct fw_card *card, struct fw_packet *p) | ||
789 | { | ||
790 | struct fw_request *request; | ||
791 | unsigned long long offset; | ||
792 | |||
793 | if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE) | ||
794 | return; | ||
795 | |||
796 | request = allocate_request(p); | ||
797 | if (request == NULL) { | ||
798 | /* FIXME: send statically allocated busy packet. */ | ||
799 | return; | ||
800 | } | ||
801 | |||
802 | offset = ((u64)HEADER_GET_OFFSET_HIGH(p->header[1]) << 32) | | ||
803 | p->header[2]; | ||
804 | |||
805 | if (!is_in_fcp_region(offset, request->length)) | ||
806 | handle_exclusive_region_request(card, p, request, offset); | ||
807 | else | ||
808 | handle_fcp_region_request(card, p, request, offset); | ||
809 | |||
810 | } | ||
735 | EXPORT_SYMBOL(fw_core_handle_request); | 811 | EXPORT_SYMBOL(fw_core_handle_request); |
736 | 812 | ||
737 | void fw_core_handle_response(struct fw_card *card, struct fw_packet *p) | 813 | void fw_core_handle_response(struct fw_card *card, struct fw_packet *p) |
diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/dvb/firewire/firedtv-fw.c index fe44789ab037..6223bf01efe9 100644 --- a/drivers/media/dvb/firewire/firedtv-fw.c +++ b/drivers/media/dvb/firewire/firedtv-fw.c | |||
@@ -202,14 +202,8 @@ static void handle_fcp(struct fw_card *card, struct fw_request *request, | |||
202 | unsigned long flags; | 202 | unsigned long flags; |
203 | int su; | 203 | int su; |
204 | 204 | ||
205 | if ((tcode != TCODE_WRITE_QUADLET_REQUEST && | 205 | if (length < 2 || (((u8 *)payload)[0] & 0xf0) != 0) |
206 | tcode != TCODE_WRITE_BLOCK_REQUEST) || | ||
207 | offset != CSR_REGISTER_BASE + CSR_FCP_RESPONSE || | ||
208 | length == 0 || | ||
209 | (((u8 *)payload)[0] & 0xf0) != 0) { | ||
210 | fw_send_response(card, request, RCODE_TYPE_ERROR); | ||
211 | return; | 206 | return; |
212 | } | ||
213 | 207 | ||
214 | su = ((u8 *)payload)[1] & 0x7; | 208 | su = ((u8 *)payload)[1] & 0x7; |
215 | 209 | ||
@@ -230,10 +224,8 @@ static void handle_fcp(struct fw_card *card, struct fw_request *request, | |||
230 | } | 224 | } |
231 | spin_unlock_irqrestore(&node_list_lock, flags); | 225 | spin_unlock_irqrestore(&node_list_lock, flags); |
232 | 226 | ||
233 | if (fdtv) { | 227 | if (fdtv) |
234 | avc_recv(fdtv, payload, length); | 228 | avc_recv(fdtv, payload, length); |
235 | fw_send_response(card, request, RCODE_COMPLETE); | ||
236 | } | ||
237 | } | 229 | } |
238 | 230 | ||
239 | static struct fw_address_handler fcp_handler = { | 231 | static struct fw_address_handler fcp_handler = { |
diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index c6b3ca3af6df..1f716d9f714b 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h | |||
@@ -340,6 +340,9 @@ struct fw_cdev_send_response { | |||
340 | * The @closure field is passed back to userspace in the response event. | 340 | * The @closure field is passed back to userspace in the response event. |
341 | * The @handle field is an out parameter, returning a handle to the allocated | 341 | * The @handle field is an out parameter, returning a handle to the allocated |
342 | * range to be used for later deallocation of the range. | 342 | * range to be used for later deallocation of the range. |
343 | * | ||
344 | * The address range is allocated on all local nodes. The address allocation | ||
345 | * is exclusive except for the FCP command and response registers. | ||
343 | */ | 346 | */ |
344 | struct fw_cdev_allocate { | 347 | struct fw_cdev_allocate { |
345 | __u64 offset; | 348 | __u64 offset; |
diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 9416a461b696..a0e67150a729 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h | |||
@@ -248,8 +248,8 @@ typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode, | |||
248 | void *data, size_t length, | 248 | void *data, size_t length, |
249 | void *callback_data); | 249 | void *callback_data); |
250 | /* | 250 | /* |
251 | * Important note: The callback must guarantee that either fw_send_response() | 251 | * Important note: Except for the FCP registers, the callback must guarantee |
252 | * or kfree() is called on the @request. | 252 | * that either fw_send_response() or kfree() is called on the @request. |
253 | */ | 253 | */ |
254 | typedef void (*fw_address_callback_t)(struct fw_card *card, | 254 | typedef void (*fw_address_callback_t)(struct fw_card *card, |
255 | struct fw_request *request, | 255 | struct fw_request *request, |