firewire: implement asynchronous stream transmission
authorJay Fenlason <fenlason@redhat.com>
Thu, 5 Mar 2009 18:08:40 +0000 (19:08 +0100)
committerStefan Richter <stefanr@s5r6.in-berlin.de>
Tue, 24 Mar 2009 19:56:49 +0000 (20:56 +0100)
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)
drivers/firewire/fw-cdev.c
drivers/firewire/fw-ohci.c
drivers/firewire/fw-transaction.c
drivers/firewire/fw-transaction.h
include/linux/firewire-cdev.h

index 214e534..539dae5 100644 (file)
@@ -1242,6 +1242,38 @@ static int ioctl_send_broadcast_request(struct client *client, void *buffer)
        return init_request(client, request, LOCAL_BUS | 0x3f, SCODE_100);
 }
 
+struct stream_packet {
+       struct fw_packet packet;
+       u8 data[0];
+};
+
+static void send_stream_packet_done(struct fw_packet *packet,
+                                   struct fw_card *card, int status)
+{
+       kfree(container_of(packet, struct stream_packet, packet));
+}
+
+static int ioctl_send_stream_packet(struct client *client, void *buffer)
+{
+       struct fw_cdev_send_stream_packet *request = buffer;
+       struct stream_packet *p;
+
+       p = kmalloc(sizeof(*p) + request->size, GFP_KERNEL);
+       if (p == NULL)
+               return -ENOMEM;
+
+       if (request->data &&
+           copy_from_user(p->data, u64_to_uptr(request->data), request->size)) {
+               kfree(p);
+               return -EFAULT;
+       }
+       fw_send_stream_packet(client->device->card, &p->packet,
+                             request->generation, request->speed,
+                             request->channel, request->sy, request->tag,
+                             p->data, request->size, send_stream_packet_done);
+       return 0;
+}
+
 static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
        ioctl_get_info,
        ioctl_send_request,
@@ -1262,6 +1294,7 @@ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
        ioctl_deallocate_iso_resource_once,
        ioctl_get_speed,
        ioctl_send_broadcast_request,
+       ioctl_send_stream_packet,
 };
 
 static int dispatch_ioctl(struct client *client,
index c922783..1180d0b 100644 (file)
@@ -936,7 +936,9 @@ static int at_context_queue_packet(struct context *ctx,
         */
 
        header = (__le32 *) &d[1];
-       if (packet->header_length > 8) {
+       switch (packet->header_length) {
+       case 16:
+       case 12:
                header[0] = cpu_to_le32((packet->header[0] & 0xffff) |
                                        (packet->speed << 16));
                header[1] = cpu_to_le32((packet->header[1] & 0xffff) |
@@ -950,12 +952,27 @@ static int at_context_queue_packet(struct context *ctx,
                        header[3] = (__force __le32) packet->header[3];
 
                d[0].req_count = cpu_to_le16(packet->header_length);
-       } else {
+               break;
+
+       case 8:
                header[0] = cpu_to_le32((OHCI1394_phy_tcode << 4) |
                                        (packet->speed << 16));
                header[1] = cpu_to_le32(packet->header[0]);
                header[2] = cpu_to_le32(packet->header[1]);
                d[0].req_count = cpu_to_le16(12);
+               break;
+
+       case 4:
+               header[0] = cpu_to_le32((packet->header[0] & 0xffff) |
+                                       (packet->speed << 16));
+               header[1] = cpu_to_le32(packet->header[0] & 0xffff0000);
+               d[0].req_count = cpu_to_le16(8);
+               break;
+
+       default:
+               /* BUG(); */
+               packet->ack = RCODE_SEND_ERROR;
+               return -1;
        }
 
        driver_data = (struct driver_data *) &d[3];
index 76938fe..e3da589 100644 (file)
 #include "fw-topology.h"
 #include "fw-device.h"
 
+#define HEADER_TAG(tag)                        ((tag) << 14)
+#define HEADER_CHANNEL(ch)             ((ch) << 8)
+#define HEADER_SY(sy)                  ((sy) << 0)
+
 #define HEADER_PRI(pri)                        ((pri) << 0)
 #define HEADER_TCODE(tcode)            ((tcode) << 4)
 #define HEADER_RETRY(retry)            ((retry) << 8)
@@ -293,6 +297,27 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode,
 }
 EXPORT_SYMBOL(fw_send_request);
 
+void fw_send_stream_packet(struct fw_card *card, struct fw_packet *p,
+               int generation, int speed, int channel, int sy, int tag,
+               void *payload, size_t length, fw_packet_callback_t callback)
+{
+       p->callback = callback;
+       p->header[0] =
+                 HEADER_DATA_LENGTH(length)
+               | HEADER_TAG(tag)
+               | HEADER_CHANNEL(channel)
+               | HEADER_TCODE(TCODE_STREAM_DATA)
+               | HEADER_SY(sy);
+       p->header_length = 4;
+       p->payload = payload;
+       p->payload_length = length;
+       p->speed = speed;
+       p->generation = generation;
+       p->ack = 0;
+
+       card->driver->send_request(card, p);
+}
+
 struct transaction_callback_data {
        struct completion done;
        void *payload;
index 35d0a4b..eed2e29 100644 (file)
@@ -407,6 +407,10 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t,
                int tcode, int destination_id, int generation, int speed,
                unsigned long long offset, void *payload, size_t length,
                fw_transaction_callback_t callback, void *callback_data);
+void fw_send_stream_packet(struct fw_card *card, struct fw_packet *p,
+               int generation, int speed, int channel, int sy, int tag,
+               void *payload, size_t length, fw_packet_callback_t callback);
+
 int fw_cancel_transaction(struct fw_card *card,
                          struct fw_transaction *transaction);
 void fw_flush_transactions(struct fw_card *card);
index 2e35379..4dfc84d 100644 (file)
@@ -246,6 +246,7 @@ union fw_cdev_event {
 #define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x10, struct fw_cdev_allocate_iso_resource)
 #define FW_CDEV_IOC_GET_SPEED                    _IOR('#', 0x11, struct fw_cdev_get_speed)
 #define FW_CDEV_IOC_SEND_BROADCAST_REQUEST       _IOW('#', 0x12, struct fw_cdev_send_request)
+#define FW_CDEV_IOC_SEND_STREAM_PACKET           _IOW('#', 0x13, struct fw_cdev_send_stream_packet)
 
 /*
  * FW_CDEV_VERSION History
@@ -609,4 +610,30 @@ struct fw_cdev_get_speed {
        __u32 max_speed;
 };
 
+/**
+ * struct fw_cdev_send_stream_packet - send an asynchronous stream packet
+ * @generation:   Bus generation where the packet is valid
+ * @speed:       Speed code to send the packet at
+ * @channel:     Channel to send the packet on
+ * @sy:                  Four-bit sy code for the packet
+ * @tag:         Two-bit tag field to use for the packet
+ * @size:        Size of the packet's data payload
+ * @data:        Userspace pointer to the payload
+ *
+ * The %FW_CDEV_IOC_SEND_STREAM_PACKET ioctl sends an asynchronous stream packet
+ * to every device (that is listening to the specified channel) on the
+ * firewire bus.  It is the applications's job to ensure
+ * that the intended device(s) will be able to receive the packet at the chosen
+ * transmit speed.
+ */
+struct fw_cdev_send_stream_packet {
+       __u32 generation;
+       __u32 speed;
+       __u32 channel;
+       __u32 sy;
+       __u32 tag;
+       __u32 size;
+       __u64 data;
+};
+
 #endif /* _LINUX_FIREWIRE_CDEV_H */