Commit f8c2287c65f8f72000102fc058232669e4540bc4
Committed by
Stefan Richter
1 parent
ba27e1f7bf
Exists in
master
and in
7 other branches
firewire: implement asynchronous stream transmission
Allow userspace and other firewire drivers (fw-ipv4 I'm looking at you!) to send Asynchronous Transmit Streams as described in 7.8.3 of release 1.1 of the 1394 Open Host Controller Interface Specification. Signed-off-by: Jay Fenlason <fenlason@redhat.com> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (tweaks)
Showing 5 changed files with 108 additions and 2 deletions Side-by-side Diff
drivers/firewire/fw-cdev.c
... | ... | @@ -1242,6 +1242,38 @@ |
1242 | 1242 | return init_request(client, request, LOCAL_BUS | 0x3f, SCODE_100); |
1243 | 1243 | } |
1244 | 1244 | |
1245 | +struct stream_packet { | |
1246 | + struct fw_packet packet; | |
1247 | + u8 data[0]; | |
1248 | +}; | |
1249 | + | |
1250 | +static void send_stream_packet_done(struct fw_packet *packet, | |
1251 | + struct fw_card *card, int status) | |
1252 | +{ | |
1253 | + kfree(container_of(packet, struct stream_packet, packet)); | |
1254 | +} | |
1255 | + | |
1256 | +static int ioctl_send_stream_packet(struct client *client, void *buffer) | |
1257 | +{ | |
1258 | + struct fw_cdev_send_stream_packet *request = buffer; | |
1259 | + struct stream_packet *p; | |
1260 | + | |
1261 | + p = kmalloc(sizeof(*p) + request->size, GFP_KERNEL); | |
1262 | + if (p == NULL) | |
1263 | + return -ENOMEM; | |
1264 | + | |
1265 | + if (request->data && | |
1266 | + copy_from_user(p->data, u64_to_uptr(request->data), request->size)) { | |
1267 | + kfree(p); | |
1268 | + return -EFAULT; | |
1269 | + } | |
1270 | + fw_send_stream_packet(client->device->card, &p->packet, | |
1271 | + request->generation, request->speed, | |
1272 | + request->channel, request->sy, request->tag, | |
1273 | + p->data, request->size, send_stream_packet_done); | |
1274 | + return 0; | |
1275 | +} | |
1276 | + | |
1245 | 1277 | static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { |
1246 | 1278 | ioctl_get_info, |
1247 | 1279 | ioctl_send_request, |
... | ... | @@ -1262,6 +1294,7 @@ |
1262 | 1294 | ioctl_deallocate_iso_resource_once, |
1263 | 1295 | ioctl_get_speed, |
1264 | 1296 | ioctl_send_broadcast_request, |
1297 | + ioctl_send_stream_packet, | |
1265 | 1298 | }; |
1266 | 1299 | |
1267 | 1300 | static int dispatch_ioctl(struct client *client, |
drivers/firewire/fw-ohci.c
... | ... | @@ -936,7 +936,9 @@ |
936 | 936 | */ |
937 | 937 | |
938 | 938 | header = (__le32 *) &d[1]; |
939 | - if (packet->header_length > 8) { | |
939 | + switch (packet->header_length) { | |
940 | + case 16: | |
941 | + case 12: | |
940 | 942 | header[0] = cpu_to_le32((packet->header[0] & 0xffff) | |
941 | 943 | (packet->speed << 16)); |
942 | 944 | header[1] = cpu_to_le32((packet->header[1] & 0xffff) | |
943 | 945 | |
... | ... | @@ -950,12 +952,27 @@ |
950 | 952 | header[3] = (__force __le32) packet->header[3]; |
951 | 953 | |
952 | 954 | d[0].req_count = cpu_to_le16(packet->header_length); |
953 | - } else { | |
955 | + break; | |
956 | + | |
957 | + case 8: | |
954 | 958 | header[0] = cpu_to_le32((OHCI1394_phy_tcode << 4) | |
955 | 959 | (packet->speed << 16)); |
956 | 960 | header[1] = cpu_to_le32(packet->header[0]); |
957 | 961 | header[2] = cpu_to_le32(packet->header[1]); |
958 | 962 | d[0].req_count = cpu_to_le16(12); |
963 | + break; | |
964 | + | |
965 | + case 4: | |
966 | + header[0] = cpu_to_le32((packet->header[0] & 0xffff) | | |
967 | + (packet->speed << 16)); | |
968 | + header[1] = cpu_to_le32(packet->header[0] & 0xffff0000); | |
969 | + d[0].req_count = cpu_to_le16(8); | |
970 | + break; | |
971 | + | |
972 | + default: | |
973 | + /* BUG(); */ | |
974 | + packet->ack = RCODE_SEND_ERROR; | |
975 | + return -1; | |
959 | 976 | } |
960 | 977 | |
961 | 978 | driver_data = (struct driver_data *) &d[3]; |
drivers/firewire/fw-transaction.c
... | ... | @@ -37,6 +37,10 @@ |
37 | 37 | #include "fw-topology.h" |
38 | 38 | #include "fw-device.h" |
39 | 39 | |
40 | +#define HEADER_TAG(tag) ((tag) << 14) | |
41 | +#define HEADER_CHANNEL(ch) ((ch) << 8) | |
42 | +#define HEADER_SY(sy) ((sy) << 0) | |
43 | + | |
40 | 44 | #define HEADER_PRI(pri) ((pri) << 0) |
41 | 45 | #define HEADER_TCODE(tcode) ((tcode) << 4) |
42 | 46 | #define HEADER_RETRY(retry) ((retry) << 8) |
... | ... | @@ -292,6 +296,27 @@ |
292 | 296 | card->driver->send_request(card, &t->packet); |
293 | 297 | } |
294 | 298 | EXPORT_SYMBOL(fw_send_request); |
299 | + | |
300 | +void fw_send_stream_packet(struct fw_card *card, struct fw_packet *p, | |
301 | + int generation, int speed, int channel, int sy, int tag, | |
302 | + void *payload, size_t length, fw_packet_callback_t callback) | |
303 | +{ | |
304 | + p->callback = callback; | |
305 | + p->header[0] = | |
306 | + HEADER_DATA_LENGTH(length) | |
307 | + | HEADER_TAG(tag) | |
308 | + | HEADER_CHANNEL(channel) | |
309 | + | HEADER_TCODE(TCODE_STREAM_DATA) | |
310 | + | HEADER_SY(sy); | |
311 | + p->header_length = 4; | |
312 | + p->payload = payload; | |
313 | + p->payload_length = length; | |
314 | + p->speed = speed; | |
315 | + p->generation = generation; | |
316 | + p->ack = 0; | |
317 | + | |
318 | + card->driver->send_request(card, p); | |
319 | +} | |
295 | 320 | |
296 | 321 | struct transaction_callback_data { |
297 | 322 | struct completion done; |
drivers/firewire/fw-transaction.h
... | ... | @@ -407,6 +407,10 @@ |
407 | 407 | int tcode, int destination_id, int generation, int speed, |
408 | 408 | unsigned long long offset, void *payload, size_t length, |
409 | 409 | fw_transaction_callback_t callback, void *callback_data); |
410 | +void fw_send_stream_packet(struct fw_card *card, struct fw_packet *p, | |
411 | + int generation, int speed, int channel, int sy, int tag, | |
412 | + void *payload, size_t length, fw_packet_callback_t callback); | |
413 | + | |
410 | 414 | int fw_cancel_transaction(struct fw_card *card, |
411 | 415 | struct fw_transaction *transaction); |
412 | 416 | void fw_flush_transactions(struct fw_card *card); |
include/linux/firewire-cdev.h
... | ... | @@ -246,6 +246,7 @@ |
246 | 246 | #define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x10, struct fw_cdev_allocate_iso_resource) |
247 | 247 | #define FW_CDEV_IOC_GET_SPEED _IOR('#', 0x11, struct fw_cdev_get_speed) |
248 | 248 | #define FW_CDEV_IOC_SEND_BROADCAST_REQUEST _IOW('#', 0x12, struct fw_cdev_send_request) |
249 | +#define FW_CDEV_IOC_SEND_STREAM_PACKET _IOW('#', 0x13, struct fw_cdev_send_stream_packet) | |
249 | 250 | |
250 | 251 | /* |
251 | 252 | * FW_CDEV_VERSION History |
... | ... | @@ -607,6 +608,32 @@ |
607 | 608 | */ |
608 | 609 | struct fw_cdev_get_speed { |
609 | 610 | __u32 max_speed; |
611 | +}; | |
612 | + | |
613 | +/** | |
614 | + * struct fw_cdev_send_stream_packet - send an asynchronous stream packet | |
615 | + * @generation: Bus generation where the packet is valid | |
616 | + * @speed: Speed code to send the packet at | |
617 | + * @channel: Channel to send the packet on | |
618 | + * @sy: Four-bit sy code for the packet | |
619 | + * @tag: Two-bit tag field to use for the packet | |
620 | + * @size: Size of the packet's data payload | |
621 | + * @data: Userspace pointer to the payload | |
622 | + * | |
623 | + * The %FW_CDEV_IOC_SEND_STREAM_PACKET ioctl sends an asynchronous stream packet | |
624 | + * to every device (that is listening to the specified channel) on the | |
625 | + * firewire bus. It is the applications's job to ensure | |
626 | + * that the intended device(s) will be able to receive the packet at the chosen | |
627 | + * transmit speed. | |
628 | + */ | |
629 | +struct fw_cdev_send_stream_packet { | |
630 | + __u32 generation; | |
631 | + __u32 speed; | |
632 | + __u32 channel; | |
633 | + __u32 sy; | |
634 | + __u32 tag; | |
635 | + __u32 size; | |
636 | + __u64 data; | |
610 | 637 | }; |
611 | 638 | |
612 | 639 | #endif /* _LINUX_FIREWIRE_CDEV_H */ |