Commit 872e330e38806d835bd6c311c93ab998e2fb9058
1 parent
ae2a976614
Exists in
master
and in
7 other branches
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>
Showing 6 changed files with 560 additions and 193 deletions Side-by-side Diff
drivers/firewire/core-cdev.c
... | ... | @@ -193,6 +193,11 @@ |
193 | 193 | struct fw_cdev_event_iso_interrupt interrupt; |
194 | 194 | }; |
195 | 195 | |
196 | +struct iso_interrupt_mc_event { | |
197 | + struct event event; | |
198 | + struct fw_cdev_event_iso_interrupt_mc interrupt; | |
199 | +}; | |
200 | + | |
196 | 201 | struct iso_resource_event { |
197 | 202 | struct event event; |
198 | 203 | struct fw_cdev_event_iso_resource iso_resource; |
... | ... | @@ -415,6 +420,7 @@ |
415 | 420 | struct fw_cdev_get_cycle_timer2 get_cycle_timer2; |
416 | 421 | struct fw_cdev_send_phy_packet send_phy_packet; |
417 | 422 | struct fw_cdev_receive_phy_packets receive_phy_packets; |
423 | + struct fw_cdev_set_iso_channels set_iso_channels; | |
418 | 424 | }; |
419 | 425 | |
420 | 426 | static int ioctl_get_info(struct client *client, union ioctl_arg *arg) |
421 | 427 | |
422 | 428 | |
423 | 429 | |
424 | 430 | |
425 | 431 | |
426 | 432 | |
427 | 433 | |
428 | 434 | |
429 | 435 | |
... | ... | @@ -932,35 +938,62 @@ |
932 | 938 | sizeof(e->interrupt) + header_length, NULL, 0); |
933 | 939 | } |
934 | 940 | |
941 | +static 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 | + | |
935 | 960 | static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) |
936 | 961 | { |
937 | 962 | struct fw_cdev_create_iso_context *a = &arg->create_iso_context; |
938 | 963 | struct fw_iso_context *context; |
964 | + fw_iso_callback_t cb; | |
939 | 965 | |
940 | 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 || | |
968 | + FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL != | |
969 | + FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL); | |
942 | 970 | |
943 | - if (a->channel > 63) | |
944 | - return -EINVAL; | |
945 | - | |
946 | 971 | switch (a->type) { |
947 | - case FW_ISO_CONTEXT_RECEIVE: | |
948 | - if (a->header_size < 4 || (a->header_size & 3)) | |
972 | + case FW_ISO_CONTEXT_TRANSMIT: | |
973 | + if (a->speed > SCODE_3200 || a->channel > 63) | |
949 | 974 | return -EINVAL; |
975 | + | |
976 | + cb = iso_callback; | |
950 | 977 | break; |
951 | 978 | |
952 | - case FW_ISO_CONTEXT_TRANSMIT: | |
953 | - if (a->speed > SCODE_3200) | |
979 | + case FW_ISO_CONTEXT_RECEIVE: | |
980 | + if (a->header_size < 4 || (a->header_size & 3) || | |
981 | + a->channel > 63) | |
954 | 982 | return -EINVAL; |
983 | + | |
984 | + cb = iso_callback; | |
955 | 985 | break; |
956 | 986 | |
987 | + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: | |
988 | + cb = (fw_iso_callback_t)iso_mc_callback; | |
989 | + break; | |
990 | + | |
957 | 991 | default: |
958 | 992 | return -EINVAL; |
959 | 993 | } |
960 | 994 | |
961 | 995 | context = fw_iso_context_create(client->device->card, a->type, |
962 | - a->channel, a->speed, a->header_size, | |
963 | - iso_callback, client); | |
996 | + a->channel, a->speed, a->header_size, cb, client); | |
964 | 997 | if (IS_ERR(context)) |
965 | 998 | return PTR_ERR(context); |
966 | 999 | |
... | ... | @@ -980,6 +1013,17 @@ |
980 | 1013 | return 0; |
981 | 1014 | } |
982 | 1015 | |
1016 | +static 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 | 1027 | /* Macros for decoding the iso packet control header. */ |
984 | 1028 | #define GET_PAYLOAD_LENGTH(v) ((v) & 0xffff) |
985 | 1029 | #define GET_INTERRUPT(v) (((v) >> 16) & 0x01) |
... | ... | @@ -993,7 +1037,7 @@ |
993 | 1037 | struct fw_cdev_queue_iso *a = &arg->queue_iso; |
994 | 1038 | struct fw_cdev_iso_packet __user *p, *end, *next; |
995 | 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 | 1041 | u32 control; |
998 | 1042 | int count; |
999 | 1043 | struct { |
... | ... | @@ -1013,7 +1057,6 @@ |
1013 | 1057 | * use the indirect payload, the iso buffer need not be mapped |
1014 | 1058 | * and the a->data pointer is ignored. |
1015 | 1059 | */ |
1016 | - | |
1017 | 1060 | payload = (unsigned long)a->data - client->vm_start; |
1018 | 1061 | buffer_end = client->buffer.page_count << PAGE_SHIFT; |
1019 | 1062 | if (a->data == 0 || client->buffer.pages == NULL || |
1020 | 1063 | |
... | ... | @@ -1022,8 +1065,10 @@ |
1022 | 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 | 1072 | if (!access_ok(VERIFY_READ, p, a->size)) |
1028 | 1073 | return -EFAULT; |
1029 | 1074 | |
1030 | 1075 | |
1031 | 1076 | |
... | ... | @@ -1039,19 +1084,24 @@ |
1039 | 1084 | u.packet.sy = GET_SY(control); |
1040 | 1085 | u.packet.header_length = GET_HEADER_LENGTH(control); |
1041 | 1086 | |
1042 | - if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) { | |
1043 | - if (u.packet.header_length % 4 != 0) | |
1087 | + switch (ctx->type) { | |
1088 | + case FW_ISO_CONTEXT_TRANSMIT: | |
1089 | + if (u.packet.header_length & 3) | |
1044 | 1090 | return -EINVAL; |
1045 | 1091 | transmit_header_bytes = u.packet.header_length; |
1046 | - } else { | |
1047 | - /* | |
1048 | - * We require that header_length is a multiple of | |
1049 | - * the fixed header size, ctx->header_size. | |
1050 | - */ | |
1092 | + break; | |
1093 | + | |
1094 | + case FW_ISO_CONTEXT_RECEIVE: | |
1051 | 1095 | if (u.packet.header_length == 0 || |
1052 | 1096 | u.packet.header_length % ctx->header_size != 0) |
1053 | 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 | 1107 | next = (struct fw_cdev_iso_packet __user *) |
... | ... | @@ -1534,6 +1584,7 @@ |
1534 | 1584 | [0x14] = ioctl_get_cycle_timer2, |
1535 | 1585 | [0x15] = ioctl_send_phy_packet, |
1536 | 1586 | [0x16] = ioctl_receive_phy_packets, |
1587 | + [0x17] = ioctl_set_iso_channels, | |
1537 | 1588 | }; |
1538 | 1589 | |
1539 | 1590 | static int dispatch_ioctl(struct client *client, |
drivers/firewire/core-iso.c
... | ... | @@ -117,6 +117,23 @@ |
117 | 117 | } |
118 | 118 | EXPORT_SYMBOL(fw_iso_buffer_destroy); |
119 | 119 | |
120 | +/* Convert DMA address to offset into virtually contiguous buffer. */ | |
121 | +size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed) | |
122 | +{ | |
123 | + int i; | |
124 | + dma_addr_t address; | |
125 | + ssize_t offset; | |
126 | + | |
127 | + for (i = 0; i < buffer->page_count; i++) { | |
128 | + address = page_private(buffer->pages[i]); | |
129 | + offset = (ssize_t)completed - (ssize_t)address; | |
130 | + if (offset > 0 && offset <= PAGE_SIZE) | |
131 | + return (i << PAGE_SHIFT) + offset; | |
132 | + } | |
133 | + | |
134 | + return 0; | |
135 | +} | |
136 | + | |
120 | 137 | struct fw_iso_context *fw_iso_context_create(struct fw_card *card, |
121 | 138 | int type, int channel, int speed, size_t header_size, |
122 | 139 | fw_iso_callback_t callback, void *callback_data) |
... | ... | @@ -133,7 +150,7 @@ |
133 | 150 | ctx->channel = channel; |
134 | 151 | ctx->speed = speed; |
135 | 152 | ctx->header_size = header_size; |
136 | - ctx->callback = callback; | |
153 | + ctx->callback.sc = callback; | |
137 | 154 | ctx->callback_data = callback_data; |
138 | 155 | |
139 | 156 | return ctx; |
... | ... | @@ -142,9 +159,7 @@ |
142 | 159 | |
143 | 160 | void fw_iso_context_destroy(struct fw_iso_context *ctx) |
144 | 161 | { |
145 | - struct fw_card *card = ctx->card; | |
146 | - | |
147 | - card->driver->free_iso_context(ctx); | |
162 | + ctx->card->driver->free_iso_context(ctx); | |
148 | 163 | } |
149 | 164 | EXPORT_SYMBOL(fw_iso_context_destroy); |
150 | 165 | |
151 | 166 | |
... | ... | @@ -155,14 +170,17 @@ |
155 | 170 | } |
156 | 171 | EXPORT_SYMBOL(fw_iso_context_start); |
157 | 172 | |
173 | +int fw_iso_context_set_channels(struct fw_iso_context *ctx, u64 *channels) | |
174 | +{ | |
175 | + return ctx->card->driver->set_iso_channels(ctx, channels); | |
176 | +} | |
177 | + | |
158 | 178 | int fw_iso_context_queue(struct fw_iso_context *ctx, |
159 | 179 | struct fw_iso_packet *packet, |
160 | 180 | struct fw_iso_buffer *buffer, |
161 | 181 | unsigned long payload) |
162 | 182 | { |
163 | - struct fw_card *card = ctx->card; | |
164 | - | |
165 | - return card->driver->queue_iso(ctx, packet, buffer, payload); | |
183 | + return ctx->card->driver->queue_iso(ctx, packet, buffer, payload); | |
166 | 184 | } |
167 | 185 | EXPORT_SYMBOL(fw_iso_context_queue); |
168 | 186 |
drivers/firewire/core.h
... | ... | @@ -90,6 +90,8 @@ |
90 | 90 | int (*start_iso)(struct fw_iso_context *ctx, |
91 | 91 | s32 cycle, u32 sync, u32 tags); |
92 | 92 | |
93 | + int (*set_iso_channels)(struct fw_iso_context *ctx, u64 *channels); | |
94 | + | |
93 | 95 | int (*queue_iso)(struct fw_iso_context *ctx, |
94 | 96 | struct fw_iso_packet *packet, |
95 | 97 | struct fw_iso_buffer *buffer, |
drivers/firewire/ohci.c
... | ... | @@ -190,11 +190,13 @@ |
190 | 190 | struct context at_request_ctx; |
191 | 191 | struct context at_response_ctx; |
192 | 192 | |
193 | - u32 it_context_mask; | |
193 | + u32 it_context_mask; /* unoccupied IT contexts */ | |
194 | 194 | struct iso_context *it_context_list; |
195 | - u64 ir_context_channels; | |
196 | - u32 ir_context_mask; | |
195 | + u64 ir_context_channels; /* unoccupied channels */ | |
196 | + u32 ir_context_mask; /* unoccupied IR contexts */ | |
197 | 197 | struct iso_context *ir_context_list; |
198 | + u64 mc_channels; /* channels in use by the multichannel IR context */ | |
199 | + bool mc_allocated; | |
198 | 200 | |
199 | 201 | __be32 *config_rom; |
200 | 202 | dma_addr_t config_rom_bus; |
201 | 203 | |
... | ... | @@ -2197,10 +2199,9 @@ |
2197 | 2199 | __le32 *ir_header; |
2198 | 2200 | void *p; |
2199 | 2201 | |
2200 | - for (pd = d; pd <= last; pd++) { | |
2202 | + for (pd = d; pd <= last; pd++) | |
2201 | 2203 | if (pd->transfer_status) |
2202 | 2204 | break; |
2203 | - } | |
2204 | 2205 | if (pd > last) |
2205 | 2206 | /* Descriptor(s) not done yet, stop iteration */ |
2206 | 2207 | return 0; |
2207 | 2208 | |
... | ... | @@ -2210,16 +2211,38 @@ |
2210 | 2211 | |
2211 | 2212 | if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) { |
2212 | 2213 | ir_header = (__le32 *) p; |
2213 | - ctx->base.callback(&ctx->base, | |
2214 | - le32_to_cpu(ir_header[0]) & 0xffff, | |
2215 | - ctx->header_length, ctx->header, | |
2216 | - ctx->base.callback_data); | |
2214 | + ctx->base.callback.sc(&ctx->base, | |
2215 | + le32_to_cpu(ir_header[0]) & 0xffff, | |
2216 | + ctx->header_length, ctx->header, | |
2217 | + ctx->base.callback_data); | |
2217 | 2218 | ctx->header_length = 0; |
2218 | 2219 | } |
2219 | 2220 | |
2220 | 2221 | return 1; |
2221 | 2222 | } |
2222 | 2223 | |
2224 | +/* d == last because each descriptor block is only a single descriptor. */ | |
2225 | +static int handle_ir_buffer_fill(struct context *context, | |
2226 | + struct descriptor *d, | |
2227 | + struct descriptor *last) | |
2228 | +{ | |
2229 | + struct iso_context *ctx = | |
2230 | + container_of(context, struct iso_context, context); | |
2231 | + | |
2232 | + if (!last->transfer_status) | |
2233 | + /* Descriptor(s) not done yet, stop iteration */ | |
2234 | + return 0; | |
2235 | + | |
2236 | + if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) | |
2237 | + ctx->base.callback.mc(&ctx->base, | |
2238 | + le32_to_cpu(last->data_address) + | |
2239 | + le16_to_cpu(last->req_count) - | |
2240 | + le16_to_cpu(last->res_count), | |
2241 | + ctx->base.callback_data); | |
2242 | + | |
2243 | + return 1; | |
2244 | +} | |
2245 | + | |
2223 | 2246 | static int handle_it_packet(struct context *context, |
2224 | 2247 | struct descriptor *d, |
2225 | 2248 | struct descriptor *last) |
2226 | 2249 | |
2227 | 2250 | |
2228 | 2251 | |
2229 | 2252 | |
2230 | 2253 | |
2231 | 2254 | |
2232 | 2255 | |
2233 | 2256 | |
2234 | 2257 | |
2235 | 2258 | |
2236 | 2259 | |
2237 | 2260 | |
2238 | 2261 | |
2239 | 2262 | |
2240 | 2263 | |
2241 | 2264 | |
... | ... | @@ -2245,72 +2268,118 @@ |
2245 | 2268 | ctx->header_length += 4; |
2246 | 2269 | } |
2247 | 2270 | if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) { |
2248 | - ctx->base.callback(&ctx->base, le16_to_cpu(last->res_count), | |
2249 | - ctx->header_length, ctx->header, | |
2250 | - ctx->base.callback_data); | |
2271 | + ctx->base.callback.sc(&ctx->base, le16_to_cpu(last->res_count), | |
2272 | + ctx->header_length, ctx->header, | |
2273 | + ctx->base.callback_data); | |
2251 | 2274 | ctx->header_length = 0; |
2252 | 2275 | } |
2253 | 2276 | return 1; |
2254 | 2277 | } |
2255 | 2278 | |
2279 | +static void set_multichannel_mask(struct fw_ohci *ohci, u64 channels) | |
2280 | +{ | |
2281 | + u32 hi = channels >> 32, lo = channels; | |
2282 | + | |
2283 | + reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, ~hi); | |
2284 | + reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, ~lo); | |
2285 | + reg_write(ohci, OHCI1394_IRMultiChanMaskHiSet, hi); | |
2286 | + reg_write(ohci, OHCI1394_IRMultiChanMaskLoSet, lo); | |
2287 | + mmiowb(); | |
2288 | + ohci->mc_channels = channels; | |
2289 | +} | |
2290 | + | |
2256 | 2291 | static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, |
2257 | 2292 | int type, int channel, size_t header_size) |
2258 | 2293 | { |
2259 | 2294 | struct fw_ohci *ohci = fw_ohci(card); |
2260 | - struct iso_context *ctx, *list; | |
2261 | - descriptor_callback_t callback; | |
2262 | - u64 *channels, dont_care = ~0ULL; | |
2263 | - u32 *mask, regs; | |
2295 | + struct iso_context *uninitialized_var(ctx); | |
2296 | + descriptor_callback_t uninitialized_var(callback); | |
2297 | + u64 *uninitialized_var(channels); | |
2298 | + u32 *uninitialized_var(mask), uninitialized_var(regs); | |
2264 | 2299 | unsigned long flags; |
2265 | - int index, ret = -ENOMEM; | |
2300 | + int index, ret = -EBUSY; | |
2266 | 2301 | |
2267 | - if (type == FW_ISO_CONTEXT_TRANSMIT) { | |
2268 | - channels = &dont_care; | |
2269 | - mask = &ohci->it_context_mask; | |
2270 | - list = ohci->it_context_list; | |
2302 | + spin_lock_irqsave(&ohci->lock, flags); | |
2303 | + | |
2304 | + switch (type) { | |
2305 | + case FW_ISO_CONTEXT_TRANSMIT: | |
2306 | + mask = &ohci->it_context_mask; | |
2271 | 2307 | callback = handle_it_packet; |
2272 | - } else { | |
2308 | + index = ffs(*mask) - 1; | |
2309 | + if (index >= 0) { | |
2310 | + *mask &= ~(1 << index); | |
2311 | + regs = OHCI1394_IsoXmitContextBase(index); | |
2312 | + ctx = &ohci->it_context_list[index]; | |
2313 | + } | |
2314 | + break; | |
2315 | + | |
2316 | + case FW_ISO_CONTEXT_RECEIVE: | |
2273 | 2317 | channels = &ohci->ir_context_channels; |
2274 | - mask = &ohci->ir_context_mask; | |
2275 | - list = ohci->ir_context_list; | |
2318 | + mask = &ohci->ir_context_mask; | |
2276 | 2319 | callback = handle_ir_packet_per_buffer; |
2277 | - } | |
2320 | + index = *channels & 1ULL << channel ? ffs(*mask) - 1 : -1; | |
2321 | + if (index >= 0) { | |
2322 | + *channels &= ~(1ULL << channel); | |
2323 | + *mask &= ~(1 << index); | |
2324 | + regs = OHCI1394_IsoRcvContextBase(index); | |
2325 | + ctx = &ohci->ir_context_list[index]; | |
2326 | + } | |
2327 | + break; | |
2278 | 2328 | |
2279 | - spin_lock_irqsave(&ohci->lock, flags); | |
2280 | - index = *channels & 1ULL << channel ? ffs(*mask) - 1 : -1; | |
2281 | - if (index >= 0) { | |
2282 | - *channels &= ~(1ULL << channel); | |
2283 | - *mask &= ~(1 << index); | |
2329 | + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: | |
2330 | + mask = &ohci->ir_context_mask; | |
2331 | + callback = handle_ir_buffer_fill; | |
2332 | + index = !ohci->mc_allocated ? ffs(*mask) - 1 : -1; | |
2333 | + if (index >= 0) { | |
2334 | + ohci->mc_allocated = true; | |
2335 | + *mask &= ~(1 << index); | |
2336 | + regs = OHCI1394_IsoRcvContextBase(index); | |
2337 | + ctx = &ohci->ir_context_list[index]; | |
2338 | + } | |
2339 | + break; | |
2340 | + | |
2341 | + default: | |
2342 | + index = -1; | |
2343 | + ret = -ENOSYS; | |
2284 | 2344 | } |
2345 | + | |
2285 | 2346 | spin_unlock_irqrestore(&ohci->lock, flags); |
2286 | 2347 | |
2287 | 2348 | if (index < 0) |
2288 | - return ERR_PTR(-EBUSY); | |
2349 | + return ERR_PTR(ret); | |
2289 | 2350 | |
2290 | - if (type == FW_ISO_CONTEXT_TRANSMIT) | |
2291 | - regs = OHCI1394_IsoXmitContextBase(index); | |
2292 | - else | |
2293 | - regs = OHCI1394_IsoRcvContextBase(index); | |
2294 | - | |
2295 | - ctx = &list[index]; | |
2296 | 2351 | memset(ctx, 0, sizeof(*ctx)); |
2297 | 2352 | ctx->header_length = 0; |
2298 | 2353 | ctx->header = (void *) __get_free_page(GFP_KERNEL); |
2299 | - if (ctx->header == NULL) | |
2354 | + if (ctx->header == NULL) { | |
2355 | + ret = -ENOMEM; | |
2300 | 2356 | goto out; |
2301 | - | |
2357 | + } | |
2302 | 2358 | ret = context_init(&ctx->context, ohci, regs, callback); |
2303 | 2359 | if (ret < 0) |
2304 | 2360 | goto out_with_header; |
2305 | 2361 | |
2362 | + if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) | |
2363 | + set_multichannel_mask(ohci, 0); | |
2364 | + | |
2306 | 2365 | return &ctx->base; |
2307 | 2366 | |
2308 | 2367 | out_with_header: |
2309 | 2368 | free_page((unsigned long)ctx->header); |
2310 | 2369 | out: |
2311 | 2370 | spin_lock_irqsave(&ohci->lock, flags); |
2312 | - *channels |= 1ULL << channel; | |
2371 | + | |
2372 | + switch (type) { | |
2373 | + case FW_ISO_CONTEXT_RECEIVE: | |
2374 | + *channels |= 1ULL << channel; | |
2375 | + break; | |
2376 | + | |
2377 | + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: | |
2378 | + ohci->mc_allocated = false; | |
2379 | + break; | |
2380 | + } | |
2313 | 2381 | *mask |= 1 << index; |
2382 | + | |
2314 | 2383 | spin_unlock_irqrestore(&ohci->lock, flags); |
2315 | 2384 | |
2316 | 2385 | return ERR_PTR(ret); |
2317 | 2386 | |
... | ... | @@ -2321,10 +2390,11 @@ |
2321 | 2390 | { |
2322 | 2391 | struct iso_context *ctx = container_of(base, struct iso_context, base); |
2323 | 2392 | struct fw_ohci *ohci = ctx->context.ohci; |
2324 | - u32 control, match; | |
2393 | + u32 control = IR_CONTEXT_ISOCH_HEADER, match; | |
2325 | 2394 | int index; |
2326 | 2395 | |
2327 | - if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) { | |
2396 | + switch (ctx->base.type) { | |
2397 | + case FW_ISO_CONTEXT_TRANSMIT: | |
2328 | 2398 | index = ctx - ohci->it_context_list; |
2329 | 2399 | match = 0; |
2330 | 2400 | if (cycle >= 0) |
2331 | 2401 | |
... | ... | @@ -2334,9 +2404,13 @@ |
2334 | 2404 | reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 1 << index); |
2335 | 2405 | reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << index); |
2336 | 2406 | context_run(&ctx->context, match); |
2337 | - } else { | |
2407 | + break; | |
2408 | + | |
2409 | + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: | |
2410 | + control |= IR_CONTEXT_BUFFER_FILL|IR_CONTEXT_MULTI_CHANNEL_MODE; | |
2411 | + /* fall through */ | |
2412 | + case FW_ISO_CONTEXT_RECEIVE: | |
2338 | 2413 | index = ctx - ohci->ir_context_list; |
2339 | - control = IR_CONTEXT_ISOCH_HEADER; | |
2340 | 2414 | match = (tags << 28) | (sync << 8) | ctx->base.channel; |
2341 | 2415 | if (cycle >= 0) { |
2342 | 2416 | match |= (cycle & 0x07fff) << 12; |
... | ... | @@ -2347,6 +2421,7 @@ |
2347 | 2421 | reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << index); |
2348 | 2422 | reg_write(ohci, CONTEXT_MATCH(ctx->context.regs), match); |
2349 | 2423 | context_run(&ctx->context, control); |
2424 | + break; | |
2350 | 2425 | } |
2351 | 2426 | |
2352 | 2427 | return 0; |
2353 | 2428 | |
2354 | 2429 | |
... | ... | @@ -2358,12 +2433,17 @@ |
2358 | 2433 | struct iso_context *ctx = container_of(base, struct iso_context, base); |
2359 | 2434 | int index; |
2360 | 2435 | |
2361 | - if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) { | |
2436 | + switch (ctx->base.type) { | |
2437 | + case FW_ISO_CONTEXT_TRANSMIT: | |
2362 | 2438 | index = ctx - ohci->it_context_list; |
2363 | 2439 | reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 1 << index); |
2364 | - } else { | |
2440 | + break; | |
2441 | + | |
2442 | + case FW_ISO_CONTEXT_RECEIVE: | |
2443 | + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: | |
2365 | 2444 | index = ctx - ohci->ir_context_list; |
2366 | 2445 | reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 1 << index); |
2446 | + break; | |
2367 | 2447 | } |
2368 | 2448 | flush_writes(ohci); |
2369 | 2449 | context_stop(&ctx->context); |
2370 | 2450 | |
2371 | 2451 | |
2372 | 2452 | |
2373 | 2453 | |
... | ... | @@ -2384,24 +2464,65 @@ |
2384 | 2464 | |
2385 | 2465 | spin_lock_irqsave(&ohci->lock, flags); |
2386 | 2466 | |
2387 | - if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) { | |
2467 | + switch (base->type) { | |
2468 | + case FW_ISO_CONTEXT_TRANSMIT: | |
2388 | 2469 | index = ctx - ohci->it_context_list; |
2389 | 2470 | ohci->it_context_mask |= 1 << index; |
2390 | - } else { | |
2471 | + break; | |
2472 | + | |
2473 | + case FW_ISO_CONTEXT_RECEIVE: | |
2391 | 2474 | index = ctx - ohci->ir_context_list; |
2392 | 2475 | ohci->ir_context_mask |= 1 << index; |
2393 | 2476 | ohci->ir_context_channels |= 1ULL << base->channel; |
2477 | + break; | |
2478 | + | |
2479 | + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: | |
2480 | + index = ctx - ohci->ir_context_list; | |
2481 | + ohci->ir_context_mask |= 1 << index; | |
2482 | + ohci->ir_context_channels |= ohci->mc_channels; | |
2483 | + ohci->mc_channels = 0; | |
2484 | + ohci->mc_allocated = false; | |
2485 | + break; | |
2394 | 2486 | } |
2395 | 2487 | |
2396 | 2488 | spin_unlock_irqrestore(&ohci->lock, flags); |
2397 | 2489 | } |
2398 | 2490 | |
2399 | -static int ohci_queue_iso_transmit(struct fw_iso_context *base, | |
2400 | - struct fw_iso_packet *packet, | |
2401 | - struct fw_iso_buffer *buffer, | |
2402 | - unsigned long payload) | |
2491 | +static int ohci_set_iso_channels(struct fw_iso_context *base, u64 *channels) | |
2403 | 2492 | { |
2404 | - struct iso_context *ctx = container_of(base, struct iso_context, base); | |
2493 | + struct fw_ohci *ohci = fw_ohci(base->card); | |
2494 | + unsigned long flags; | |
2495 | + int ret; | |
2496 | + | |
2497 | + switch (base->type) { | |
2498 | + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: | |
2499 | + | |
2500 | + spin_lock_irqsave(&ohci->lock, flags); | |
2501 | + | |
2502 | + /* Don't allow multichannel to grab other contexts' channels. */ | |
2503 | + if (~ohci->ir_context_channels & ~ohci->mc_channels & *channels) { | |
2504 | + *channels = ohci->ir_context_channels; | |
2505 | + ret = -EBUSY; | |
2506 | + } else { | |
2507 | + set_multichannel_mask(ohci, *channels); | |
2508 | + ret = 0; | |
2509 | + } | |
2510 | + | |
2511 | + spin_unlock_irqrestore(&ohci->lock, flags); | |
2512 | + | |
2513 | + break; | |
2514 | + default: | |
2515 | + ret = -EINVAL; | |
2516 | + } | |
2517 | + | |
2518 | + return ret; | |
2519 | +} | |
2520 | + | |
2521 | +static int queue_iso_transmit(struct iso_context *ctx, | |
2522 | + struct fw_iso_packet *packet, | |
2523 | + struct fw_iso_buffer *buffer, | |
2524 | + unsigned long payload) | |
2525 | +{ | |
2405 | 2526 | struct descriptor *d, *last, *pd; |
2406 | 2527 | struct fw_iso_packet *p; |
2407 | 2528 | __le32 *header; |
2408 | 2529 | |
2409 | 2530 | |
... | ... | @@ -2497,14 +2618,12 @@ |
2497 | 2618 | return 0; |
2498 | 2619 | } |
2499 | 2620 | |
2500 | -static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base, | |
2501 | - struct fw_iso_packet *packet, | |
2502 | - struct fw_iso_buffer *buffer, | |
2503 | - unsigned long payload) | |
2621 | +static int queue_iso_packet_per_buffer(struct iso_context *ctx, | |
2622 | + struct fw_iso_packet *packet, | |
2623 | + struct fw_iso_buffer *buffer, | |
2624 | + unsigned long payload) | |
2504 | 2625 | { |
2505 | - struct iso_context *ctx = container_of(base, struct iso_context, base); | |
2506 | 2626 | struct descriptor *d, *pd; |
2507 | - struct fw_iso_packet *p = packet; | |
2508 | 2627 | dma_addr_t d_bus, page_bus; |
2509 | 2628 | u32 z, header_z, rest; |
2510 | 2629 | int i, j, length; |
2511 | 2630 | |
... | ... | @@ -2514,14 +2633,14 @@ |
2514 | 2633 | * The OHCI controller puts the isochronous header and trailer in the |
2515 | 2634 | * buffer, so we need at least 8 bytes. |
2516 | 2635 | */ |
2517 | - packet_count = p->header_length / ctx->base.header_size; | |
2636 | + packet_count = packet->header_length / ctx->base.header_size; | |
2518 | 2637 | header_size = max(ctx->base.header_size, (size_t)8); |
2519 | 2638 | |
2520 | 2639 | /* Get header size in number of descriptors. */ |
2521 | 2640 | header_z = DIV_ROUND_UP(header_size, sizeof(*d)); |
2522 | 2641 | page = payload >> PAGE_SHIFT; |
2523 | 2642 | offset = payload & ~PAGE_MASK; |
2524 | - payload_per_buffer = p->payload_length / packet_count; | |
2643 | + payload_per_buffer = packet->payload_length / packet_count; | |
2525 | 2644 | |
2526 | 2645 | for (i = 0; i < packet_count; i++) { |
2527 | 2646 | /* d points to the header descriptor */ |
... | ... | @@ -2533,7 +2652,7 @@ |
2533 | 2652 | |
2534 | 2653 | d->control = cpu_to_le16(DESCRIPTOR_STATUS | |
2535 | 2654 | DESCRIPTOR_INPUT_MORE); |
2536 | - if (p->skip && i == 0) | |
2655 | + if (packet->skip && i == 0) | |
2537 | 2656 | d->control |= cpu_to_le16(DESCRIPTOR_WAIT); |
2538 | 2657 | d->req_count = cpu_to_le16(header_size); |
2539 | 2658 | d->res_count = d->req_count; |
... | ... | @@ -2566,7 +2685,7 @@ |
2566 | 2685 | pd->control = cpu_to_le16(DESCRIPTOR_STATUS | |
2567 | 2686 | DESCRIPTOR_INPUT_LAST | |
2568 | 2687 | DESCRIPTOR_BRANCH_ALWAYS); |
2569 | - if (p->interrupt && i == packet_count - 1) | |
2688 | + if (packet->interrupt && i == packet_count - 1) | |
2570 | 2689 | pd->control |= cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS); |
2571 | 2690 | |
2572 | 2691 | context_append(&ctx->context, d, z, header_z); |
... | ... | @@ -2575,6 +2694,58 @@ |
2575 | 2694 | return 0; |
2576 | 2695 | } |
2577 | 2696 | |
2697 | +static int queue_iso_buffer_fill(struct iso_context *ctx, | |
2698 | + struct fw_iso_packet *packet, | |
2699 | + struct fw_iso_buffer *buffer, | |
2700 | + unsigned long payload) | |
2701 | +{ | |
2702 | + struct descriptor *d; | |
2703 | + dma_addr_t d_bus, page_bus; | |
2704 | + int page, offset, rest, z, i, length; | |
2705 | + | |
2706 | + page = payload >> PAGE_SHIFT; | |
2707 | + offset = payload & ~PAGE_MASK; | |
2708 | + rest = packet->payload_length; | |
2709 | + | |
2710 | + /* We need one descriptor for each page in the buffer. */ | |
2711 | + z = DIV_ROUND_UP(offset + rest, PAGE_SIZE); | |
2712 | + | |
2713 | + if (WARN_ON(offset & 3 || rest & 3 || page + z > buffer->page_count)) | |
2714 | + return -EFAULT; | |
2715 | + | |
2716 | + for (i = 0; i < z; i++) { | |
2717 | + d = context_get_descriptors(&ctx->context, 1, &d_bus); | |
2718 | + if (d == NULL) | |
2719 | + return -ENOMEM; | |
2720 | + | |
2721 | + d->control = cpu_to_le16(DESCRIPTOR_INPUT_MORE | | |
2722 | + DESCRIPTOR_BRANCH_ALWAYS); | |
2723 | + if (packet->skip && i == 0) | |
2724 | + d->control |= cpu_to_le16(DESCRIPTOR_WAIT); | |
2725 | + if (packet->interrupt && i == z - 1) | |
2726 | + d->control |= cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS); | |
2727 | + | |
2728 | + if (offset + rest < PAGE_SIZE) | |
2729 | + length = rest; | |
2730 | + else | |
2731 | + length = PAGE_SIZE - offset; | |
2732 | + d->req_count = cpu_to_le16(length); | |
2733 | + d->res_count = d->req_count; | |
2734 | + d->transfer_status = 0; | |
2735 | + | |
2736 | + page_bus = page_private(buffer->pages[page]); | |
2737 | + d->data_address = cpu_to_le32(page_bus + offset); | |
2738 | + | |
2739 | + rest -= length; | |
2740 | + offset = 0; | |
2741 | + page++; | |
2742 | + | |
2743 | + context_append(&ctx->context, d, 1, 0); | |
2744 | + } | |
2745 | + | |
2746 | + return 0; | |
2747 | +} | |
2748 | + | |
2578 | 2749 | static int ohci_queue_iso(struct fw_iso_context *base, |
2579 | 2750 | struct fw_iso_packet *packet, |
2580 | 2751 | struct fw_iso_buffer *buffer, |
2581 | 2752 | |
... | ... | @@ -2582,14 +2753,20 @@ |
2582 | 2753 | { |
2583 | 2754 | struct iso_context *ctx = container_of(base, struct iso_context, base); |
2584 | 2755 | unsigned long flags; |
2585 | - int ret; | |
2756 | + int ret = -ENOSYS; | |
2586 | 2757 | |
2587 | 2758 | spin_lock_irqsave(&ctx->context.ohci->lock, flags); |
2588 | - if (base->type == FW_ISO_CONTEXT_TRANSMIT) | |
2589 | - ret = ohci_queue_iso_transmit(base, packet, buffer, payload); | |
2590 | - else | |
2591 | - ret = ohci_queue_iso_receive_packet_per_buffer(base, packet, | |
2592 | - buffer, payload); | |
2759 | + switch (base->type) { | |
2760 | + case FW_ISO_CONTEXT_TRANSMIT: | |
2761 | + ret = queue_iso_transmit(ctx, packet, buffer, payload); | |
2762 | + break; | |
2763 | + case FW_ISO_CONTEXT_RECEIVE: | |
2764 | + ret = queue_iso_packet_per_buffer(ctx, packet, buffer, payload); | |
2765 | + break; | |
2766 | + case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: | |
2767 | + ret = queue_iso_buffer_fill(ctx, packet, buffer, payload); | |
2768 | + break; | |
2769 | + } | |
2593 | 2770 | spin_unlock_irqrestore(&ctx->context.ohci->lock, flags); |
2594 | 2771 | |
2595 | 2772 | return ret; |
... | ... | @@ -2609,6 +2786,7 @@ |
2609 | 2786 | |
2610 | 2787 | .allocate_iso_context = ohci_allocate_iso_context, |
2611 | 2788 | .free_iso_context = ohci_free_iso_context, |
2789 | + .set_iso_channels = ohci_set_iso_channels, | |
2612 | 2790 | .queue_iso = ohci_queue_iso, |
2613 | 2791 | .start_iso = ohci_start_iso, |
2614 | 2792 | .stop_iso = ohci_stop_iso, |
include/linux/firewire-cdev.h
... | ... | @@ -25,17 +25,18 @@ |
25 | 25 | #include <linux/types.h> |
26 | 26 | #include <linux/firewire-constants.h> |
27 | 27 | |
28 | -#define FW_CDEV_EVENT_BUS_RESET 0x00 | |
29 | -#define FW_CDEV_EVENT_RESPONSE 0x01 | |
30 | -#define FW_CDEV_EVENT_REQUEST 0x02 | |
31 | -#define FW_CDEV_EVENT_ISO_INTERRUPT 0x03 | |
32 | -#define FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED 0x04 | |
33 | -#define FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED 0x05 | |
28 | +#define FW_CDEV_EVENT_BUS_RESET 0x00 | |
29 | +#define FW_CDEV_EVENT_RESPONSE 0x01 | |
30 | +#define FW_CDEV_EVENT_REQUEST 0x02 | |
31 | +#define FW_CDEV_EVENT_ISO_INTERRUPT 0x03 | |
32 | +#define FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED 0x04 | |
33 | +#define FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED 0x05 | |
34 | 34 | |
35 | 35 | /* available since kernel version 2.6.36 */ |
36 | -#define FW_CDEV_EVENT_REQUEST2 0x06 | |
37 | -#define FW_CDEV_EVENT_PHY_PACKET_SENT 0x07 | |
38 | -#define FW_CDEV_EVENT_PHY_PACKET_RECEIVED 0x08 | |
36 | +#define FW_CDEV_EVENT_REQUEST2 0x06 | |
37 | +#define FW_CDEV_EVENT_PHY_PACKET_SENT 0x07 | |
38 | +#define FW_CDEV_EVENT_PHY_PACKET_RECEIVED 0x08 | |
39 | +#define FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL 0x09 | |
39 | 40 | |
40 | 41 | /** |
41 | 42 | * struct fw_cdev_event_common - Common part of all fw_cdev_event_ types |
42 | 43 | |
43 | 44 | |
44 | 45 | |
45 | 46 | |
46 | 47 | |
47 | 48 | |
... | ... | @@ -218,35 +219,41 @@ |
218 | 219 | * This event is sent when the controller has completed an &fw_cdev_iso_packet |
219 | 220 | * with the %FW_CDEV_ISO_INTERRUPT bit set. |
220 | 221 | * |
221 | - * Isochronous transmit events: | |
222 | + * Isochronous transmit events (context type %FW_CDEV_ISO_CONTEXT_TRANSMIT): | |
222 | 223 | * |
223 | - * In version 1 of the ABI, &header_length is 0. In version 3 and some | |
224 | - * implementations of version 2 of the ABI, &header_length is a multiple of 4 | |
225 | - * and &header contains timestamps of all packets up until the interrupt packet. | |
226 | - * The format of the timestamps is as described below for isochronous reception. | |
224 | + * In version 3 and some implementations of version 2 of the ABI, &header_length | |
225 | + * is a multiple of 4 and &header contains timestamps of all packets up until | |
226 | + * the interrupt packet. The format of the timestamps is as described below for | |
227 | + * isochronous reception. In version 1 of the ABI, &header_length was 0. | |
227 | 228 | * |
228 | - * Isochronous receive events: | |
229 | + * Isochronous receive events (context type %FW_CDEV_ISO_CONTEXT_RECEIVE): | |
229 | 230 | * |
230 | 231 | * The headers stripped of all packets up until and including the interrupt |
231 | 232 | * packet are returned in the @header field. The amount of header data per |
232 | 233 | * packet is as specified at iso context creation by |
233 | 234 | * &fw_cdev_create_iso_context.header_size. |
234 | 235 | * |
235 | - * In version 1 of this ABI, header data consisted of the 1394 isochronous | |
236 | - * packet header, followed by quadlets from the packet payload if | |
237 | - * &fw_cdev_create_iso_context.header_size > 4. | |
236 | + * Hence, _interrupt.header_length / _context.header_size is the number of | |
237 | + * packets received in this interrupt event. The client can now iterate | |
238 | + * through the mmap()'ed DMA buffer according to this number of packets and | |
239 | + * to the buffer sizes as the client specified in &fw_cdev_queue_iso. | |
238 | 240 | * |
239 | - * In version 2 of this ABI, header data consist of the 1394 isochronous | |
240 | - * packet header, followed by a timestamp quadlet if | |
241 | - * &fw_cdev_create_iso_context.header_size > 4, followed by quadlets from the | |
242 | - * packet payload if &fw_cdev_create_iso_context.header_size > 8. | |
241 | + * Since version 2 of this ABI, the portion for each packet in _interrupt.header | |
242 | + * consists of the 1394 isochronous packet header, followed by a timestamp | |
243 | + * quadlet if &fw_cdev_create_iso_context.header_size > 4, followed by quadlets | |
244 | + * from the packet payload if &fw_cdev_create_iso_context.header_size > 8. | |
243 | 245 | * |
244 | - * Behaviour of ver. 1 of this ABI is no longer available since ABI ver. 2. | |
246 | + * Format of 1394 iso packet header: 16 bits data_length, 2 bits tag, 6 bits | |
247 | + * channel, 4 bits tcode, 4 bits sy, in big endian byte order. | |
248 | + * data_length is the actual received size of the packet without the four | |
249 | + * 1394 iso packet header bytes. | |
245 | 250 | * |
246 | - * Format of 1394 iso packet header: 16 bits len, 2 bits tag, 6 bits channel, | |
247 | - * 4 bits tcode, 4 bits sy, in big endian byte order. Format of timestamp: | |
248 | - * 16 bits invalid, 3 bits cycleSeconds, 13 bits cycleCount, in big endian byte | |
249 | - * order. | |
251 | + * Format of timestamp: 16 bits invalid, 3 bits cycleSeconds, 13 bits | |
252 | + * cycleCount, in big endian byte order. | |
253 | + * | |
254 | + * In version 1 of the ABI, no timestamp quadlet was inserted; instead, payload | |
255 | + * data followed directly after the 1394 is header if header_size > 4. | |
256 | + * Behaviour of ver. 1 of this ABI is no longer available since ABI ver. 2. | |
250 | 257 | */ |
251 | 258 | struct fw_cdev_event_iso_interrupt { |
252 | 259 | __u64 closure; |
... | ... | @@ -257,6 +264,43 @@ |
257 | 264 | }; |
258 | 265 | |
259 | 266 | /** |
267 | + * struct fw_cdev_event_iso_interrupt_mc - An iso buffer chunk was completed | |
268 | + * @closure: See &fw_cdev_event_common; | |
269 | + * set by %FW_CDEV_CREATE_ISO_CONTEXT ioctl | |
270 | + * @type: %FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL | |
271 | + * @completed: Offset into the receive buffer; data before this offest is valid | |
272 | + * | |
273 | + * This event is sent in multichannel contexts (context type | |
274 | + * %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL) for &fw_cdev_iso_packet buffer | |
275 | + * chunks that have the %FW_CDEV_ISO_INTERRUPT bit set. Whether this happens | |
276 | + * when a packet is completed and/or when a buffer chunk is completed depends | |
277 | + * on the hardware implementation. | |
278 | + * | |
279 | + * The buffer is continuously filled with the following data, per packet: | |
280 | + * - the 1394 iso packet header as described at &fw_cdev_event_iso_interrupt, | |
281 | + * but in little endian byte order, | |
282 | + * - packet payload (as many bytes as specified in the data_length field of | |
283 | + * the 1394 iso packet header) in big endian byte order, | |
284 | + * - 0...3 padding bytes as needed to align the following trailer quadlet, | |
285 | + * - trailer quadlet, containing the reception timestamp as described at | |
286 | + * &fw_cdev_event_iso_interrupt, but in little endian byte order. | |
287 | + * | |
288 | + * Hence the per-packet size is data_length (rounded up to a multiple of 4) + 8. | |
289 | + * When processing the data, stop before a packet that would cross the | |
290 | + * @completed offset. | |
291 | + * | |
292 | + * A packet near the end of a buffer chunk will typically spill over into the | |
293 | + * next queued buffer chunk. It is the responsibility of the client to check | |
294 | + * for this condition, assemble a broken-up packet from its parts, and not to | |
295 | + * re-queue any buffer chunks in which as yet unread packet parts reside. | |
296 | + */ | |
297 | +struct fw_cdev_event_iso_interrupt_mc { | |
298 | + __u64 closure; | |
299 | + __u32 type; | |
300 | + __u32 completed; | |
301 | +}; | |
302 | + | |
303 | +/** | |
260 | 304 | * struct fw_cdev_event_iso_resource - Iso resources were allocated or freed |
261 | 305 | * @closure: See &fw_cdev_event_common; |
262 | 306 | * set by %FW_CDEV_IOC_(DE)ALLOCATE_ISO_RESOURCE(_ONCE) ioctl |
263 | 307 | |
... | ... | @@ -311,16 +355,18 @@ |
311 | 355 | |
312 | 356 | /** |
313 | 357 | * union fw_cdev_event - Convenience union of fw_cdev_event_ types |
314 | - * @common: Valid for all types | |
315 | - * @bus_reset: Valid if @common.type == %FW_CDEV_EVENT_BUS_RESET | |
316 | - * @response: Valid if @common.type == %FW_CDEV_EVENT_RESPONSE | |
317 | - * @request: Valid if @common.type == %FW_CDEV_EVENT_REQUEST | |
318 | - * @request2: Valid if @common.type == %FW_CDEV_EVENT_REQUEST2 | |
319 | - * @iso_interrupt: Valid if @common.type == %FW_CDEV_EVENT_ISO_INTERRUPT | |
320 | - * @iso_resource: Valid if @common.type == | |
358 | + * @common: Valid for all types | |
359 | + * @bus_reset: Valid if @common.type == %FW_CDEV_EVENT_BUS_RESET | |
360 | + * @response: Valid if @common.type == %FW_CDEV_EVENT_RESPONSE | |
361 | + * @request: Valid if @common.type == %FW_CDEV_EVENT_REQUEST | |
362 | + * @request2: Valid if @common.type == %FW_CDEV_EVENT_REQUEST2 | |
363 | + * @iso_interrupt: Valid if @common.type == %FW_CDEV_EVENT_ISO_INTERRUPT | |
364 | + * @iso_interrupt_mc: Valid if @common.type == | |
365 | + * %FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL | |
366 | + * @iso_resource: Valid if @common.type == | |
321 | 367 | * %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or |
322 | 368 | * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED |
323 | - * @phy_packet: Valid if @common.type == | |
369 | + * @phy_packet: Valid if @common.type == | |
324 | 370 | * %FW_CDEV_EVENT_PHY_PACKET_SENT or |
325 | 371 | * %FW_CDEV_EVENT_PHY_PACKET_RECEIVED |
326 | 372 | * |
327 | 373 | |
... | ... | @@ -337,10 +383,11 @@ |
337 | 383 | struct fw_cdev_event_bus_reset bus_reset; |
338 | 384 | struct fw_cdev_event_response response; |
339 | 385 | struct fw_cdev_event_request request; |
340 | - struct fw_cdev_event_request2 request2; /* added in 2.6.36 */ | |
386 | + struct fw_cdev_event_request2 request2; /* added in 2.6.36 */ | |
341 | 387 | struct fw_cdev_event_iso_interrupt iso_interrupt; |
342 | - struct fw_cdev_event_iso_resource iso_resource; /* added in 2.6.30 */ | |
343 | - struct fw_cdev_event_phy_packet phy_packet; /* added in 2.6.36 */ | |
388 | + struct fw_cdev_event_iso_interrupt_mc iso_interrupt_mc; /* added in 2.6.36 */ | |
389 | + struct fw_cdev_event_iso_resource iso_resource; /* added in 2.6.30 */ | |
390 | + struct fw_cdev_event_phy_packet phy_packet; /* added in 2.6.36 */ | |
344 | 391 | }; |
345 | 392 | |
346 | 393 | /* available since kernel version 2.6.22 */ |
... | ... | @@ -375,6 +422,7 @@ |
375 | 422 | /* available since kernel version 2.6.36 */ |
376 | 423 | #define FW_CDEV_IOC_SEND_PHY_PACKET _IOWR('#', 0x15, struct fw_cdev_send_phy_packet) |
377 | 424 | #define FW_CDEV_IOC_RECEIVE_PHY_PACKETS _IOW('#', 0x16, struct fw_cdev_receive_phy_packets) |
425 | +#define FW_CDEV_IOC_SET_ISO_CHANNELS _IOW('#', 0x17, struct fw_cdev_set_iso_channels) | |
378 | 426 | |
379 | 427 | /* |
380 | 428 | * ABI version history |
381 | 429 | |
... | ... | @@ -391,10 +439,13 @@ |
391 | 439 | * - shared use and auto-response for FCP registers |
392 | 440 | * 3 (2.6.34) - made &fw_cdev_get_cycle_timer reliable |
393 | 441 | * - added %FW_CDEV_IOC_GET_CYCLE_TIMER2 |
394 | - * 4 (2.6.36) - added %FW_CDEV_EVENT_REQUEST2, %FW_CDEV_EVENT_PHY_PACKET_* | |
442 | + * 4 (2.6.36) - added %FW_CDEV_EVENT_REQUEST2, %FW_CDEV_EVENT_PHY_PACKET_*, | |
443 | + * and &fw_cdev_allocate.region_end | |
395 | 444 | * - implemented &fw_cdev_event_bus_reset.bm_node_id |
396 | 445 | * - added %FW_CDEV_IOC_SEND_PHY_PACKET, _RECEIVE_PHY_PACKETS |
397 | - * - added &fw_cdev_allocate.region_end | |
446 | + * - added %FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL, | |
447 | + * %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL, and | |
448 | + * %FW_CDEV_IOC_SET_ISO_CHANNELS | |
398 | 449 | */ |
399 | 450 | #define FW_CDEV_VERSION 3 /* Meaningless; don't use this macro. */ |
400 | 451 | |
401 | 452 | |
402 | 453 | |
403 | 454 | |
404 | 455 | |
405 | 456 | |
406 | 457 | |
407 | 458 | |
... | ... | @@ -597,34 +648,43 @@ |
597 | 648 | __u32 handle; |
598 | 649 | }; |
599 | 650 | |
600 | -#define FW_CDEV_ISO_CONTEXT_TRANSMIT 0 | |
601 | -#define FW_CDEV_ISO_CONTEXT_RECEIVE 1 | |
651 | +#define FW_CDEV_ISO_CONTEXT_TRANSMIT 0 | |
652 | +#define FW_CDEV_ISO_CONTEXT_RECEIVE 1 | |
653 | +#define FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL 2 /* added in 2.6.36 */ | |
602 | 654 | |
603 | 655 | /** |
604 | - * struct fw_cdev_create_iso_context - Create a context for isochronous IO | |
605 | - * @type: %FW_CDEV_ISO_CONTEXT_TRANSMIT or %FW_CDEV_ISO_CONTEXT_RECEIVE | |
606 | - * @header_size: Header size to strip for receive contexts | |
607 | - * @channel: Channel to bind to | |
608 | - * @speed: Speed for transmit contexts | |
609 | - * @closure: To be returned in &fw_cdev_event_iso_interrupt | |
656 | + * struct fw_cdev_create_iso_context - Create a context for isochronous I/O | |
657 | + * @type: %FW_CDEV_ISO_CONTEXT_TRANSMIT or %FW_CDEV_ISO_CONTEXT_RECEIVE or | |
658 | + * %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL | |
659 | + * @header_size: Header size to strip in single-channel reception | |
660 | + * @channel: Channel to bind to in single-channel reception or transmission | |
661 | + * @speed: Transmission speed | |
662 | + * @closure: To be returned in &fw_cdev_event_iso_interrupt or | |
663 | + * &fw_cdev_event_iso_interrupt_multichannel | |
610 | 664 | * @handle: Handle to context, written back by kernel |
611 | 665 | * |
612 | 666 | * Prior to sending or receiving isochronous I/O, a context must be created. |
613 | 667 | * The context records information about the transmit or receive configuration |
614 | 668 | * and typically maps to an underlying hardware resource. A context is set up |
615 | 669 | * for either sending or receiving. It is bound to a specific isochronous |
616 | - * channel. | |
670 | + * @channel. | |
617 | 671 | * |
618 | - * If a context was successfully created, the kernel writes back a handle to the | |
619 | - * context, which must be passed in for subsequent operations on that context. | |
672 | + * In case of multichannel reception, @header_size and @channel are ignored | |
673 | + * and the channels are selected by %FW_CDEV_IOC_SET_ISO_CHANNELS. | |
620 | 674 | * |
621 | - * For receive contexts, @header_size must be at least 4 and must be a multiple | |
622 | - * of 4. | |
675 | + * For %FW_CDEV_ISO_CONTEXT_RECEIVE contexts, @header_size must be at least 4 | |
676 | + * and must be a multiple of 4. It is ignored in other context types. | |
623 | 677 | * |
624 | - * Note that the effect of a @header_size > 4 depends on | |
625 | - * &fw_cdev_get_info.version, as documented at &fw_cdev_event_iso_interrupt. | |
678 | + * @speed is ignored in receive context types. | |
626 | 679 | * |
680 | + * If a context was successfully created, the kernel writes back a handle to the | |
681 | + * context, which must be passed in for subsequent operations on that context. | |
682 | + * | |
683 | + * Limitations: | |
627 | 684 | * No more than one iso context can be created per fd. |
685 | + * The total number of contexts that all userspace and kernelspace drivers can | |
686 | + * create on a card at a time is a hardware limit, typically 4 or 8 contexts per | |
687 | + * direction, and of them at most one multichannel receive context. | |
628 | 688 | */ |
629 | 689 | struct fw_cdev_create_iso_context { |
630 | 690 | __u32 type; |
... | ... | @@ -635,6 +695,22 @@ |
635 | 695 | __u32 handle; |
636 | 696 | }; |
637 | 697 | |
698 | +/** | |
699 | + * struct fw_cdev_set_iso_channels - Select channels in multichannel reception | |
700 | + * @channels: Bitmask of channels to listen to | |
701 | + * @handle: Handle of the mutichannel receive context | |
702 | + * | |
703 | + * @channels is the bitwise or of 1ULL << n for each channel n to listen to. | |
704 | + * | |
705 | + * The ioctl fails with errno %EBUSY if there is already another receive context | |
706 | + * on a channel in @channels. In that case, the bitmask of all unoccupied | |
707 | + * channels is returned in @channels. | |
708 | + */ | |
709 | +struct fw_cdev_set_iso_channels { | |
710 | + __u64 channels; | |
711 | + __u32 handle; | |
712 | +}; | |
713 | + | |
638 | 714 | #define FW_CDEV_ISO_PAYLOAD_LENGTH(v) (v) |
639 | 715 | #define FW_CDEV_ISO_INTERRUPT (1 << 16) |
640 | 716 | #define FW_CDEV_ISO_SKIP (1 << 17) |
641 | 717 | |
642 | 718 | |
643 | 719 | |
644 | 720 | |
645 | 721 | |
646 | 722 | |
647 | 723 | |
... | ... | @@ -645,42 +721,72 @@ |
645 | 721 | |
646 | 722 | /** |
647 | 723 | * struct fw_cdev_iso_packet - Isochronous packet |
648 | - * @control: Contains the header length (8 uppermost bits), the sy field | |
649 | - * (4 bits), the tag field (2 bits), a sync flag (1 bit), | |
650 | - * a skip flag (1 bit), an interrupt flag (1 bit), and the | |
724 | + * @control: Contains the header length (8 uppermost bits), | |
725 | + * the sy field (4 bits), the tag field (2 bits), a sync flag | |
726 | + * or a skip flag (1 bit), an interrupt flag (1 bit), and the | |
651 | 727 | * payload length (16 lowermost bits) |
652 | - * @header: Header and payload | |
728 | + * @header: Header and payload in case of a transmit context. | |
653 | 729 | * |
654 | 730 | * &struct fw_cdev_iso_packet is used to describe isochronous packet queues. |
655 | - * | |
656 | 731 | * Use the FW_CDEV_ISO_ macros to fill in @control. |
732 | + * The @header array is empty in case of receive contexts. | |
657 | 733 | * |
658 | - * For transmit packets, the header length must be a multiple of 4 and specifies | |
659 | - * the numbers of bytes in @header that will be prepended to the packet's | |
660 | - * payload; these bytes are copied into the kernel and will not be accessed | |
661 | - * after the ioctl has returned. The sy and tag fields are copied to the iso | |
662 | - * packet header (these fields are specified by IEEE 1394a and IEC 61883-1). | |
663 | - * The skip flag specifies that no packet is to be sent in a frame; when using | |
664 | - * this, all other fields except the interrupt flag must be zero. | |
734 | + * Context type %FW_CDEV_ISO_CONTEXT_TRANSMIT: | |
665 | 735 | * |
666 | - * For receive packets, the header length must be a multiple of the context's | |
667 | - * header size; if the header length is larger than the context's header size, | |
668 | - * multiple packets are queued for this entry. The sy and tag fields are | |
669 | - * ignored. If the sync flag is set, the context drops all packets until | |
670 | - * a packet with a matching sy field is received (the sync value to wait for is | |
671 | - * specified in the &fw_cdev_start_iso structure). The payload length defines | |
672 | - * how many payload bytes can be received for one packet (in addition to payload | |
673 | - * quadlets that have been defined as headers and are stripped and returned in | |
674 | - * the &fw_cdev_event_iso_interrupt structure). If more bytes are received, the | |
675 | - * additional bytes are dropped. If less bytes are received, the remaining | |
676 | - * bytes in this part of the payload buffer will not be written to, not even by | |
677 | - * the next packet, i.e., packets received in consecutive frames will not | |
678 | - * necessarily be consecutive in memory. If an entry has queued multiple | |
679 | - * packets, the payload length is divided equally among them. | |
736 | + * @control.HEADER_LENGTH must be a multiple of 4. It specifies the numbers of | |
737 | + * bytes in @header that will be prepended to the packet's payload. These bytes | |
738 | + * are copied into the kernel and will not be accessed after the ioctl has | |
739 | + * returned. | |
680 | 740 | * |
681 | - * When a packet with the interrupt flag set has been completed, the | |
741 | + * The @control.SY and TAG fields are copied to the iso packet header. These | |
742 | + * fields are specified by IEEE 1394a and IEC 61883-1. | |
743 | + * | |
744 | + * The @control.SKIP flag specifies that no packet is to be sent in a frame. | |
745 | + * When using this, all other fields except @control.INTERRUPT must be zero. | |
746 | + * | |
747 | + * When a packet with the @control.INTERRUPT flag set has been completed, an | |
748 | + * &fw_cdev_event_iso_interrupt event will be sent. | |
749 | + * | |
750 | + * Context type %FW_CDEV_ISO_CONTEXT_RECEIVE: | |
751 | + * | |
752 | + * @control.HEADER_LENGTH must be a multiple of the context's header_size. | |
753 | + * If the HEADER_LENGTH is larger than the context's header_size, multiple | |
754 | + * packets are queued for this entry. | |
755 | + * | |
756 | + * The @control.SY and TAG fields are ignored. | |
757 | + * | |
758 | + * If the @control.SYNC flag is set, the context drops all packets until a | |
759 | + * packet with a sy field is received which matches &fw_cdev_start_iso.sync. | |
760 | + * | |
761 | + * @control.PAYLOAD_LENGTH defines how many payload bytes can be received for | |
762 | + * one packet (in addition to payload quadlets that have been defined as headers | |
763 | + * and are stripped and returned in the &fw_cdev_event_iso_interrupt structure). | |
764 | + * If more bytes are received, the additional bytes are dropped. If less bytes | |
765 | + * are received, the remaining bytes in this part of the payload buffer will not | |
766 | + * be written to, not even by the next packet. I.e., packets received in | |
767 | + * consecutive frames will not necessarily be consecutive in memory. If an | |
768 | + * entry has queued multiple packets, the PAYLOAD_LENGTH is divided equally | |
769 | + * among them. | |
770 | + * | |
771 | + * When a packet with the @control.INTERRUPT flag set has been completed, an | |
682 | 772 | * &fw_cdev_event_iso_interrupt event will be sent. An entry that has queued |
683 | 773 | * multiple receive packets is completed when its last packet is completed. |
774 | + * | |
775 | + * Context type %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL: | |
776 | + * | |
777 | + * Here, &fw_cdev_iso_packet would be more aptly named _iso_buffer_chunk since | |
778 | + * it specifies a chunk of the mmap()'ed buffer, while the number and alignment | |
779 | + * of packets to be placed into the buffer chunk is not known beforehand. | |
780 | + * | |
781 | + * @control.PAYLOAD_LENGTH is the size of the buffer chunk and specifies room | |
782 | + * for header, payload, padding, and trailer bytes of one or more packets. | |
783 | + * It must be a multiple of 4. | |
784 | + * | |
785 | + * @control.HEADER_LENGTH, TAG and SY are ignored. SYNC is treated as described | |
786 | + * for single-channel reception. | |
787 | + * | |
788 | + * When a buffer chunk with the @control.INTERRUPT flag set has been filled | |
789 | + * entirely, an &fw_cdev_event_iso_interrupt_mc event will be sent. | |
684 | 790 | */ |
685 | 791 | struct fw_cdev_iso_packet { |
686 | 792 | __u32 control; |
687 | 793 | |
... | ... | @@ -689,9 +795,9 @@ |
689 | 795 | |
690 | 796 | /** |
691 | 797 | * struct fw_cdev_queue_iso - Queue isochronous packets for I/O |
692 | - * @packets: Userspace pointer to packet data | |
798 | + * @packets: Userspace pointer to an array of &fw_cdev_iso_packet | |
693 | 799 | * @data: Pointer into mmap()'ed payload buffer |
694 | - * @size: Size of packet data in bytes | |
800 | + * @size: Size of the @packets array, in bytes | |
695 | 801 | * @handle: Isochronous context handle |
696 | 802 | * |
697 | 803 | * Queue a number of isochronous packets for reception or transmission. |
... | ... | @@ -704,6 +810,9 @@ |
704 | 810 | * The kernel may or may not queue all packets, but will write back updated |
705 | 811 | * values of the @packets, @data and @size fields, so the ioctl can be |
706 | 812 | * resubmitted easily. |
813 | + * | |
814 | + * In case of a multichannel receive context, @data must be quadlet-aligned | |
815 | + * relative to the buffer start. | |
707 | 816 | */ |
708 | 817 | struct fw_cdev_queue_iso { |
709 | 818 | __u64 packets; |
include/linux/firewire.h
... | ... | @@ -372,17 +372,19 @@ |
372 | 372 | * scatter-gather streaming (e.g. assembling video frame automatically). |
373 | 373 | */ |
374 | 374 | struct fw_iso_packet { |
375 | - u16 payload_length; /* Length of indirect payload. */ | |
376 | - u32 interrupt:1; /* Generate interrupt on this packet */ | |
377 | - u32 skip:1; /* Set to not send packet at all. */ | |
378 | - u32 tag:2; | |
379 | - u32 sy:4; | |
380 | - u32 header_length:8; /* Length of immediate header. */ | |
381 | - u32 header[0]; | |
375 | + u16 payload_length; /* Length of indirect payload */ | |
376 | + u32 interrupt:1; /* Generate interrupt on this packet */ | |
377 | + u32 skip:1; /* tx: Set to not send packet at all */ | |
378 | + /* rx: Sync bit, wait for matching sy */ | |
379 | + u32 tag:2; /* tx: Tag in packet header */ | |
380 | + u32 sy:4; /* tx: Sy in packet header */ | |
381 | + u32 header_length:8; /* Length of immediate header */ | |
382 | + u32 header[0]; /* tx: Top of 1394 isoch. data_block */ | |
382 | 383 | }; |
383 | 384 | |
384 | -#define FW_ISO_CONTEXT_TRANSMIT 0 | |
385 | -#define FW_ISO_CONTEXT_RECEIVE 1 | |
385 | +#define FW_ISO_CONTEXT_TRANSMIT 0 | |
386 | +#define FW_ISO_CONTEXT_RECEIVE 1 | |
387 | +#define FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL 2 | |
386 | 388 | |
387 | 389 | #define FW_ISO_CONTEXT_MATCH_TAG0 1 |
388 | 390 | #define FW_ISO_CONTEXT_MATCH_TAG1 2 |
389 | 391 | |
390 | 392 | |
391 | 393 | |
... | ... | @@ -406,24 +408,31 @@ |
406 | 408 | int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, |
407 | 409 | int page_count, enum dma_data_direction direction); |
408 | 410 | void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card); |
411 | +size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed); | |
409 | 412 | |
410 | 413 | struct fw_iso_context; |
411 | 414 | typedef void (*fw_iso_callback_t)(struct fw_iso_context *context, |
412 | 415 | u32 cycle, size_t header_length, |
413 | 416 | void *header, void *data); |
417 | +typedef void (*fw_iso_mc_callback_t)(struct fw_iso_context *context, | |
418 | + dma_addr_t completed, void *data); | |
414 | 419 | struct fw_iso_context { |
415 | 420 | struct fw_card *card; |
416 | 421 | int type; |
417 | 422 | int channel; |
418 | 423 | int speed; |
419 | 424 | size_t header_size; |
420 | - fw_iso_callback_t callback; | |
425 | + union { | |
426 | + fw_iso_callback_t sc; | |
427 | + fw_iso_mc_callback_t mc; | |
428 | + } callback; | |
421 | 429 | void *callback_data; |
422 | 430 | }; |
423 | 431 | |
424 | 432 | struct fw_iso_context *fw_iso_context_create(struct fw_card *card, |
425 | 433 | int type, int channel, int speed, size_t header_size, |
426 | 434 | fw_iso_callback_t callback, void *callback_data); |
435 | +int fw_iso_context_set_channels(struct fw_iso_context *ctx, u64 *channels); | |
427 | 436 | int fw_iso_context_queue(struct fw_iso_context *ctx, |
428 | 437 | struct fw_iso_packet *packet, |
429 | 438 | struct fw_iso_buffer *buffer, |