summaryrefslogtreecommitdiff
path: root/drivers/firewire/core-cdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firewire/core-cdev.c')
-rw-r--r--drivers/firewire/core-cdev.c252
1 files changed, 188 insertions, 64 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index 2c16ee8fd842..6274b86eb943 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -43,6 +43,7 @@
#define FW_CDEV_VERSION_EVENT_REQUEST2 4
#define FW_CDEV_VERSION_ALLOCATE_REGION_END 4
#define FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW 5
+#define FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP 6
struct client {
u32 version;
@@ -169,7 +170,10 @@ struct outbound_transaction_event {
struct event event;
struct client *client;
struct outbound_transaction_resource r;
- struct fw_cdev_event_response response;
+ union {
+ struct fw_cdev_event_response without_tstamp;
+ struct fw_cdev_event_response2 with_tstamp;
+ } rsp;
};
struct inbound_transaction_event {
@@ -177,6 +181,7 @@ struct inbound_transaction_event {
union {
struct fw_cdev_event_request request;
struct fw_cdev_event_request2 request2;
+ struct fw_cdev_event_request3 with_tstamp;
} req;
};
@@ -199,12 +204,18 @@ struct outbound_phy_packet_event {
struct event event;
struct client *client;
struct fw_packet p;
- struct fw_cdev_event_phy_packet phy_packet;
+ union {
+ struct fw_cdev_event_phy_packet without_tstamp;
+ struct fw_cdev_event_phy_packet2 with_tstamp;
+ } phy_packet;
};
struct inbound_phy_packet_event {
struct event event;
- struct fw_cdev_event_phy_packet phy_packet;
+ union {
+ struct fw_cdev_event_phy_packet without_tstamp;
+ struct fw_cdev_event_phy_packet2 with_tstamp;
+ } phy_packet;
};
#ifdef CONFIG_COMPAT
@@ -534,41 +545,64 @@ static void release_transaction(struct client *client,
{
}
-static void complete_transaction(struct fw_card *card, int rcode,
- void *payload, size_t length, void *data)
+static void complete_transaction(struct fw_card *card, int rcode, u32 request_tstamp,
+ u32 response_tstamp, void *payload, size_t length, void *data)
{
struct outbound_transaction_event *e = data;
- struct fw_cdev_event_response *rsp = &e->response;
struct client *client = e->client;
unsigned long flags;
- if (length < rsp->length)
- rsp->length = length;
- if (rcode == RCODE_COMPLETE)
- memcpy(rsp->data, payload, rsp->length);
-
spin_lock_irqsave(&client->lock, flags);
idr_remove(&client->resource_idr, e->r.resource.handle);
if (client->in_shutdown)
wake_up(&client->tx_flush_wait);
spin_unlock_irqrestore(&client->lock, flags);
- rsp->type = FW_CDEV_EVENT_RESPONSE;
- rsp->rcode = rcode;
+ switch (e->rsp.without_tstamp.type) {
+ case FW_CDEV_EVENT_RESPONSE:
+ {
+ struct fw_cdev_event_response *rsp = &e->rsp.without_tstamp;
+
+ if (length < rsp->length)
+ rsp->length = length;
+ if (rcode == RCODE_COMPLETE)
+ memcpy(rsp->data, payload, rsp->length);
+
+ rsp->rcode = rcode;
+
+ // In the case that sizeof(*rsp) doesn't align with the position of the
+ // data, and the read is short, preserve an extra copy of the data
+ // to stay compatible with a pre-2.6.27 bug. Since the bug is harmless
+ // for short reads and some apps depended on it, this is both safe
+ // and prudent for compatibility.
+ if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data))
+ queue_event(client, &e->event, rsp, sizeof(*rsp), rsp->data, rsp->length);
+ else
+ queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, NULL, 0);
- /*
- * In the case that sizeof(*rsp) doesn't align with the position of the
- * data, and the read is short, preserve an extra copy of the data
- * to stay compatible with a pre-2.6.27 bug. Since the bug is harmless
- * for short reads and some apps depended on it, this is both safe
- * and prudent for compatibility.
- */
- if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data))
- queue_event(client, &e->event, rsp, sizeof(*rsp),
- rsp->data, rsp->length);
- else
- queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length,
- NULL, 0);
+ break;
+ }
+ case FW_CDEV_EVENT_RESPONSE2:
+ {
+ struct fw_cdev_event_response2 *rsp = &e->rsp.with_tstamp;
+
+ if (length < rsp->length)
+ rsp->length = length;
+ if (rcode == RCODE_COMPLETE)
+ memcpy(rsp->data, payload, rsp->length);
+
+ rsp->rcode = rcode;
+ rsp->request_tstamp = request_tstamp;
+ rsp->response_tstamp = response_tstamp;
+
+ queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, NULL, 0);
+
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+ }
/* Drop the idr's reference */
client_put(client);
@@ -579,6 +613,7 @@ static int init_request(struct client *client,
int destination_id, int speed)
{
struct outbound_transaction_event *e;
+ void *payload;
int ret;
if (request->tcode != TCODE_STREAM_DATA &&
@@ -592,14 +627,25 @@ static int init_request(struct client *client,
e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);
if (e == NULL)
return -ENOMEM;
-
e->client = client;
- e->response.length = request->length;
- e->response.closure = request->closure;
- if (request->data &&
- copy_from_user(e->response.data,
- u64_to_uptr(request->data), request->length)) {
+ if (client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) {
+ struct fw_cdev_event_response *rsp = &e->rsp.without_tstamp;
+
+ rsp->type = FW_CDEV_EVENT_RESPONSE;
+ rsp->length = request->length;
+ rsp->closure = request->closure;
+ payload = rsp->data;
+ } else {
+ struct fw_cdev_event_response2 *rsp = &e->rsp.with_tstamp;
+
+ rsp->type = FW_CDEV_EVENT_RESPONSE2;
+ rsp->length = request->length;
+ rsp->closure = request->closure;
+ payload = rsp->data;
+ }
+
+ if (request->data && copy_from_user(payload, u64_to_uptr(request->data), request->length)) {
ret = -EFAULT;
goto failed;
}
@@ -609,10 +655,9 @@ static int init_request(struct client *client,
if (ret < 0)
goto failed;
- fw_send_request(client->device->card, &e->r.transaction,
- request->tcode, destination_id, request->generation,
- speed, request->offset, e->response.data,
- request->length, complete_transaction, e);
+ fw_send_request_with_tstamp(client->device->card, &e->r.transaction, request->tcode,
+ destination_id, request->generation, speed, request->offset,
+ payload, request->length, complete_transaction, e);
return 0;
failed:
@@ -708,7 +753,7 @@ static void handle_request(struct fw_card *card, struct fw_request *request,
req->handle = r->resource.handle;
req->closure = handler->closure;
event_size0 = sizeof(*req);
- } else {
+ } else if (handler->client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) {
struct fw_cdev_event_request2 *req = &e->req.request2;
req->type = FW_CDEV_EVENT_REQUEST2;
@@ -722,6 +767,21 @@ static void handle_request(struct fw_card *card, struct fw_request *request,
req->handle = r->resource.handle;
req->closure = handler->closure;
event_size0 = sizeof(*req);
+ } else {
+ struct fw_cdev_event_request3 *req = &e->req.with_tstamp;
+
+ req->type = FW_CDEV_EVENT_REQUEST3;
+ req->tcode = tcode;
+ req->offset = offset;
+ req->source_node_id = source;
+ req->destination_node_id = destination;
+ req->card = card->index;
+ req->generation = generation;
+ req->length = length;
+ req->handle = r->resource.handle;
+ req->closure = handler->closure;
+ req->tstamp = fw_request_get_timestamp(request);
+ event_size0 = sizeof(*req);
}
queue_event(handler->client, &e->event,
@@ -1495,26 +1555,61 @@ static void outbound_phy_packet_callback(struct fw_packet *packet,
{
struct outbound_phy_packet_event *e =
container_of(packet, struct outbound_phy_packet_event, p);
- struct client *e_client;
+ struct client *e_client = e->client;
+ u32 rcode;
switch (status) {
- /* expected: */
- case ACK_COMPLETE: e->phy_packet.rcode = RCODE_COMPLETE; break;
- /* should never happen with PHY packets: */
- case ACK_PENDING: e->phy_packet.rcode = RCODE_COMPLETE; break;
+ // expected:
+ case ACK_COMPLETE:
+ rcode = RCODE_COMPLETE;
+ break;
+ // should never happen with PHY packets:
+ case ACK_PENDING:
+ rcode = RCODE_COMPLETE;
+ break;
case ACK_BUSY_X:
case ACK_BUSY_A:
- case ACK_BUSY_B: e->phy_packet.rcode = RCODE_BUSY; break;
- case ACK_DATA_ERROR: e->phy_packet.rcode = RCODE_DATA_ERROR; break;
- case ACK_TYPE_ERROR: e->phy_packet.rcode = RCODE_TYPE_ERROR; break;
- /* stale generation; cancelled; on certain controllers: no ack */
- default: e->phy_packet.rcode = status; break;
+ case ACK_BUSY_B:
+ rcode = RCODE_BUSY;
+ break;
+ case ACK_DATA_ERROR:
+ rcode = RCODE_DATA_ERROR;
+ break;
+ case ACK_TYPE_ERROR:
+ rcode = RCODE_TYPE_ERROR;
+ break;
+ // stale generation; cancelled; on certain controllers: no ack
+ default:
+ rcode = status;
+ break;
+ }
+
+ switch (e->phy_packet.without_tstamp.type) {
+ case FW_CDEV_EVENT_PHY_PACKET_SENT:
+ {
+ struct fw_cdev_event_phy_packet *pp = &e->phy_packet.without_tstamp;
+
+ pp->rcode = rcode;
+ pp->data[0] = packet->timestamp;
+ queue_event(e->client, &e->event, &e->phy_packet, sizeof(*pp) + pp->length,
+ NULL, 0);
+ break;
+ }
+ case FW_CDEV_EVENT_PHY_PACKET_SENT2:
+ {
+ struct fw_cdev_event_phy_packet2 *pp = &e->phy_packet.with_tstamp;
+
+ pp->rcode = rcode;
+ pp->tstamp = packet->timestamp;
+ queue_event(e->client, &e->event, &e->phy_packet, sizeof(*pp) + pp->length,
+ NULL, 0);
+ break;
+ }
+ default:
+ WARN_ON(1);
+ break;
}
- e->phy_packet.data[0] = packet->timestamp;
- e_client = e->client;
- queue_event(e->client, &e->event, &e->phy_packet,
- sizeof(e->phy_packet) + e->phy_packet.length, NULL, 0);
client_put(e_client);
}
@@ -1528,7 +1623,7 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg)
if (!client->device->is_local)
return -ENOSYS;
- e = kzalloc(sizeof(*e) + 4, GFP_KERNEL);
+ e = kzalloc(sizeof(*e) + sizeof(a->data), GFP_KERNEL);
if (e == NULL)
return -ENOMEM;
@@ -1541,10 +1636,24 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg)
e->p.header[2] = a->data[1];
e->p.header_length = 12;
e->p.callback = outbound_phy_packet_callback;
- e->phy_packet.closure = a->closure;
- e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_SENT;
- if (is_ping_packet(a->data))
- e->phy_packet.length = 4;
+
+ if (client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) {
+ struct fw_cdev_event_phy_packet *pp = &e->phy_packet.without_tstamp;
+
+ pp->closure = a->closure;
+ pp->type = FW_CDEV_EVENT_PHY_PACKET_SENT;
+ if (is_ping_packet(a->data))
+ pp->length = 4;
+ } else {
+ struct fw_cdev_event_phy_packet2 *pp = &e->phy_packet.with_tstamp;
+
+ pp->closure = a->closure;
+ pp->type = FW_CDEV_EVENT_PHY_PACKET_SENT2;
+ // Keep the data field so that application can match the response event to the
+ // request.
+ pp->length = sizeof(a->data);
+ memcpy(pp->data, a->data, sizeof(a->data));
+ }
card->driver->send_request(card, &e->p);
@@ -1583,14 +1692,29 @@ void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p)
if (e == NULL)
break;
- e->phy_packet.closure = client->phy_receiver_closure;
- e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED;
- e->phy_packet.rcode = RCODE_COMPLETE;
- e->phy_packet.length = 8;
- e->phy_packet.data[0] = p->header[1];
- e->phy_packet.data[1] = p->header[2];
- queue_event(client, &e->event,
- &e->phy_packet, sizeof(e->phy_packet) + 8, NULL, 0);
+ if (client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) {
+ struct fw_cdev_event_phy_packet *pp = &e->phy_packet.without_tstamp;
+
+ pp->closure = client->phy_receiver_closure;
+ pp->type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED;
+ pp->rcode = RCODE_COMPLETE;
+ pp->length = 8;
+ pp->data[0] = p->header[1];
+ pp->data[1] = p->header[2];
+ queue_event(client, &e->event, &e->phy_packet, sizeof(*pp) + 8, NULL, 0);
+ } else {
+ struct fw_cdev_event_phy_packet2 *pp = &e->phy_packet.with_tstamp;
+
+ pp = &e->phy_packet.with_tstamp;
+ pp->closure = client->phy_receiver_closure;
+ pp->type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED2;
+ pp->rcode = RCODE_COMPLETE;
+ pp->length = 8;
+ pp->tstamp = p->timestamp;
+ pp->data[0] = p->header[1];
+ pp->data[1] = p->header[2];
+ queue_event(client, &e->event, &e->phy_packet, sizeof(*pp) + 8, NULL, 0);
+ }
}
spin_unlock_irqrestore(&card->lock, flags);