aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire/core-cdev.c
diff options
context:
space:
mode:
authorStefan Richter <stefanr@s5r6.in-berlin.de>2010-07-29 12:19:22 -0400
committerStefan Richter <stefanr@s5r6.in-berlin.de>2010-07-29 17:09:18 -0400
commit872e330e38806d835bd6c311c93ab998e2fb9058 (patch)
tree92497ce79b1157761b1aebdb63b8d74f68d42c15 /drivers/firewire/core-cdev.c
parentae2a97661482c1d0f1aa41b837da95054d0e9a1b (diff)
firewire: add isochronous multichannel reception
This adds the DMA context programming and userspace ABI for multichannel reception, i.e. for listening on multiple channel numbers by means of a single DMA context. The use case is reception of more streams than there are IR DMA units offered by the link layer. This is already implemented by the older ohci1394 + ieee1394 + raw1394 stack. And as discussed recently on linux1394-devel, this feature is occasionally used in practice. The big drawbacks of this mode are that buffer layout and interrupt generation necessarily differ from single-channel reception: Headers and trailers are not stripped from packets, packets are not aligned with buffer chunks, interrupts are per buffer chunk, not per packet. These drawbacks also cause a rather hefty code footprint to support this rarely used OHCI-1394 feature. (367 lines added, among them 94 lines of added userspace ABI documentation.) This implementation enforces that a multichannel reception context may only listen to channels to which no single-channel context on the same link layer is presently listening to. OHCI-1394 would allow to overlay single-channel contexts by the multi-channel context, but this would be a departure from the present first-come-first-served policy of IR context creation. The implementation is heavily based on an earlier one by Jay Fenlason. Thanks Jay. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire/core-cdev.c')
-rw-r--r--drivers/firewire/core-cdev.c93
1 files changed, 72 insertions, 21 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index cf989e1635e1..ba23646bb108 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -193,6 +193,11 @@ struct iso_interrupt_event {
193 struct fw_cdev_event_iso_interrupt interrupt; 193 struct fw_cdev_event_iso_interrupt interrupt;
194}; 194};
195 195
196struct iso_interrupt_mc_event {
197 struct event event;
198 struct fw_cdev_event_iso_interrupt_mc interrupt;
199};
200
196struct iso_resource_event { 201struct iso_resource_event {
197 struct event event; 202 struct event event;
198 struct fw_cdev_event_iso_resource iso_resource; 203 struct fw_cdev_event_iso_resource iso_resource;
@@ -415,6 +420,7 @@ union ioctl_arg {
415 struct fw_cdev_get_cycle_timer2 get_cycle_timer2; 420 struct fw_cdev_get_cycle_timer2 get_cycle_timer2;
416 struct fw_cdev_send_phy_packet send_phy_packet; 421 struct fw_cdev_send_phy_packet send_phy_packet;
417 struct fw_cdev_receive_phy_packets receive_phy_packets; 422 struct fw_cdev_receive_phy_packets receive_phy_packets;
423 struct fw_cdev_set_iso_channels set_iso_channels;
418}; 424};
419 425
420static int ioctl_get_info(struct client *client, union ioctl_arg *arg) 426static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
@@ -932,26 +938,54 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle,
932 sizeof(e->interrupt) + header_length, NULL, 0); 938 sizeof(e->interrupt) + header_length, NULL, 0);
933} 939}
934 940
941static void iso_mc_callback(struct fw_iso_context *context,
942 dma_addr_t completed, void *data)
943{
944 struct client *client = data;
945 struct iso_interrupt_mc_event *e;
946
947 e = kmalloc(sizeof(*e), GFP_ATOMIC);
948 if (e == NULL) {
949 fw_notify("Out of memory when allocating event\n");
950 return;
951 }
952 e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL;
953 e->interrupt.closure = client->iso_closure;
954 e->interrupt.completed = fw_iso_buffer_lookup(&client->buffer,
955 completed);
956 queue_event(client, &e->event, &e->interrupt,
957 sizeof(e->interrupt), NULL, 0);
958}
959
935static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) 960static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
936{ 961{
937 struct fw_cdev_create_iso_context *a = &arg->create_iso_context; 962 struct fw_cdev_create_iso_context *a = &arg->create_iso_context;
938 struct fw_iso_context *context; 963 struct fw_iso_context *context;
964 fw_iso_callback_t cb;
939 965
940 BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT || 966 BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT ||
941 FW_CDEV_ISO_CONTEXT_RECEIVE != FW_ISO_CONTEXT_RECEIVE); 967 FW_CDEV_ISO_CONTEXT_RECEIVE != FW_ISO_CONTEXT_RECEIVE ||
942 968 FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL !=
943 if (a->channel > 63) 969 FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL);
944 return -EINVAL;
945 970
946 switch (a->type) { 971 switch (a->type) {
947 case FW_ISO_CONTEXT_RECEIVE: 972 case FW_ISO_CONTEXT_TRANSMIT:
948 if (a->header_size < 4 || (a->header_size & 3)) 973 if (a->speed > SCODE_3200 || a->channel > 63)
949 return -EINVAL; 974 return -EINVAL;
975
976 cb = iso_callback;
950 break; 977 break;
951 978
952 case FW_ISO_CONTEXT_TRANSMIT: 979 case FW_ISO_CONTEXT_RECEIVE:
953 if (a->speed > SCODE_3200) 980 if (a->header_size < 4 || (a->header_size & 3) ||
981 a->channel > 63)
954 return -EINVAL; 982 return -EINVAL;
983
984 cb = iso_callback;
985 break;
986
987 case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
988 cb = (fw_iso_callback_t)iso_mc_callback;
955 break; 989 break;
956 990
957 default: 991 default:
@@ -959,8 +993,7 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
959 } 993 }
960 994
961 context = fw_iso_context_create(client->device->card, a->type, 995 context = fw_iso_context_create(client->device->card, a->type,
962 a->channel, a->speed, a->header_size, 996 a->channel, a->speed, a->header_size, cb, client);
963 iso_callback, client);
964 if (IS_ERR(context)) 997 if (IS_ERR(context))
965 return PTR_ERR(context); 998 return PTR_ERR(context);
966 999
@@ -980,6 +1013,17 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
980 return 0; 1013 return 0;
981} 1014}
982 1015
1016static int ioctl_set_iso_channels(struct client *client, union ioctl_arg *arg)
1017{
1018 struct fw_cdev_set_iso_channels *a = &arg->set_iso_channels;
1019 struct fw_iso_context *ctx = client->iso_context;
1020
1021 if (ctx == NULL || a->handle != 0)
1022 return -EINVAL;
1023
1024 return fw_iso_context_set_channels(ctx, &a->channels);
1025}
1026
983/* Macros for decoding the iso packet control header. */ 1027/* Macros for decoding the iso packet control header. */
984#define GET_PAYLOAD_LENGTH(v) ((v) & 0xffff) 1028#define GET_PAYLOAD_LENGTH(v) ((v) & 0xffff)
985#define GET_INTERRUPT(v) (((v) >> 16) & 0x01) 1029#define GET_INTERRUPT(v) (((v) >> 16) & 0x01)
@@ -993,7 +1037,7 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
993 struct fw_cdev_queue_iso *a = &arg->queue_iso; 1037 struct fw_cdev_queue_iso *a = &arg->queue_iso;
994 struct fw_cdev_iso_packet __user *p, *end, *next; 1038 struct fw_cdev_iso_packet __user *p, *end, *next;
995 struct fw_iso_context *ctx = client->iso_context; 1039 struct fw_iso_context *ctx = client->iso_context;
996 unsigned long payload, buffer_end, transmit_header_bytes; 1040 unsigned long payload, buffer_end, transmit_header_bytes = 0;
997 u32 control; 1041 u32 control;
998 int count; 1042 int count;
999 struct { 1043 struct {
@@ -1013,7 +1057,6 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
1013 * use the indirect payload, the iso buffer need not be mapped 1057 * use the indirect payload, the iso buffer need not be mapped
1014 * and the a->data pointer is ignored. 1058 * and the a->data pointer is ignored.
1015 */ 1059 */
1016
1017 payload = (unsigned long)a->data - client->vm_start; 1060 payload = (unsigned long)a->data - client->vm_start;
1018 buffer_end = client->buffer.page_count << PAGE_SHIFT; 1061 buffer_end = client->buffer.page_count << PAGE_SHIFT;
1019 if (a->data == 0 || client->buffer.pages == NULL || 1062 if (a->data == 0 || client->buffer.pages == NULL ||
@@ -1022,8 +1065,10 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
1022 buffer_end = 0; 1065 buffer_end = 0;
1023 } 1066 }
1024 1067
1025 p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets); 1068 if (ctx->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL && payload & 3)
1069 return -EINVAL;
1026 1070
1071 p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets);
1027 if (!access_ok(VERIFY_READ, p, a->size)) 1072 if (!access_ok(VERIFY_READ, p, a->size))
1028 return -EFAULT; 1073 return -EFAULT;
1029 1074
@@ -1039,19 +1084,24 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
1039 u.packet.sy = GET_SY(control); 1084 u.packet.sy = GET_SY(control);
1040 u.packet.header_length = GET_HEADER_LENGTH(control); 1085 u.packet.header_length = GET_HEADER_LENGTH(control);
1041 1086
1042 if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) { 1087 switch (ctx->type) {
1043 if (u.packet.header_length % 4 != 0) 1088 case FW_ISO_CONTEXT_TRANSMIT:
1089 if (u.packet.header_length & 3)
1044 return -EINVAL; 1090 return -EINVAL;
1045 transmit_header_bytes = u.packet.header_length; 1091 transmit_header_bytes = u.packet.header_length;
1046 } else { 1092 break;
1047 /* 1093
1048 * We require that header_length is a multiple of 1094 case FW_ISO_CONTEXT_RECEIVE:
1049 * the fixed header size, ctx->header_size.
1050 */
1051 if (u.packet.header_length == 0 || 1095 if (u.packet.header_length == 0 ||
1052 u.packet.header_length % ctx->header_size != 0) 1096 u.packet.header_length % ctx->header_size != 0)
1053 return -EINVAL; 1097 return -EINVAL;
1054 transmit_header_bytes = 0; 1098 break;
1099
1100 case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
1101 if (u.packet.payload_length == 0 ||
1102 u.packet.payload_length & 3)
1103 return -EINVAL;
1104 break;
1055 } 1105 }
1056 1106
1057 next = (struct fw_cdev_iso_packet __user *) 1107 next = (struct fw_cdev_iso_packet __user *)
@@ -1534,6 +1584,7 @@ static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = {
1534 [0x14] = ioctl_get_cycle_timer2, 1584 [0x14] = ioctl_get_cycle_timer2,
1535 [0x15] = ioctl_send_phy_packet, 1585 [0x15] = ioctl_send_phy_packet,
1536 [0x16] = ioctl_receive_phy_packets, 1586 [0x16] = ioctl_receive_phy_packets,
1587 [0x17] = ioctl_set_iso_channels,
1537}; 1588};
1538 1589
1539static int dispatch_ioctl(struct client *client, 1590static int dispatch_ioctl(struct client *client,