Commit f8c2287c authored by Jay Fenlason's avatar Jay Fenlason Committed by Stefan Richter

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: default avatarJay Fenlason <fenlason@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (tweaks)
parent ba27e1f7
...@@ -1242,6 +1242,38 @@ static int ioctl_send_broadcast_request(struct client *client, void *buffer) ...@@ -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); 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) = { static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
ioctl_get_info, ioctl_get_info,
ioctl_send_request, ioctl_send_request,
...@@ -1262,6 +1294,7 @@ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { ...@@ -1262,6 +1294,7 @@ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
ioctl_deallocate_iso_resource_once, ioctl_deallocate_iso_resource_once,
ioctl_get_speed, ioctl_get_speed,
ioctl_send_broadcast_request, ioctl_send_broadcast_request,
ioctl_send_stream_packet,
}; };
static int dispatch_ioctl(struct client *client, static int dispatch_ioctl(struct client *client,
......
...@@ -936,7 +936,9 @@ static int at_context_queue_packet(struct context *ctx, ...@@ -936,7 +936,9 @@ static int at_context_queue_packet(struct context *ctx,
*/ */
header = (__le32 *) &d[1]; 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) | header[0] = cpu_to_le32((packet->header[0] & 0xffff) |
(packet->speed << 16)); (packet->speed << 16));
header[1] = cpu_to_le32((packet->header[1] & 0xffff) | header[1] = cpu_to_le32((packet->header[1] & 0xffff) |
...@@ -950,12 +952,27 @@ static int at_context_queue_packet(struct context *ctx, ...@@ -950,12 +952,27 @@ static int at_context_queue_packet(struct context *ctx,
header[3] = (__force __le32) packet->header[3]; header[3] = (__force __le32) packet->header[3];
d[0].req_count = cpu_to_le16(packet->header_length); d[0].req_count = cpu_to_le16(packet->header_length);
} else { break;
case 8:
header[0] = cpu_to_le32((OHCI1394_phy_tcode << 4) | header[0] = cpu_to_le32((OHCI1394_phy_tcode << 4) |
(packet->speed << 16)); (packet->speed << 16));
header[1] = cpu_to_le32(packet->header[0]); header[1] = cpu_to_le32(packet->header[0]);
header[2] = cpu_to_le32(packet->header[1]); header[2] = cpu_to_le32(packet->header[1]);
d[0].req_count = cpu_to_le16(12); 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]; driver_data = (struct driver_data *) &d[3];
......
...@@ -37,6 +37,10 @@ ...@@ -37,6 +37,10 @@
#include "fw-topology.h" #include "fw-topology.h"
#include "fw-device.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_PRI(pri) ((pri) << 0)
#define HEADER_TCODE(tcode) ((tcode) << 4) #define HEADER_TCODE(tcode) ((tcode) << 4)
#define HEADER_RETRY(retry) ((retry) << 8) #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,6 +297,27 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode,
} }
EXPORT_SYMBOL(fw_send_request); 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 transaction_callback_data {
struct completion done; struct completion done;
void *payload; void *payload;
......
...@@ -407,6 +407,10 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, ...@@ -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, int tcode, int destination_id, int generation, int speed,
unsigned long long offset, void *payload, size_t length, unsigned long long offset, void *payload, size_t length,
fw_transaction_callback_t callback, void *callback_data); 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, int fw_cancel_transaction(struct fw_card *card,
struct fw_transaction *transaction); struct fw_transaction *transaction);
void fw_flush_transactions(struct fw_card *card); void fw_flush_transactions(struct fw_card *card);
......
...@@ -246,6 +246,7 @@ union fw_cdev_event { ...@@ -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_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_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_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 * FW_CDEV_VERSION History
...@@ -609,4 +610,30 @@ struct fw_cdev_get_speed { ...@@ -609,4 +610,30 @@ struct fw_cdev_get_speed {
__u32 max_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 */ #endif /* _LINUX_FIREWIRE_CDEV_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment