Skip to content

Commit

Permalink
firewire: cdev: add closure to async stream ioctl
Browse files Browse the repository at this point in the history
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: Stefan Richter <stefanr@s5r6.in-berlin.de>
  • Loading branch information
Stefan Richter committed Mar 24, 2009
1 parent 664d801 commit 18e9b10
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 72 deletions.
46 changes: 19 additions & 27 deletions drivers/firewire/fw-cdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,8 @@ static int init_request(struct client *client,
struct outbound_transaction_event *e;
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;

e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);
Expand Down Expand Up @@ -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);
}

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;
struct fw_cdev_send_stream_packet *p = buffer;
struct fw_cdev_send_request request;
int dest;

p = kmalloc(sizeof(*p) + request->size, GFP_KERNEL);
if (p == NULL)
return -ENOMEM;
if (p->speed > client->device->card->link_speed ||
p->length > 1024 << p->speed)
return -EIO;

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;
if (p->tag > 3 || p->channel > 63 || p->sy > 15)
return -EINVAL;

dest = fw_stream_packet_destination_id(p->tag, p->channel, p->sy);
request.tcode = TCODE_STREAM_DATA;
request.length = p->length;
request.closure = p->closure;
request.data = p->data;
request.generation = p->generation;

return init_request(client, &request, dest, p->speed);
}

static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
Expand Down
42 changes: 16 additions & 26 deletions drivers/firewire/fw-transaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@
#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)
Expand Down Expand Up @@ -158,6 +154,18 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
{
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) {
ext_tcode = tcode & ~0x10;
tcode = TCODE_LOCK_REQUEST;
Expand Down Expand Up @@ -204,7 +212,7 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
packet->payload_length = 0;
break;
}

common:
packet->speed = speed;
packet->generation = generation;
packet->ack = 0;
Expand Down Expand Up @@ -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_data pointer to arbitrary data, which will be
* 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,
int destination_id, int generation, int speed,
Expand Down Expand Up @@ -297,27 +308,6 @@ 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;
Expand Down
9 changes: 5 additions & 4 deletions drivers/firewire/fw-transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
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);
Expand All @@ -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,
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
* activity; found, lost, or updated nodes.
Expand Down
31 changes: 16 additions & 15 deletions include/linux/firewire-cdev.h
Original file line number Diff line number Diff line change
Expand Up @@ -606,28 +606,29 @@ struct fw_cdev_allocate_iso_resource {

/**
* 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
* @length: Length of outgoing payload, in bytes
* @tag: Data format tag
* @channel: Isochronous channel to transmit to
* @sy: Synchronization code
* @closure: Passed back to userspace in the response event
* @data: Userspace pointer to 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
* 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.
* to every device which is listening to the specified channel. The kernel
* writes an &fw_cdev_event_response event which indicates success or failure of
* the transmission.
*/
struct fw_cdev_send_stream_packet {
__u32 generation;
__u32 speed;
__u32 length;
__u32 tag;
__u32 channel;
__u32 sy;
__u32 tag;
__u32 size;
__u64 closure;
__u64 data;
__u32 generation;
__u32 speed;
};

#endif /* _LINUX_FIREWIRE_CDEV_H */

0 comments on commit 18e9b10

Please sign in to comment.