Commit 18e9b10f authored by Stefan Richter's avatar Stefan Richter

firewire: cdev: add closure to async stream ioctl

This changes the as yet unreleased FW_CDEV_IOC_SEND_STREAM_PACKET ioctl
to generate an fw_cdev_event_response event just like the other two
ioctls for asynchronous request transmission do.  This way, clients get
feedback on successful or unsuccessful transmission.

This also adds input validation for length, tag, channel, sy, speed.
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent 664d8010
...@@ -522,7 +522,8 @@ static int init_request(struct client *client, ...@@ -522,7 +522,8 @@ static int init_request(struct client *client,
struct outbound_transaction_event *e; struct outbound_transaction_event *e;
int ret; int ret;
if (request->length > 4096 || request->length > 512 << speed) if (request->tcode != TCODE_STREAM_DATA &&
(request->length > 4096 || request->length > 512 << speed))
return -EIO; return -EIO;
e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL); e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);
...@@ -1247,36 +1248,27 @@ static int ioctl_send_broadcast_request(struct client *client, void *buffer) ...@@ -1247,36 +1248,27 @@ 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) static int ioctl_send_stream_packet(struct client *client, void *buffer)
{ {
struct fw_cdev_send_stream_packet *request = buffer; struct fw_cdev_send_stream_packet *p = buffer;
struct stream_packet *p; struct fw_cdev_send_request request;
int dest;
p = kmalloc(sizeof(*p) + request->size, GFP_KERNEL); if (p->speed > client->device->card->link_speed ||
if (p == NULL) p->length > 1024 << p->speed)
return -ENOMEM; return -EIO;
if (request->data && if (p->tag > 3 || p->channel > 63 || p->sy > 15)
copy_from_user(p->data, u64_to_uptr(request->data), request->size)) { return -EINVAL;
kfree(p);
return -EFAULT; dest = fw_stream_packet_destination_id(p->tag, p->channel, p->sy);
} request.tcode = TCODE_STREAM_DATA;
fw_send_stream_packet(client->device->card, &p->packet, request.length = p->length;
request->generation, request->speed, request.closure = p->closure;
request->channel, request->sy, request->tag, request.data = p->data;
p->data, request->size, send_stream_packet_done); request.generation = p->generation;
return 0;
return init_request(client, &request, dest, p->speed);
} }
static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
......
...@@ -37,10 +37,6 @@ ...@@ -37,10 +37,6 @@
#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)
...@@ -158,6 +154,18 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, ...@@ -158,6 +154,18 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
{ {
int ext_tcode; int ext_tcode;
if (tcode == TCODE_STREAM_DATA) {
packet->header[0] =
HEADER_DATA_LENGTH(length) |
destination_id |
HEADER_TCODE(TCODE_STREAM_DATA);
packet->header_length = 4;
packet->payload = payload;
packet->payload_length = length;
goto common;
}
if (tcode > 0x10) { if (tcode > 0x10) {
ext_tcode = tcode & ~0x10; ext_tcode = tcode & ~0x10;
tcode = TCODE_LOCK_REQUEST; tcode = TCODE_LOCK_REQUEST;
...@@ -204,7 +212,7 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, ...@@ -204,7 +212,7 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
packet->payload_length = 0; packet->payload_length = 0;
break; break;
} }
common:
packet->speed = speed; packet->speed = speed;
packet->generation = generation; packet->generation = generation;
packet->ack = 0; packet->ack = 0;
...@@ -246,6 +254,9 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, ...@@ -246,6 +254,9 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
* @param callback function to be called when the transaction is completed * @param callback function to be called when the transaction is completed
* @param callback_data pointer to arbitrary data, which will be * @param callback_data pointer to arbitrary data, which will be
* passed to the callback * passed to the callback
*
* In case of asynchronous stream packets i.e. TCODE_STREAM_DATA, the caller
* needs to synthesize @destination_id with fw_stream_packet_destination_id().
*/ */
void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode, void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode,
int destination_id, int generation, int speed, int destination_id, int generation, int speed,
...@@ -297,27 +308,6 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode, ...@@ -297,27 +308,6 @@ 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;
......
...@@ -412,10 +412,6 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, ...@@ -412,10 +412,6 @@ 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);
...@@ -425,6 +421,11 @@ int fw_run_transaction(struct fw_card *card, int tcode, int destination_id, ...@@ -425,6 +421,11 @@ int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
void fw_send_phy_config(struct fw_card *card, void fw_send_phy_config(struct fw_card *card,
int node_id, int generation, int gap_count); int node_id, int generation, int gap_count);
static inline int fw_stream_packet_destination_id(int tag, int channel, int sy)
{
return tag << 14 | channel << 8 | sy;
}
/* /*
* Called by the topology code to inform the device code of node * Called by the topology code to inform the device code of node
* activity; found, lost, or updated nodes. * activity; found, lost, or updated nodes.
......
...@@ -606,28 +606,29 @@ struct fw_cdev_allocate_iso_resource { ...@@ -606,28 +606,29 @@ struct fw_cdev_allocate_iso_resource {
/** /**
* struct fw_cdev_send_stream_packet - send an asynchronous stream packet * struct fw_cdev_send_stream_packet - send an asynchronous stream packet
* @generation: Bus generation where the packet is valid * @length: Length of outgoing payload, in bytes
* @speed: Speed code to send the packet at * @tag: Data format tag
* @channel: Channel to send the packet on * @channel: Isochronous channel to transmit to
* @sy: Four-bit sy code for the packet * @sy: Synchronization code
* @tag: Two-bit tag field to use for the packet * @closure: Passed back to userspace in the response event
* @size: Size of the packet's data payload * @data: Userspace pointer to payload
* @data: Userspace pointer to the payload * @generation: The bus generation where packet is valid
* @speed: Speed to transmit at
* *
* The %FW_CDEV_IOC_SEND_STREAM_PACKET ioctl sends an asynchronous stream packet * 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 * to every device which is listening to the specified channel. The kernel
* firewire bus. It is the applications's job to ensure * writes an &fw_cdev_event_response event which indicates success or failure of
* that the intended device(s) will be able to receive the packet at the chosen * the transmission.
* transmit speed.
*/ */
struct fw_cdev_send_stream_packet { struct fw_cdev_send_stream_packet {
__u32 generation; __u32 length;
__u32 speed; __u32 tag;
__u32 channel; __u32 channel;
__u32 sy; __u32 sy;
__u32 tag; __u64 closure;
__u32 size;
__u64 data; __u64 data;
__u32 generation;
__u32 speed;
}; };
#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