diff options
author | Jay Fenlason <fenlason@redhat.com> | 2009-03-05 13:08:40 -0500 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2009-03-24 15:56:49 -0400 |
commit | f8c2287c65f8f72000102fc058232669e4540bc4 (patch) | |
tree | a82393fbdd3a2b20f8e499537b10eb9e61dae941 | |
parent | ba27e1f7bf220799cd3d7503f82bda71b8ebe8c5 (diff) |
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)
-rw-r--r-- | drivers/firewire/fw-cdev.c | 33 | ||||
-rw-r--r-- | drivers/firewire/fw-ohci.c | 21 | ||||
-rw-r--r-- | drivers/firewire/fw-transaction.c | 25 | ||||
-rw-r--r-- | drivers/firewire/fw-transaction.h | 4 | ||||
-rw-r--r-- | include/linux/firewire-cdev.h | 27 |
5 files changed, 108 insertions, 2 deletions
diff --git a/drivers/firewire/fw-cdev.c b/drivers/firewire/fw-cdev.c index 214e534efee5..539dae5eb5b2 100644 --- a/drivers/firewire/fw-cdev.c +++ b/drivers/firewire/fw-cdev.c | |||
@@ -1242,6 +1242,38 @@ static int ioctl_send_broadcast_request(struct client *client, void *buffer) | |||
1242 | return init_request(client, request, LOCAL_BUS | 0x3f, SCODE_100); | 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 | static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { | 1277 | static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { |
1246 | ioctl_get_info, | 1278 | ioctl_get_info, |
1247 | ioctl_send_request, | 1279 | ioctl_send_request, |
@@ -1262,6 +1294,7 @@ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { | |||
1262 | ioctl_deallocate_iso_resource_once, | 1294 | ioctl_deallocate_iso_resource_once, |
1263 | ioctl_get_speed, | 1295 | ioctl_get_speed, |
1264 | ioctl_send_broadcast_request, | 1296 | ioctl_send_broadcast_request, |
1297 | ioctl_send_stream_packet, | ||
1265 | }; | 1298 | }; |
1266 | 1299 | ||
1267 | static int dispatch_ioctl(struct client *client, | 1300 | static int dispatch_ioctl(struct client *client, |
diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index c92278374658..1180d0be0bb4 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c | |||
@@ -936,7 +936,9 @@ static int at_context_queue_packet(struct context *ctx, | |||
936 | */ | 936 | */ |
937 | 937 | ||
938 | header = (__le32 *) &d[1]; | 938 | header = (__le32 *) &d[1]; |
939 | if (packet->header_length > 8) { | 939 | switch (packet->header_length) { |
940 | case 16: | ||
941 | case 12: | ||
940 | header[0] = cpu_to_le32((packet->header[0] & 0xffff) | | 942 | header[0] = cpu_to_le32((packet->header[0] & 0xffff) | |
941 | (packet->speed << 16)); | 943 | (packet->speed << 16)); |
942 | header[1] = cpu_to_le32((packet->header[1] & 0xffff) | | 944 | header[1] = cpu_to_le32((packet->header[1] & 0xffff) | |
@@ -950,12 +952,27 @@ static int at_context_queue_packet(struct context *ctx, | |||
950 | header[3] = (__force __le32) packet->header[3]; | 952 | header[3] = (__force __le32) packet->header[3]; |
951 | 953 | ||
952 | d[0].req_count = cpu_to_le16(packet->header_length); | 954 | d[0].req_count = cpu_to_le16(packet->header_length); |
953 | } else { | 955 | break; |
956 | |||
957 | case 8: | ||
954 | header[0] = cpu_to_le32((OHCI1394_phy_tcode << 4) | | 958 | header[0] = cpu_to_le32((OHCI1394_phy_tcode << 4) | |
955 | (packet->speed << 16)); | 959 | (packet->speed << 16)); |
956 | header[1] = cpu_to_le32(packet->header[0]); | 960 | header[1] = cpu_to_le32(packet->header[0]); |
957 | header[2] = cpu_to_le32(packet->header[1]); | 961 | header[2] = cpu_to_le32(packet->header[1]); |
958 | d[0].req_count = cpu_to_le16(12); | 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 | driver_data = (struct driver_data *) &d[3]; | 978 | driver_data = (struct driver_data *) &d[3]; |
diff --git a/drivers/firewire/fw-transaction.c b/drivers/firewire/fw-transaction.c index 76938fe432a0..e3da58991960 100644 --- a/drivers/firewire/fw-transaction.c +++ b/drivers/firewire/fw-transaction.c | |||
@@ -37,6 +37,10 @@ | |||
37 | #include "fw-topology.h" | 37 | #include "fw-topology.h" |
38 | #include "fw-device.h" | 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 | #define HEADER_PRI(pri) ((pri) << 0) | 44 | #define HEADER_PRI(pri) ((pri) << 0) |
41 | #define HEADER_TCODE(tcode) ((tcode) << 4) | 45 | #define HEADER_TCODE(tcode) ((tcode) << 4) |
42 | #define HEADER_RETRY(retry) ((retry) << 8) | 46 | #define HEADER_RETRY(retry) ((retry) << 8) |
@@ -293,6 +297,27 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode, | |||
293 | } | 297 | } |
294 | EXPORT_SYMBOL(fw_send_request); | 298 | EXPORT_SYMBOL(fw_send_request); |
295 | 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 | } | ||
320 | |||
296 | struct transaction_callback_data { | 321 | struct transaction_callback_data { |
297 | struct completion done; | 322 | struct completion done; |
298 | void *payload; | 323 | void *payload; |
diff --git a/drivers/firewire/fw-transaction.h b/drivers/firewire/fw-transaction.h index 35d0a4bb6d5c..eed2e295eb3c 100644 --- a/drivers/firewire/fw-transaction.h +++ b/drivers/firewire/fw-transaction.h | |||
@@ -407,6 +407,10 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, | |||
407 | int tcode, int destination_id, int generation, int speed, | 407 | int tcode, int destination_id, int generation, int speed, |
408 | unsigned long long offset, void *payload, size_t length, | 408 | unsigned long long offset, void *payload, size_t length, |
409 | fw_transaction_callback_t callback, void *callback_data); | 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 | int fw_cancel_transaction(struct fw_card *card, | 414 | int fw_cancel_transaction(struct fw_card *card, |
411 | struct fw_transaction *transaction); | 415 | struct fw_transaction *transaction); |
412 | void fw_flush_transactions(struct fw_card *card); | 416 | void fw_flush_transactions(struct fw_card *card); |
diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index 2e35379bf96c..4dfc84d0ac76 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h | |||
@@ -246,6 +246,7 @@ union fw_cdev_event { | |||
246 | #define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x10, struct fw_cdev_allocate_iso_resource) | 246 | #define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x10, struct fw_cdev_allocate_iso_resource) |
247 | #define FW_CDEV_IOC_GET_SPEED _IOR('#', 0x11, struct fw_cdev_get_speed) | 247 | #define FW_CDEV_IOC_GET_SPEED _IOR('#', 0x11, struct fw_cdev_get_speed) |
248 | #define FW_CDEV_IOC_SEND_BROADCAST_REQUEST _IOW('#', 0x12, struct fw_cdev_send_request) | 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 | * FW_CDEV_VERSION History | 252 | * FW_CDEV_VERSION History |
@@ -609,4 +610,30 @@ struct fw_cdev_get_speed { | |||
609 | __u32 max_speed; | 610 | __u32 max_speed; |
610 | }; | 611 | }; |
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; | ||
637 | }; | ||
638 | |||
612 | #endif /* _LINUX_FIREWIRE_CDEV_H */ | 639 | #endif /* _LINUX_FIREWIRE_CDEV_H */ |