diff options
Diffstat (limited to 'drivers/firewire/core-transaction.c')
-rw-r--r-- | drivers/firewire/core-transaction.c | 154 |
1 files changed, 111 insertions, 43 deletions
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index da628c72a462..673b03f8b4ec 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c | |||
@@ -218,12 +218,15 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, | |||
218 | packet->header_length = 16; | 218 | packet->header_length = 16; |
219 | packet->payload_length = 0; | 219 | packet->payload_length = 0; |
220 | break; | 220 | break; |
221 | |||
222 | default: | ||
223 | WARN(1, KERN_ERR "wrong tcode %d", tcode); | ||
221 | } | 224 | } |
222 | common: | 225 | common: |
223 | packet->speed = speed; | 226 | packet->speed = speed; |
224 | packet->generation = generation; | 227 | packet->generation = generation; |
225 | packet->ack = 0; | 228 | packet->ack = 0; |
226 | packet->payload_bus = 0; | 229 | packet->payload_mapped = false; |
227 | } | 230 | } |
228 | 231 | ||
229 | /** | 232 | /** |
@@ -429,14 +432,20 @@ static struct fw_address_handler *lookup_overlapping_address_handler( | |||
429 | return NULL; | 432 | return NULL; |
430 | } | 433 | } |
431 | 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 | |||
432 | static struct fw_address_handler *lookup_enclosing_address_handler( | 442 | static struct fw_address_handler *lookup_enclosing_address_handler( |
433 | struct list_head *list, unsigned long long offset, size_t length) | 443 | struct list_head *list, unsigned long long offset, size_t length) |
434 | { | 444 | { |
435 | struct fw_address_handler *handler; | 445 | struct fw_address_handler *handler; |
436 | 446 | ||
437 | list_for_each_entry(handler, list, link) { | 447 | list_for_each_entry(handler, list, link) { |
438 | if (handler->offset <= offset && | 448 | if (is_enclosing_handler(handler, offset, length)) |
439 | offset + length <= handler->offset + handler->length) | ||
440 | return handler; | 449 | return handler; |
441 | } | 450 | } |
442 | 451 | ||
@@ -462,6 +471,12 @@ const struct fw_address_region fw_unit_space_region = | |||
462 | { .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, }; | 471 | { .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, }; |
463 | #endif /* 0 */ | 472 | #endif /* 0 */ |
464 | 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 | |||
465 | /** | 480 | /** |
466 | * fw_core_add_address_handler - register for incoming requests | 481 | * fw_core_add_address_handler - register for incoming requests |
467 | * @handler: callback | 482 | * @handler: callback |
@@ -474,8 +489,11 @@ const struct fw_address_region fw_unit_space_region = | |||
474 | * give the details of the particular request. | 489 | * give the details of the particular request. |
475 | * | 490 | * |
476 | * Return value: 0 on success, non-zero otherwise. | 491 | * Return value: 0 on success, non-zero otherwise. |
492 | * | ||
477 | * 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 |
478 | * 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. | ||
479 | */ | 497 | */ |
480 | int fw_core_add_address_handler(struct fw_address_handler *handler, | 498 | int fw_core_add_address_handler(struct fw_address_handler *handler, |
481 | const struct fw_address_region *region) | 499 | const struct fw_address_region *region) |
@@ -495,10 +513,12 @@ int fw_core_add_address_handler(struct fw_address_handler *handler, | |||
495 | 513 | ||
496 | handler->offset = region->start; | 514 | handler->offset = region->start; |
497 | while (handler->offset + handler->length <= region->end) { | 515 | while (handler->offset + handler->length <= region->end) { |
498 | other = | 516 | if (is_in_fcp_region(handler->offset, handler->length)) |
499 | lookup_overlapping_address_handler(&address_handler_list, | 517 | other = NULL; |
500 | handler->offset, | 518 | else |
501 | handler->length); | 519 | other = lookup_overlapping_address_handler |
520 | (&address_handler_list, | ||
521 | handler->offset, handler->length); | ||
502 | if (other != NULL) { | 522 | if (other != NULL) { |
503 | handler->offset += other->length; | 523 | handler->offset += other->length; |
504 | } else { | 524 | } else { |
@@ -595,11 +615,10 @@ void fw_fill_response(struct fw_packet *response, u32 *request_header, | |||
595 | break; | 615 | break; |
596 | 616 | ||
597 | default: | 617 | default: |
598 | BUG(); | 618 | WARN(1, KERN_ERR "wrong tcode %d", tcode); |
599 | return; | ||
600 | } | 619 | } |
601 | 620 | ||
602 | response->payload_bus = 0; | 621 | response->payload_mapped = false; |
603 | } | 622 | } |
604 | EXPORT_SYMBOL(fw_fill_response); | 623 | EXPORT_SYMBOL(fw_fill_response); |
605 | 624 | ||
@@ -666,6 +685,9 @@ static struct fw_request *allocate_request(struct fw_packet *p) | |||
666 | void fw_send_response(struct fw_card *card, | 685 | void fw_send_response(struct fw_card *card, |
667 | struct fw_request *request, int rcode) | 686 | struct fw_request *request, int rcode) |
668 | { | 687 | { |
688 | if (WARN_ONCE(!request, "invalid for FCP address handlers")) | ||
689 | return; | ||
690 | |||
669 | /* unified transaction or broadcast transaction: don't respond */ | 691 | /* unified transaction or broadcast transaction: don't respond */ |
670 | if (request->ack != ACK_PENDING || | 692 | if (request->ack != ACK_PENDING || |
671 | HEADER_DESTINATION_IS_BROADCAST(request->request_header[0])) { | 693 | HEADER_DESTINATION_IS_BROADCAST(request->request_header[0])) { |
@@ -684,26 +706,15 @@ void fw_send_response(struct fw_card *card, | |||
684 | } | 706 | } |
685 | EXPORT_SYMBOL(fw_send_response); | 707 | EXPORT_SYMBOL(fw_send_response); |
686 | 708 | ||
687 | 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) | ||
688 | { | 713 | { |
689 | struct fw_address_handler *handler; | 714 | struct fw_address_handler *handler; |
690 | struct fw_request *request; | ||
691 | unsigned long long offset; | ||
692 | unsigned long flags; | 715 | unsigned long flags; |
693 | int tcode, destination, source; | 716 | int tcode, destination, source; |
694 | 717 | ||
695 | if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE) | ||
696 | return; | ||
697 | |||
698 | request = allocate_request(p); | ||
699 | if (request == NULL) { | ||
700 | /* FIXME: send statically allocated busy packet. */ | ||
701 | return; | ||
702 | } | ||
703 | |||
704 | offset = | ||
705 | ((unsigned long long) | ||
706 | HEADER_GET_OFFSET_HIGH(p->header[1]) << 32) | p->header[2]; | ||
707 | tcode = HEADER_GET_TCODE(p->header[0]); | 718 | tcode = HEADER_GET_TCODE(p->header[0]); |
708 | destination = HEADER_GET_DESTINATION(p->header[0]); | 719 | destination = HEADER_GET_DESTINATION(p->header[0]); |
709 | source = HEADER_GET_SOURCE(p->header[1]); | 720 | source = HEADER_GET_SOURCE(p->header[1]); |
@@ -730,6 +741,73 @@ void fw_core_handle_request(struct fw_card *card, struct fw_packet *p) | |||
730 | request->data, request->length, | 741 | request->data, request->length, |
731 | handler->callback_data); | 742 | handler->callback_data); |
732 | } | 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 | } | ||
733 | EXPORT_SYMBOL(fw_core_handle_request); | 811 | EXPORT_SYMBOL(fw_core_handle_request); |
734 | 812 | ||
735 | 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) |
@@ -810,8 +888,7 @@ static void handle_topology_map(struct fw_card *card, struct fw_request *request | |||
810 | int speed, unsigned long long offset, | 888 | int speed, unsigned long long offset, |
811 | void *payload, size_t length, void *callback_data) | 889 | void *payload, size_t length, void *callback_data) |
812 | { | 890 | { |
813 | int i, start, end; | 891 | int start; |
814 | __be32 *map; | ||
815 | 892 | ||
816 | if (!TCODE_IS_READ_REQUEST(tcode)) { | 893 | if (!TCODE_IS_READ_REQUEST(tcode)) { |
817 | fw_send_response(card, request, RCODE_TYPE_ERROR); | 894 | fw_send_response(card, request, RCODE_TYPE_ERROR); |
@@ -824,11 +901,7 @@ static void handle_topology_map(struct fw_card *card, struct fw_request *request | |||
824 | } | 901 | } |
825 | 902 | ||
826 | start = (offset - topology_map_region.start) / 4; | 903 | start = (offset - topology_map_region.start) / 4; |
827 | end = start + length / 4; | 904 | memcpy(payload, &card->topology_map[start], length); |
828 | map = payload; | ||
829 | |||
830 | for (i = 0; i < length / 4; i++) | ||
831 | map[i] = cpu_to_be32(card->topology_map[start + i]); | ||
832 | 905 | ||
833 | fw_send_response(card, request, RCODE_COMPLETE); | 906 | fw_send_response(card, request, RCODE_COMPLETE); |
834 | } | 907 | } |
@@ -848,23 +921,15 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, | |||
848 | void *payload, size_t length, void *callback_data) | 921 | void *payload, size_t length, void *callback_data) |
849 | { | 922 | { |
850 | int reg = offset & ~CSR_REGISTER_BASE; | 923 | int reg = offset & ~CSR_REGISTER_BASE; |
851 | unsigned long long bus_time; | ||
852 | __be32 *data = payload; | 924 | __be32 *data = payload; |
853 | int rcode = RCODE_COMPLETE; | 925 | int rcode = RCODE_COMPLETE; |
854 | 926 | ||
855 | switch (reg) { | 927 | switch (reg) { |
856 | case CSR_CYCLE_TIME: | 928 | case CSR_CYCLE_TIME: |
857 | case CSR_BUS_TIME: | 929 | if (TCODE_IS_READ_REQUEST(tcode) && length == 4) |
858 | if (!TCODE_IS_READ_REQUEST(tcode) || length != 4) { | 930 | *data = cpu_to_be32(card->driver->get_cycle_time(card)); |
859 | rcode = RCODE_TYPE_ERROR; | ||
860 | break; | ||
861 | } | ||
862 | |||
863 | bus_time = card->driver->get_bus_time(card); | ||
864 | if (reg == CSR_CYCLE_TIME) | ||
865 | *data = cpu_to_be32(bus_time); | ||
866 | else | 931 | else |
867 | *data = cpu_to_be32(bus_time >> 25); | 932 | rcode = RCODE_TYPE_ERROR; |
868 | break; | 933 | break; |
869 | 934 | ||
870 | case CSR_BROADCAST_CHANNEL: | 935 | case CSR_BROADCAST_CHANNEL: |
@@ -895,6 +960,9 @@ static void handle_registers(struct fw_card *card, struct fw_request *request, | |||
895 | case CSR_BUSY_TIMEOUT: | 960 | case CSR_BUSY_TIMEOUT: |
896 | /* FIXME: Implement this. */ | 961 | /* FIXME: Implement this. */ |
897 | 962 | ||
963 | case CSR_BUS_TIME: | ||
964 | /* Useless without initialization by the bus manager. */ | ||
965 | |||
898 | default: | 966 | default: |
899 | rcode = RCODE_ADDRESS_ERROR; | 967 | rcode = RCODE_ADDRESS_ERROR; |
900 | break; | 968 | break; |