aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-07-04 11:02:34 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2023-07-04 11:02:34 -0700
commit406fb9eb198a05fa61c31ec8a6e667c8440749c8 (patch)
treeb05d24bc60e20cf713eb0c651f1d472eb1fe7d77
parentf1962207150c8b602e980616f04b37ea4e64bb9f (diff)
parent3ff256751a2853e1ffaa36958ff933ccc98c6cb5 (diff)
downloadnet-next-406fb9eb198a05fa61c31ec8a6e667c8440749c8.tar.gz
Merge tag 'firewire-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394
Pull firewire updates from Takashi Sakamoto: "This consist of three parts; UAPI update, OHCI driver update, and several bug fixes. Firstly, the 1394 OHCI specification defines method to retrieve hardware time stamps for asynchronous communication, which was previously unavailable in user space. This adds new events to the UAPI, allowing applications to retrieve the time when asynchronous packet are received and sent. The new events are tested in the bleeding edge of libhinawa and look to work well. The new version of libhinawa will be released after current merge window is closed: https://git.kernel.org/pub/scm/libs/ieee1394/libhinawa.git/ Secondly, the FireWire stack includes a PCM device driver for 1394 OHCI hardware, This change modernizes the driver by managed resource (devres) framework. Lastly, bug fixes for firewire-net and firewire-core" * tag 'firewire-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394: (25 commits) firewire: net: fix use after free in fwnet_finish_incoming_packet() firewire: core: obsolete usage of GFP_ATOMIC at building node tree firewire: ohci: release buffer for AR req/resp contexts when managed resource is released firewire: ohci: use devres for content of configuration ROM firewire: ohci: use devres for IT, IR, AT/receive, and AT/request contexts firewire: ohci: use devres for list of isochronous contexts firewire: ohci: use devres for requested IRQ firewire: ohci: use devres for misc DMA buffer firewire: ohci: use devres for MMIO region mapping firewire: ohci: use devres for PCI-related resources firewire: ohci: use devres for memory object of ohci structure firewire: fix warnings to generate UAPI documentation firewire: fix build failure due to missing module license firewire: cdev: implement new event relevant to phy packet with time stamp firewire: cdev: add new event to notify phy packet with time stamp firewire: cdev: code refactoring to dispatch event for phy packet firewire: cdev: implement new event to notify response subaction with time stamp firewire: cdev: add new event to notify response subaction with time stamp firewire: cdev: code refactoring to operate event of response firewire: core: implement variations to send request and wait for response with time stamp ...
-rw-r--r--drivers/firewire/.kunitconfig4
-rw-r--r--drivers/firewire/Kconfig16
-rw-r--r--drivers/firewire/Makefile3
-rw-r--r--drivers/firewire/core-cdev.c252
-rw-r--r--drivers/firewire/core-device.c2
-rw-r--r--drivers/firewire/core-topology.c2
-rw-r--r--drivers/firewire/core-transaction.c93
-rw-r--r--drivers/firewire/core.h7
-rw-r--r--drivers/firewire/net.c6
-rw-r--r--drivers/firewire/ohci.c191
-rw-r--r--drivers/firewire/uapi-test.c89
-rw-r--r--include/linux/firewire.h82
-rw-r--r--include/uapi/linux/firewire-cdev.h178
13 files changed, 694 insertions, 231 deletions
diff --git a/drivers/firewire/.kunitconfig b/drivers/firewire/.kunitconfig
new file mode 100644
index 0000000000000..1599e069395fc
--- /dev/null
+++ b/drivers/firewire/.kunitconfig
@@ -0,0 +1,4 @@
+CONFIG_KUNIT=y
+CONFIG_PCI=y
+CONFIG_FIREWIRE=y
+CONFIG_FIREWIRE_KUNIT_UAPI_TEST=y
diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig
index ec00a6f70da81..0a6596b027db9 100644
--- a/drivers/firewire/Kconfig
+++ b/drivers/firewire/Kconfig
@@ -18,6 +18,22 @@ config FIREWIRE
To compile this driver as a module, say M here: the module will be
called firewire-core.
+config FIREWIRE_KUNIT_UAPI_TEST
+ tristate "KUnit tests for layout of structure in UAPI" if !KUNIT_ALL_TESTS
+ depends on FIREWIRE && KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds the KUnit tests whether structures exposed to user
+ space have expected layout.
+
+ KUnit tests run during boot and output the results to the debug
+ log in TAP format (https://testanything.org/). Only useful for
+ kernel devs running KUnit test harness and are not for inclusion
+ into a production build.
+
+ For more information on KUnit and unit tests in general, refer
+ to the KUnit documentation in Documentation/dev-tools/kunit/.
+
config FIREWIRE_OHCI
tristate "OHCI-1394 controllers"
depends on PCI && FIREWIRE && MMU
diff --git a/drivers/firewire/Makefile b/drivers/firewire/Makefile
index e58c8c7947782..b24b2879ac34f 100644
--- a/drivers/firewire/Makefile
+++ b/drivers/firewire/Makefile
@@ -15,3 +15,6 @@ obj-$(CONFIG_FIREWIRE_SBP2) += firewire-sbp2.o
obj-$(CONFIG_FIREWIRE_NET) += firewire-net.o
obj-$(CONFIG_FIREWIRE_NOSY) += nosy.o
obj-$(CONFIG_PROVIDE_OHCI1394_DMA_INIT) += init_ohci1394_dma.o
+
+firewire-uapi-test-objs += uapi-test.o
+obj-$(CONFIG_FIREWIRE_KUNIT_UAPI_TEST) += firewire-uapi-test.o
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index 2c16ee8fd842d..6274b86eb9437 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);
diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c
index aa597cda0d887..a3104e35412c1 100644
--- a/drivers/firewire/core-device.c
+++ b/drivers/firewire/core-device.c
@@ -1211,7 +1211,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
* without actually having a link.
*/
create:
- device = kzalloc(sizeof(*device), GFP_ATOMIC);
+ device = kzalloc(sizeof(*device), GFP_KERNEL);
if (device == NULL)
break;
diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c
index f40c815343812..88466b663482f 100644
--- a/drivers/firewire/core-topology.c
+++ b/drivers/firewire/core-topology.c
@@ -101,7 +101,7 @@ static struct fw_node *fw_node_create(u32 sid, int port_count, int color)
{
struct fw_node *node;
- node = kzalloc(struct_size(node, ports, port_count), GFP_ATOMIC);
+ node = kzalloc(struct_size(node, ports, port_count), GFP_KERNEL);
if (node == NULL)
return NULL;
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c
index a9f70c96323e6..130b95aca6290 100644
--- a/drivers/firewire/core-transaction.c
+++ b/drivers/firewire/core-transaction.c
@@ -70,8 +70,8 @@ static int try_cancel_split_timeout(struct fw_transaction *t)
return 1;
}
-static int close_transaction(struct fw_transaction *transaction,
- struct fw_card *card, int rcode)
+static int close_transaction(struct fw_transaction *transaction, struct fw_card *card, int rcode,
+ u32 response_tstamp)
{
struct fw_transaction *t = NULL, *iter;
unsigned long flags;
@@ -92,7 +92,12 @@ static int close_transaction(struct fw_transaction *transaction,
spin_unlock_irqrestore(&card->lock, flags);
if (t) {
- t->callback(card, rcode, NULL, 0, t->callback_data);
+ if (!t->with_tstamp) {
+ t->callback.without_tstamp(card, rcode, NULL, 0, t->callback_data);
+ } else {
+ t->callback.with_tstamp(card, rcode, t->packet.timestamp, response_tstamp,
+ NULL, 0, t->callback_data);
+ }
return 0;
}
@@ -107,6 +112,8 @@ static int close_transaction(struct fw_transaction *transaction,
int fw_cancel_transaction(struct fw_card *card,
struct fw_transaction *transaction)
{
+ u32 tstamp;
+
/*
* Cancel the packet transmission if it's still queued. That
* will call the packet transmission callback which cancels
@@ -121,7 +128,17 @@ int fw_cancel_transaction(struct fw_card *card,
* if the transaction is still pending and remove it in that case.
*/
- return close_transaction(transaction, card, RCODE_CANCELLED);
+ if (transaction->packet.ack == 0) {
+ // The timestamp is reused since it was just read now.
+ tstamp = transaction->packet.timestamp;
+ } else {
+ u32 curr_cycle_time = 0;
+
+ (void)fw_card_read_cycle_time(card, &curr_cycle_time);
+ tstamp = cycle_time_to_ohci_tstamp(curr_cycle_time);
+ }
+
+ return close_transaction(transaction, card, RCODE_CANCELLED, tstamp);
}
EXPORT_SYMBOL(fw_cancel_transaction);
@@ -140,7 +157,12 @@ static void split_transaction_timeout_callback(struct timer_list *timer)
card->tlabel_mask &= ~(1ULL << t->tlabel);
spin_unlock_irqrestore(&card->lock, flags);
- t->callback(card, RCODE_CANCELLED, NULL, 0, t->callback_data);
+ if (!t->with_tstamp) {
+ t->callback.without_tstamp(card, RCODE_CANCELLED, NULL, 0, t->callback_data);
+ } else {
+ t->callback.with_tstamp(card, RCODE_CANCELLED, t->packet.timestamp,
+ t->split_timeout_cycle, NULL, 0, t->callback_data);
+ }
}
static void start_split_transaction_timeout(struct fw_transaction *t,
@@ -162,6 +184,8 @@ static void start_split_transaction_timeout(struct fw_transaction *t,
spin_unlock_irqrestore(&card->lock, flags);
}
+static u32 compute_split_timeout_timestamp(struct fw_card *card, u32 request_timestamp);
+
static void transmit_complete_callback(struct fw_packet *packet,
struct fw_card *card, int status)
{
@@ -170,28 +194,32 @@ static void transmit_complete_callback(struct fw_packet *packet,
switch (status) {
case ACK_COMPLETE:
- close_transaction(t, card, RCODE_COMPLETE);
+ close_transaction(t, card, RCODE_COMPLETE, packet->timestamp);
break;
case ACK_PENDING:
+ {
+ t->split_timeout_cycle =
+ compute_split_timeout_timestamp(card, packet->timestamp) & 0xffff;
start_split_transaction_timeout(t, card);
break;
+ }
case ACK_BUSY_X:
case ACK_BUSY_A:
case ACK_BUSY_B:
- close_transaction(t, card, RCODE_BUSY);
+ close_transaction(t, card, RCODE_BUSY, packet->timestamp);
break;
case ACK_DATA_ERROR:
- close_transaction(t, card, RCODE_DATA_ERROR);
+ close_transaction(t, card, RCODE_DATA_ERROR, packet->timestamp);
break;
case ACK_TYPE_ERROR:
- close_transaction(t, card, RCODE_TYPE_ERROR);
+ close_transaction(t, card, RCODE_TYPE_ERROR, packet->timestamp);
break;
default:
/*
* In this case the ack is really a juju specific
* rcode, so just forward that to the callback.
*/
- close_transaction(t, card, status);
+ close_transaction(t, card, status, packet->timestamp);
break;
}
}
@@ -288,7 +316,8 @@ static int allocate_tlabel(struct fw_card *card)
}
/**
- * fw_send_request() - submit a request packet for transmission
+ * __fw_send_request() - submit a request packet for transmission to generate callback for response
+ * subaction with or without time stamp.
* @card: interface to send the request at
* @t: transaction instance to which the request belongs
* @tcode: transaction code
@@ -298,7 +327,9 @@ static int allocate_tlabel(struct fw_card *card)
* @offset: 48bit wide offset into destination's address space
* @payload: data payload for the request subaction
* @length: length of the payload, in bytes
- * @callback: function to be called when the transaction is completed
+ * @callback: union of two functions whether to receive time stamp or not for response
+ * subaction.
+ * @with_tstamp: Whether to receive time stamp or not for response subaction.
* @callback_data: data to be passed to the transaction completion callback
*
* Submit a request packet into the asynchronous request transmission queue.
@@ -335,10 +366,10 @@ static int allocate_tlabel(struct fw_card *card)
* transaction completion and hence execution of @callback may happen even
* before fw_send_request() returns.
*/
-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_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, union fw_transaction_callback callback,
+ bool with_tstamp, void *callback_data)
{
unsigned long flags;
int tlabel;
@@ -353,7 +384,19 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode,
tlabel = allocate_tlabel(card);
if (tlabel < 0) {
spin_unlock_irqrestore(&card->lock, flags);
- callback(card, RCODE_SEND_ERROR, NULL, 0, callback_data);
+ if (!with_tstamp) {
+ callback.without_tstamp(card, RCODE_SEND_ERROR, NULL, 0, callback_data);
+ } else {
+ // Timestamping on behalf of hardware.
+ u32 curr_cycle_time = 0;
+ u32 tstamp;
+
+ (void)fw_card_read_cycle_time(card, &curr_cycle_time);
+ tstamp = cycle_time_to_ohci_tstamp(curr_cycle_time);
+
+ callback.with_tstamp(card, RCODE_SEND_ERROR, tstamp, tstamp, NULL, 0,
+ callback_data);
+ }
return;
}
@@ -361,13 +404,12 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode,
t->tlabel = tlabel;
t->card = card;
t->is_split_transaction = false;
- timer_setup(&t->split_timeout_timer,
- split_transaction_timeout_callback, 0);
+ timer_setup(&t->split_timeout_timer, split_transaction_timeout_callback, 0);
t->callback = callback;
+ t->with_tstamp = with_tstamp;
t->callback_data = callback_data;
- fw_fill_request(&t->packet, tcode, t->tlabel,
- destination_id, card->node_id, generation,
+ fw_fill_request(&t->packet, tcode, t->tlabel, destination_id, card->node_id, generation,
speed, offset, payload, length);
t->packet.callback = transmit_complete_callback;
@@ -377,7 +419,7 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode,
card->driver->send_request(card, &t->packet);
}
-EXPORT_SYMBOL(fw_send_request);
+EXPORT_SYMBOL_GPL(__fw_send_request);
struct transaction_callback_data {
struct completion done;
@@ -1047,7 +1089,12 @@ void fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
*/
card->driver->cancel_packet(card, &t->packet);
- t->callback(card, rcode, data, data_length, t->callback_data);
+ if (!t->with_tstamp) {
+ t->callback.without_tstamp(card, rcode, data, data_length, t->callback_data);
+ } else {
+ t->callback.with_tstamp(card, rcode, t->packet.timestamp, p->timestamp, data,
+ data_length, t->callback_data);
+ }
}
EXPORT_SYMBOL(fw_core_handle_response);
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
index eafa4eaae737a..2a05f411328fd 100644
--- a/drivers/firewire/core.h
+++ b/drivers/firewire/core.h
@@ -247,6 +247,13 @@ void fw_fill_response(struct fw_packet *response, u32 *request_header,
void fw_request_get(struct fw_request *request);
void fw_request_put(struct fw_request *request);
+// Convert the value of IEEE 1394 CYCLE_TIME register to the format of timeStamp field in
+// descriptors of 1394 OHCI.
+static inline u32 cycle_time_to_ohci_tstamp(u32 tstamp)
+{
+ return (tstamp & 0x0ffff000) >> 12;
+}
+
#define FW_PHY_CONFIG_NO_NODE_ID -1
#define FW_PHY_CONFIG_CURRENT_GAP_COUNT -1
void fw_send_phy_config(struct fw_card *card,
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c
index 538bd677c254a..7a4d1a478e33e 100644
--- a/drivers/firewire/net.c
+++ b/drivers/firewire/net.c
@@ -479,7 +479,7 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
struct sk_buff *skb, u16 source_node_id,
bool is_broadcast, u16 ether_type)
{
- int status;
+ int status, len;
switch (ether_type) {
case ETH_P_ARP:
@@ -533,13 +533,15 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
}
skb->protocol = protocol;
}
+
+ len = skb->len;
status = netif_rx(skb);
if (status == NET_RX_DROP) {
net->stats.rx_errors++;
net->stats.rx_dropped++;
} else {
net->stats.rx_packets++;
- net->stats.rx_bytes += skb->len;
+ net->stats.rx_bytes += len;
}
return 0;
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index 17c9d825188bb..7e88fd4897414 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -677,6 +677,9 @@ static void ar_context_release(struct ar_context *ctx)
struct device *dev = ctx->ohci->card.device;
unsigned int i;
+ if (!ctx->buffer)
+ return;
+
vunmap(ctx->buffer);
for (i = 0; i < AR_BUFFERS; i++) {
@@ -1105,8 +1108,7 @@ static int context_add_buffer(struct context *ctx)
if (ctx->total_allocation >= 16*1024*1024)
return -ENOMEM;
- desc = dma_alloc_coherent(ctx->ohci->card.device, PAGE_SIZE,
- &bus_addr, GFP_ATOMIC);
+ desc = dmam_alloc_coherent(ctx->ohci->card.device, PAGE_SIZE, &bus_addr, GFP_ATOMIC);
if (!desc)
return -ENOMEM;
@@ -1165,10 +1167,10 @@ static void context_release(struct context *ctx)
struct fw_card *card = &ctx->ohci->card;
struct descriptor_buffer *desc, *tmp;
- list_for_each_entry_safe(desc, tmp, &ctx->buffer_list, list)
- dma_free_coherent(card->device, PAGE_SIZE, desc,
- desc->buffer_bus -
- ((void *)&desc->buffer - (void *)desc));
+ list_for_each_entry_safe(desc, tmp, &ctx->buffer_list, list) {
+ dmam_free_coherent(card->device, PAGE_SIZE, desc,
+ desc->buffer_bus - ((void *)&desc->buffer - (void *)desc));
+ }
}
/* Must be called with ohci->lock held */
@@ -1623,6 +1625,8 @@ static void handle_local_request(struct context *ctx, struct fw_packet *packet)
}
}
+static u32 get_cycle_time(struct fw_ohci *ohci);
+
static void at_context_transmit(struct context *ctx, struct fw_packet *packet)
{
unsigned long flags;
@@ -1633,6 +1637,10 @@ static void at_context_transmit(struct context *ctx, struct fw_packet *packet)
if (HEADER_GET_DESTINATION(packet->header[0]) == ctx->ohci->node_id &&
ctx->ohci->generation == packet->generation) {
spin_unlock_irqrestore(&ctx->ohci->lock, flags);
+
+ // Timestamping on behalf of the hardware.
+ packet->timestamp = cycle_time_to_ohci_tstamp(get_cycle_time(ctx->ohci));
+
handle_local_request(ctx, packet);
return;
}
@@ -1640,9 +1648,12 @@ static void at_context_transmit(struct context *ctx, struct fw_packet *packet)
ret = at_context_queue_packet(ctx, packet);
spin_unlock_irqrestore(&ctx->ohci->lock, flags);
- if (ret < 0)
- packet->callback(packet, &ctx->ohci->card, packet->ack);
+ if (ret < 0) {
+ // Timestamping on behalf of the hardware.
+ packet->timestamp = cycle_time_to_ohci_tstamp(get_cycle_time(ctx->ohci));
+ packet->callback(packet, &ctx->ohci->card, packet->ack);
+ }
}
static void detect_dead_context(struct fw_ohci *ohci,
@@ -2044,8 +2055,7 @@ static void bus_reset_work(struct work_struct *work)
spin_unlock_irq(&ohci->lock);
if (free_rom)
- dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
- free_rom, free_rom_bus);
+ dmam_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, free_rom, free_rom_bus);
log_selfids(ohci, generation, self_id_count);
@@ -2377,10 +2387,8 @@ static int ohci_enable(struct fw_card *card,
*/
if (config_rom) {
- ohci->next_config_rom =
- dma_alloc_coherent(ohci->card.device, CONFIG_ROM_SIZE,
- &ohci->next_config_rom_bus,
- GFP_KERNEL);
+ ohci->next_config_rom = dmam_alloc_coherent(ohci->card.device, CONFIG_ROM_SIZE,
+ &ohci->next_config_rom_bus, GFP_KERNEL);
if (ohci->next_config_rom == NULL)
return -ENOMEM;
@@ -2472,9 +2480,8 @@ static int ohci_set_config_rom(struct fw_card *card,
* ohci->next_config_rom to NULL (see bus_reset_work).
*/
- next_config_rom =
- dma_alloc_coherent(ohci->card.device, CONFIG_ROM_SIZE,
- &next_config_rom_bus, GFP_KERNEL);
+ next_config_rom = dmam_alloc_coherent(ohci->card.device, CONFIG_ROM_SIZE,
+ &next_config_rom_bus, GFP_KERNEL);
if (next_config_rom == NULL)
return -ENOMEM;
@@ -2507,9 +2514,10 @@ static int ohci_set_config_rom(struct fw_card *card,
spin_unlock_irq(&ohci->lock);
/* If we didn't use the DMA allocation, delete it. */
- if (next_config_rom != NULL)
- dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
- next_config_rom, next_config_rom_bus);
+ if (next_config_rom != NULL) {
+ dmam_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, next_config_rom,
+ next_config_rom_bus);
+ }
/*
* Now initiate a bus reset to have the changes take
@@ -2557,6 +2565,10 @@ static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet)
log_ar_at_event(ohci, 'T', packet->speed, packet->header, 0x20);
driver_data->packet = NULL;
packet->ack = RCODE_CANCELLED;
+
+ // Timestamping on behalf of the hardware.
+ packet->timestamp = cycle_time_to_ohci_tstamp(get_cycle_time(ohci));
+
packet->callback(packet, &ohci->card, packet->ack);
ret = 0;
out:
@@ -3544,6 +3556,19 @@ static inline void pmac_ohci_on(struct pci_dev *dev) {}
static inline void pmac_ohci_off(struct pci_dev *dev) {}
#endif /* CONFIG_PPC_PMAC */
+static void release_ohci(struct device *dev, void *data)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct fw_ohci *ohci = pci_get_drvdata(pdev);
+
+ pmac_ohci_off(pdev);
+
+ ar_context_release(&ohci->ar_response_ctx);
+ ar_context_release(&ohci->ar_request_ctx);
+
+ dev_notice(dev, "removed fw-ohci device\n");
+}
+
static int pci_probe(struct pci_dev *dev,
const struct pci_device_id *ent)
{
@@ -3558,25 +3583,22 @@ static int pci_probe(struct pci_dev *dev,
return -ENOSYS;
}
- ohci = kzalloc(sizeof(*ohci), GFP_KERNEL);
- if (ohci == NULL) {
- err = -ENOMEM;
- goto fail;
- }
-
+ ohci = devres_alloc(release_ohci, sizeof(*ohci), GFP_KERNEL);
+ if (ohci == NULL)
+ return -ENOMEM;
fw_card_initialize(&ohci->card, &ohci_driver, &dev->dev);
-
+ pci_set_drvdata(dev, ohci);
pmac_ohci_on(dev);
+ devres_add(&dev->dev, ohci);
- err = pci_enable_device(dev);
+ err = pcim_enable_device(dev);
if (err) {
dev_err(&dev->dev, "failed to enable OHCI hardware\n");
- goto fail_free;
+ return err;
}
pci_set_master(dev);
pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0);
- pci_set_drvdata(dev, ohci);
spin_lock_init(&ohci->lock);
mutex_init(&ohci->phy_reg_mutex);
@@ -3586,22 +3608,15 @@ static int pci_probe(struct pci_dev *dev,
if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM) ||
pci_resource_len(dev, 0) < OHCI1394_REGISTER_SIZE) {
ohci_err(ohci, "invalid MMIO resource\n");
- err = -ENXIO;
- goto fail_disable;
+ return -ENXIO;
}
- err = pci_request_region(dev, 0, ohci_driver_name);
+ err = pcim_iomap_regions(dev, 1 << 0, ohci_driver_name);
if (err) {
- ohci_err(ohci, "MMIO resource unavailable\n");
- goto fail_disable;
- }
-
- ohci->registers = pci_iomap(dev, 0, OHCI1394_REGISTER_SIZE);
- if (ohci->registers == NULL) {
- ohci_err(ohci, "failed to remap registers\n");
- err = -ENXIO;
- goto fail_iomem;
+ ohci_err(ohci, "request and map MMIO resource unavailable\n");
+ return -ENXIO;
}
+ ohci->registers = pcim_iomap_table(dev)[0];
for (i = 0; i < ARRAY_SIZE(ohci_quirks); i++)
if ((ohci_quirks[i].vendor == dev->vendor) &&
@@ -3622,34 +3637,30 @@ static int pci_probe(struct pci_dev *dev,
*/
BUILD_BUG_ON(AR_BUFFERS * sizeof(struct descriptor) > PAGE_SIZE/4);
BUILD_BUG_ON(SELF_ID_BUF_SIZE > PAGE_SIZE/2);
- ohci->misc_buffer = dma_alloc_coherent(ohci->card.device,
- PAGE_SIZE,
- &ohci->misc_buffer_bus,
- GFP_KERNEL);
- if (!ohci->misc_buffer) {
- err = -ENOMEM;
- goto fail_iounmap;
- }
+ ohci->misc_buffer = dmam_alloc_coherent(&dev->dev, PAGE_SIZE, &ohci->misc_buffer_bus,
+ GFP_KERNEL);
+ if (!ohci->misc_buffer)
+ return -ENOMEM;
err = ar_context_init(&ohci->ar_request_ctx, ohci, 0,
OHCI1394_AsReqRcvContextControlSet);
if (err < 0)
- goto fail_misc_buf;
+ return err;
err = ar_context_init(&ohci->ar_response_ctx, ohci, PAGE_SIZE/4,
OHCI1394_AsRspRcvContextControlSet);
if (err < 0)
- goto fail_arreq_ctx;
+ return err;
err = context_init(&ohci->at_request_ctx, ohci,
OHCI1394_AsReqTrContextControlSet, handle_at_packet);
if (err < 0)
- goto fail_arrsp_ctx;
+ return err;
err = context_init(&ohci->at_response_ctx, ohci,
OHCI1394_AsRspTrContextControlSet, handle_at_packet);
if (err < 0)
- goto fail_atreq_ctx;
+ return err;
reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, ~0);
ohci->ir_context_channels = ~0ULL;
@@ -3658,7 +3669,9 @@ static int pci_probe(struct pci_dev *dev,
ohci->ir_context_mask = ohci->ir_context_support;
ohci->n_ir = hweight32(ohci->ir_context_mask);
size = sizeof(struct iso_context) * ohci->n_ir;
- ohci->ir_context_list = kzalloc(size, GFP_KERNEL);
+ ohci->ir_context_list = devm_kzalloc(&dev->dev, size, GFP_KERNEL);
+ if (!ohci->ir_context_list)
+ return -ENOMEM;
reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, ~0);
ohci->it_context_support = reg_read(ohci, OHCI1394_IsoXmitIntMaskSet);
@@ -3671,12 +3684,9 @@ static int pci_probe(struct pci_dev *dev,
ohci->it_context_mask = ohci->it_context_support;
ohci->n_it = hweight32(ohci->it_context_mask);
size = sizeof(struct iso_context) * ohci->n_it;
- ohci->it_context_list = kzalloc(size, GFP_KERNEL);
-
- if (ohci->it_context_list == NULL || ohci->ir_context_list == NULL) {
- err = -ENOMEM;
- goto fail_contexts;
- }
+ ohci->it_context_list = devm_kzalloc(&dev->dev, size, GFP_KERNEL);
+ if (!ohci->it_context_list)
+ return -ENOMEM;
ohci->self_id = ohci->misc_buffer + PAGE_SIZE/2;
ohci->self_id_bus = ohci->misc_buffer_bus + PAGE_SIZE/2;
@@ -3689,17 +3699,16 @@ static int pci_probe(struct pci_dev *dev,
if (!(ohci->quirks & QUIRK_NO_MSI))
pci_enable_msi(dev);
- if (request_irq(dev->irq, irq_handler,
- pci_dev_msi_enabled(dev) ? 0 : IRQF_SHARED,
- ohci_driver_name, ohci)) {
+ err = devm_request_irq(&dev->dev, dev->irq, irq_handler,
+ pci_dev_msi_enabled(dev) ? 0 : IRQF_SHARED, ohci_driver_name, ohci);
+ if (err < 0) {
ohci_err(ohci, "failed to allocate interrupt %d\n", dev->irq);
- err = -EIO;
goto fail_msi;
}
err = fw_card_add(&ohci->card, max_receive, link_speed, guid);
if (err)
- goto fail_irq;
+ goto fail_msi;
version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff;
ohci_notice(ohci,
@@ -3712,33 +3721,9 @@ static int pci_probe(struct pci_dev *dev,
return 0;
- fail_irq:
- free_irq(dev->irq, ohci);
fail_msi:
pci_disable_msi(dev);
- fail_contexts:
- kfree(ohci->ir_context_list);
- kfree(ohci->it_context_list);
- context_release(&ohci->at_response_ctx);
- fail_atreq_ctx:
- context_release(&ohci->at_request_ctx);
- fail_arrsp_ctx:
- ar_context_release(&ohci->ar_response_ctx);
- fail_arreq_ctx:
- ar_context_release(&ohci->ar_request_ctx);
- fail_misc_buf:
- dma_free_coherent(ohci->card.device, PAGE_SIZE,
- ohci->misc_buffer, ohci->misc_buffer_bus);
- fail_iounmap:
- pci_iounmap(dev, ohci->registers);
- fail_iomem:
- pci_release_region(dev, 0);
- fail_disable:
- pci_disable_device(dev);
- fail_free:
- kfree(ohci);
- pmac_ohci_off(dev);
- fail:
+
return err;
}
@@ -3763,30 +3748,10 @@ static void pci_remove(struct pci_dev *dev)
*/
software_reset(ohci);
- free_irq(dev->irq, ohci);
-
- if (ohci->next_config_rom && ohci->next_config_rom != ohci->config_rom)
- dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
- ohci->next_config_rom, ohci->next_config_rom_bus);
- if (ohci->config_rom)
- dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
- ohci->config_rom, ohci->config_rom_bus);
- ar_context_release(&ohci->ar_request_ctx);
- ar_context_release(&ohci->ar_response_ctx);
- dma_free_coherent(ohci->card.device, PAGE_SIZE,
- ohci->misc_buffer, ohci->misc_buffer_bus);
- context_release(&ohci->at_request_ctx);
- context_release(&ohci->at_response_ctx);
- kfree(ohci->it_context_list);
- kfree(ohci->ir_context_list);
+
pci_disable_msi(dev);
- pci_iounmap(dev, ohci->registers);
- pci_release_region(dev, 0);
- pci_disable_device(dev);
- kfree(ohci);
- pmac_ohci_off(dev);
- dev_notice(&dev->dev, "removed fw-ohci device\n");
+ dev_notice(&dev->dev, "removing fw-ohci device\n");
}
#ifdef CONFIG_PM
diff --git a/drivers/firewire/uapi-test.c b/drivers/firewire/uapi-test.c
new file mode 100644
index 0000000000000..2fcbede4fab1f
--- /dev/null
+++ b/drivers/firewire/uapi-test.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// uapi_test.c - An application of Kunit to check layout of structures exposed to user space for
+// FireWire subsystem.
+//
+// Copyright (c) 2023 Takashi Sakamoto
+
+#include <kunit/test.h>
+#include <linux/firewire-cdev.h>
+
+// Known issue added at v2.6.27 kernel.
+static void structure_layout_event_response(struct kunit *test)
+{
+#if defined(CONFIG_X86_32)
+ // 4 bytes alignment for aggregate type including 8 bytes storage types.
+ KUNIT_EXPECT_EQ(test, 20, sizeof(struct fw_cdev_event_response));
+#else
+ // 8 bytes alignment for aggregate type including 8 bytes storage types.
+ KUNIT_EXPECT_EQ(test, 24, sizeof(struct fw_cdev_event_response));
+#endif
+
+ KUNIT_EXPECT_EQ(test, 0, offsetof(struct fw_cdev_event_response, closure));
+ KUNIT_EXPECT_EQ(test, 8, offsetof(struct fw_cdev_event_response, type));
+ KUNIT_EXPECT_EQ(test, 12, offsetof(struct fw_cdev_event_response, rcode));
+ KUNIT_EXPECT_EQ(test, 16, offsetof(struct fw_cdev_event_response, length));
+ KUNIT_EXPECT_EQ(test, 20, offsetof(struct fw_cdev_event_response, data));
+}
+
+// Added at v6.5.
+static void structure_layout_event_request3(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, 56, sizeof(struct fw_cdev_event_request3));
+
+ KUNIT_EXPECT_EQ(test, 0, offsetof(struct fw_cdev_event_request3, closure));
+ KUNIT_EXPECT_EQ(test, 8, offsetof(struct fw_cdev_event_request3, type));
+ KUNIT_EXPECT_EQ(test, 12, offsetof(struct fw_cdev_event_request3, tcode));
+ KUNIT_EXPECT_EQ(test, 16, offsetof(struct fw_cdev_event_request3, offset));
+ KUNIT_EXPECT_EQ(test, 24, offsetof(struct fw_cdev_event_request3, source_node_id));
+ KUNIT_EXPECT_EQ(test, 28, offsetof(struct fw_cdev_event_request3, destination_node_id));
+ KUNIT_EXPECT_EQ(test, 32, offsetof(struct fw_cdev_event_request3, card));
+ KUNIT_EXPECT_EQ(test, 36, offsetof(struct fw_cdev_event_request3, generation));
+ KUNIT_EXPECT_EQ(test, 40, offsetof(struct fw_cdev_event_request3, handle));
+ KUNIT_EXPECT_EQ(test, 44, offsetof(struct fw_cdev_event_request3, length));
+ KUNIT_EXPECT_EQ(test, 48, offsetof(struct fw_cdev_event_request3, tstamp));
+ KUNIT_EXPECT_EQ(test, 56, offsetof(struct fw_cdev_event_request3, data));
+}
+
+// Added at v6.5.
+static void structure_layout_event_response2(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, 32, sizeof(struct fw_cdev_event_response2));
+
+ KUNIT_EXPECT_EQ(test, 0, offsetof(struct fw_cdev_event_response2, closure));
+ KUNIT_EXPECT_EQ(test, 8, offsetof(struct fw_cdev_event_response2, type));
+ KUNIT_EXPECT_EQ(test, 12, offsetof(struct fw_cdev_event_response2, rcode));
+ KUNIT_EXPECT_EQ(test, 16, offsetof(struct fw_cdev_event_response2, length));
+ KUNIT_EXPECT_EQ(test, 20, offsetof(struct fw_cdev_event_response2, request_tstamp));
+ KUNIT_EXPECT_EQ(test, 24, offsetof(struct fw_cdev_event_response2, response_tstamp));
+ KUNIT_EXPECT_EQ(test, 32, offsetof(struct fw_cdev_event_response2, data));
+}
+
+// Added at v6.5.
+static void structure_layout_event_phy_packet2(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, 24, sizeof(struct fw_cdev_event_phy_packet2));
+
+ KUNIT_EXPECT_EQ(test, 0, offsetof(struct fw_cdev_event_phy_packet2, closure));
+ KUNIT_EXPECT_EQ(test, 8, offsetof(struct fw_cdev_event_phy_packet2, type));
+ KUNIT_EXPECT_EQ(test, 12, offsetof(struct fw_cdev_event_phy_packet2, rcode));
+ KUNIT_EXPECT_EQ(test, 16, offsetof(struct fw_cdev_event_phy_packet2, length));
+ KUNIT_EXPECT_EQ(test, 20, offsetof(struct fw_cdev_event_phy_packet2, tstamp));
+ KUNIT_EXPECT_EQ(test, 24, offsetof(struct fw_cdev_event_phy_packet2, data));
+}
+
+static struct kunit_case structure_layout_test_cases[] = {
+ KUNIT_CASE(structure_layout_event_response),
+ KUNIT_CASE(structure_layout_event_request3),
+ KUNIT_CASE(structure_layout_event_response2),
+ KUNIT_CASE(structure_layout_event_phy_packet2),
+ {}
+};
+
+static struct kunit_suite structure_layout_test_suite = {
+ .name = "firewire-uapi-structure-layout",
+ .test_cases = structure_layout_test_cases,
+};
+kunit_test_suite(structure_layout_test_suite);
+
+MODULE_LICENSE("GPL");
diff --git a/include/linux/firewire.h b/include/linux/firewire.h
index efb6e2cf20343..bd3fc75d4f146 100644
--- a/include/linux/firewire.h
+++ b/include/linux/firewire.h
@@ -261,6 +261,15 @@ typedef void (*fw_packet_callback_t)(struct fw_packet *packet,
typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode,
void *data, size_t length,
void *callback_data);
+typedef void (*fw_transaction_callback_with_tstamp_t)(struct fw_card *card, int rcode,
+ u32 request_tstamp, u32 response_tstamp, void *data,
+ size_t length, void *callback_data);
+
+union fw_transaction_callback {
+ fw_transaction_callback_t without_tstamp;
+ fw_transaction_callback_with_tstamp_t with_tstamp;
+};
+
/*
* This callback handles an inbound request subaction. It is called in
* RCU read-side context, therefore must not sleep.
@@ -312,6 +321,7 @@ struct fw_transaction {
struct fw_card *card;
bool is_split_transaction;
struct timer_list split_timeout_timer;
+ u32 split_timeout_cycle;
struct fw_packet packet;
@@ -319,7 +329,8 @@ struct fw_transaction {
* The data passed to the callback is valid only during the
* callback.
*/
- fw_transaction_callback_t callback;
+ union fw_transaction_callback callback;
+ bool with_tstamp;
void *callback_data;
};
@@ -345,10 +356,71 @@ void fw_send_response(struct fw_card *card,
struct fw_request *request, int rcode);
int fw_get_request_speed(struct fw_request *request);
u32 fw_request_get_timestamp(const struct fw_request *request);
-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_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, union fw_transaction_callback callback,
+ bool with_tstamp, void *callback_data);
+
+/**
+ * fw_send_request() - submit a request packet for transmission to generate callback for response
+ * subaction without time stamp.
+ * @card: interface to send the request at
+ * @t: transaction instance to which the request belongs
+ * @tcode: transaction code
+ * @destination_id: destination node ID, consisting of bus_ID and phy_ID
+ * @generation: bus generation in which request and response are valid
+ * @speed: transmission speed
+ * @offset: 48bit wide offset into destination's address space
+ * @payload: data payload for the request subaction
+ * @length: length of the payload, in bytes
+ * @callback: function to be called when the transaction is completed
+ * @callback_data: data to be passed to the transaction completion callback
+ *
+ * A variation of __fw_send_request() to generate callback for response subaction without time
+ * stamp.
+ */
+static inline 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)
+{
+ union fw_transaction_callback cb = {
+ .without_tstamp = callback,
+ };
+ __fw_send_request(card, t, tcode, destination_id, generation, speed, offset, payload,
+ length, cb, false, callback_data);
+}
+
+/**
+ * fw_send_request_with_tstamp() - submit a request packet for transmission to generate callback for
+ * response with time stamp.
+ * @card: interface to send the request at
+ * @t: transaction instance to which the request belongs
+ * @tcode: transaction code
+ * @destination_id: destination node ID, consisting of bus_ID and phy_ID
+ * @generation: bus generation in which request and response are valid
+ * @speed: transmission speed
+ * @offset: 48bit wide offset into destination's address space
+ * @payload: data payload for the request subaction
+ * @length: length of the payload, in bytes
+ * @callback: function to be called when the transaction is completed
+ * @callback_data: data to be passed to the transaction completion callback
+ *
+ * A variation of __fw_send_request() to generate callback for response subaction with time stamp.
+ */
+static inline void fw_send_request_with_tstamp(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_with_tstamp_t callback,
+ void *callback_data)
+{
+ union fw_transaction_callback cb = {
+ .with_tstamp = callback,
+ };
+ __fw_send_request(card, t, tcode, destination_id, generation, speed, offset, payload,
+ length, cb, true, callback_data);
+}
+
int fw_cancel_transaction(struct fw_card *card,
struct fw_transaction *transaction);
int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
diff --git a/include/uapi/linux/firewire-cdev.h b/include/uapi/linux/firewire-cdev.h
index 92be3ea3c6e0b..1f2c9469f9214 100644
--- a/include/uapi/linux/firewire-cdev.h
+++ b/include/uapi/linux/firewire-cdev.h
@@ -46,6 +46,12 @@
#define FW_CDEV_EVENT_PHY_PACKET_RECEIVED 0x08
#define FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL 0x09
+/* available since kernel version 6.5 */
+#define FW_CDEV_EVENT_REQUEST3 0x0a
+#define FW_CDEV_EVENT_RESPONSE2 0x0b
+#define FW_CDEV_EVENT_PHY_PACKET_SENT2 0x0c
+#define FW_CDEV_EVENT_PHY_PACKET_RECEIVED2 0x0d
+
/**
* struct fw_cdev_event_common - Common part of all fw_cdev_event_* types
* @closure: For arbitrary use by userspace
@@ -103,6 +109,32 @@ struct fw_cdev_event_bus_reset {
* @length: Data length, i.e. the response's payload size in bytes
* @data: Payload data, if any
*
+ * This event is sent instead of &fw_cdev_event_response if the kernel or the client implements
+ * ABI version <= 5. It has the lack of time stamp field comparing to &fw_cdev_event_response2.
+ */
+struct fw_cdev_event_response {
+ __u64 closure;
+ __u32 type;
+ __u32 rcode;
+ __u32 length;
+ __u32 data[];
+};
+
+/**
+ * struct fw_cdev_event_response2 - Sent when a response packet was received
+ * @closure: See &fw_cdev_event_common; set by %FW_CDEV_IOC_SEND_REQUEST
+ * or %FW_CDEV_IOC_SEND_BROADCAST_REQUEST
+ * or %FW_CDEV_IOC_SEND_STREAM_PACKET ioctl
+ * @type: See &fw_cdev_event_common; always %FW_CDEV_EVENT_RESPONSE
+ * @rcode: Response code returned by the remote node
+ * @length: Data length, i.e. the response's payload size in bytes
+ * @request_tstamp: The time stamp of isochronous cycle at which the request was sent.
+ * @response_tstamp: The time stamp of isochronous cycle at which the response was sent.
+ * @padding: Padding to keep the size of structure as multiples of 8 in various architectures
+ * since 4 byte alignment is used for 8 byte of object type in System V ABI for i386
+ * architecture.
+ * @data: Payload data, if any
+ *
* This event is sent when the stack receives a response to an outgoing request
* sent by %FW_CDEV_IOC_SEND_REQUEST ioctl. The payload data for responses
* carrying data (read and lock responses) follows immediately and can be
@@ -112,12 +144,21 @@ struct fw_cdev_event_bus_reset {
* involve response packets. This includes unified write transactions,
* broadcast write transactions, and transmission of asynchronous stream
* packets. @rcode indicates success or failure of such transmissions.
+ *
+ * The value of @request_tstamp expresses the isochronous cycle at which the request was sent to
+ * initiate the transaction. The value of @response_tstamp expresses the isochronous cycle at which
+ * the response arrived to complete the transaction. Each value is unsigned 16 bit integer
+ * containing three low order bits of second field and all 13 bits of cycle field in format of
+ * CYCLE_TIMER register.
*/
-struct fw_cdev_event_response {
+struct fw_cdev_event_response2 {
__u64 closure;
__u32 type;
__u32 rcode;
__u32 length;
+ __u32 request_tstamp;
+ __u32 response_tstamp;
+ __u32 padding;
__u32 data[];
};
@@ -159,6 +200,41 @@ struct fw_cdev_event_request {
* @length: Data length, i.e. the request's payload size in bytes
* @data: Incoming data, if any
*
+ * This event is sent instead of &fw_cdev_event_request3 if the kernel or the client implements
+ * ABI version <= 5. It has the lack of time stamp field comparing to &fw_cdev_event_request3.
+ */
+struct fw_cdev_event_request2 {
+ __u64 closure;
+ __u32 type;
+ __u32 tcode;
+ __u64 offset;
+ __u32 source_node_id;
+ __u32 destination_node_id;
+ __u32 card;
+ __u32 generation;
+ __u32 handle;
+ __u32 length;
+ __u32 data[];
+};
+
+/**
+ * struct fw_cdev_event_request3 - Sent on incoming request to an address region
+ * @closure: See &fw_cdev_event_common; set by %FW_CDEV_IOC_ALLOCATE ioctl
+ * @type: See &fw_cdev_event_common; always %FW_CDEV_EVENT_REQUEST2
+ * @tcode: Transaction code of the incoming request
+ * @offset: The offset into the 48-bit per-node address space
+ * @source_node_id: Sender node ID
+ * @destination_node_id: Destination node ID
+ * @card: The index of the card from which the request came
+ * @generation: Bus generation in which the request is valid
+ * @handle: Reference to the kernel-side pending request
+ * @length: Data length, i.e. the request's payload size in bytes
+ * @tstamp: The time stamp of isochronous cycle at which the request arrived.
+ * @padding: Padding to keep the size of structure as multiples of 8 in various architectures
+ * since 4 byte alignment is used for 8 byte of object type in System V ABI for i386
+ * architecture.
+ * @data: Incoming data, if any
+ *
* This event is sent when the stack receives an incoming request to an address
* region registered using the %FW_CDEV_IOC_ALLOCATE ioctl. The request is
* guaranteed to be completely contained in the specified region. Userspace is
@@ -191,10 +267,14 @@ struct fw_cdev_event_request {
* sent.
*
* If the client subsequently needs to initiate requests to the sender node of
- * an &fw_cdev_event_request2, it needs to use a device file with matching
+ * an &fw_cdev_event_request3, it needs to use a device file with matching
* card index, node ID, and generation for outbound requests.
+ *
+ * @tstamp is isochronous cycle at which the request arrived. It is 16 bit integer value and the
+ * higher 3 bits expresses three low order bits of second field in the format of CYCLE_TIME
+ * register and the rest 13 bits expresses cycle field.
*/
-struct fw_cdev_event_request2 {
+struct fw_cdev_event_request3 {
__u64 closure;
__u32 type;
__u32 tcode;
@@ -205,6 +285,8 @@ struct fw_cdev_event_request2 {
__u32 generation;
__u32 handle;
__u32 length;
+ __u32 tstamp;
+ __u32 padding;
__u32 data[];
};
@@ -341,20 +423,59 @@ struct fw_cdev_event_iso_resource {
* @type: %FW_CDEV_EVENT_PHY_PACKET_SENT or %..._RECEIVED
* @rcode: %RCODE_..., indicates success or failure of transmission
* @length: Data length in bytes
+ * @data: Incoming data for %FW_CDEV_IOC_RECEIVE_PHY_PACKETS. For %FW_CDEV_IOC_SEND_PHY_PACKET
+ * the field has the same data in the request, thus the length of 8 bytes.
+ *
+ * This event is sent instead of &fw_cdev_event_phy_packet2 if the kernel or
+ * the client implements ABI version <= 5. It has the lack of time stamp field comparing to
+ * &fw_cdev_event_phy_packet2.
+ */
+struct fw_cdev_event_phy_packet {
+ __u64 closure;
+ __u32 type;
+ __u32 rcode;
+ __u32 length;
+ __u32 data[];
+};
+
+/**
+ * struct fw_cdev_event_phy_packet2 - A PHY packet was transmitted or received with time stamp.
+ * @closure: See &fw_cdev_event_common; set by %FW_CDEV_IOC_SEND_PHY_PACKET
+ * or %FW_CDEV_IOC_RECEIVE_PHY_PACKETS ioctl
+ * @type: %FW_CDEV_EVENT_PHY_PACKET_SENT2 or %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2
+ * @rcode: %RCODE_..., indicates success or failure of transmission
+ * @length: Data length in bytes
+ * @tstamp: For %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2, the time stamp of isochronous cycle at
+ * which the packet arrived. For %FW_CDEV_EVENT_PHY_PACKET_SENT2 and non-ping packet,
+ * the time stamp of isochronous cycle at which the packet was sent. For ping packet,
+ * the tick count for round-trip time measured by 1394 OHCI controller.
+ * The time stamp of isochronous cycle at which either the response was sent for
+ * %FW_CDEV_EVENT_PHY_PACKET_SENT2 or the request arrived for
+ * %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2.
* @data: Incoming data
*
- * If @type is %FW_CDEV_EVENT_PHY_PACKET_SENT, @length is 0 and @data empty,
- * except in case of a ping packet: Then, @length is 4, and @data[0] is the
- * ping time in 49.152MHz clocks if @rcode is %RCODE_COMPLETE.
+ * If @type is %FW_CDEV_EVENT_PHY_PACKET_SENT2, @length is 8 and @data consists of the two PHY
+ * packet quadlets to be sent, in host byte order,
*
- * If @type is %FW_CDEV_EVENT_PHY_PACKET_RECEIVED, @length is 8 and @data
- * consists of the two PHY packet quadlets, in host byte order.
+ * If @type is %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2, @length is 8 and @data consists of the two PHY
+ * packet quadlets, in host byte order.
+ *
+ * For %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2, the @tstamp is the isochronous cycle at which the
+ * packet arrived. It is 16 bit integer value and the higher 3 bits expresses three low order bits
+ * of second field and the rest 13 bits expresses cycle field in the format of CYCLE_TIME register.
+ *
+ * For %FW_CDEV_EVENT_PHY_PACKET_SENT2, the @tstamp has different meanings whether to sent the
+ * packet for ping or not. If it's not for ping, the @tstamp is the isochronous cycle at which the
+ * packet was sent, and use the same format as the case of %FW_CDEV_EVENT_PHY_PACKET_SENT2. If it's
+ * for ping, the @tstamp is for round-trip time measured by 1394 OHCI controller with 42.195 MHz
+ * resolution.
*/
-struct fw_cdev_event_phy_packet {
+struct fw_cdev_event_phy_packet2 {
__u64 closure;
__u32 type;
__u32 rcode;
__u32 length;
+ __u32 tstamp;
__u32 data[];
};
@@ -375,6 +496,11 @@ struct fw_cdev_event_phy_packet {
* %FW_CDEV_EVENT_PHY_PACKET_SENT or
* %FW_CDEV_EVENT_PHY_PACKET_RECEIVED
*
+ * @request3: Valid if @common.type == %FW_CDEV_EVENT_REQUEST3
+ * @response2: Valid if @common.type == %FW_CDEV_EVENT_RESPONSE2
+ * @phy_packet2: Valid if @common.type == %FW_CDEV_EVENT_PHY_PACKET_SENT2 or
+ * %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2
+ *
* Convenience union for userspace use. Events could be read(2) into an
* appropriately aligned char buffer and then cast to this union for further
* processing. Note that for a request, response or iso_interrupt event,
@@ -393,6 +519,9 @@ union fw_cdev_event {
struct fw_cdev_event_iso_interrupt_mc iso_interrupt_mc; /* added in 2.6.36 */
struct fw_cdev_event_iso_resource iso_resource; /* added in 2.6.30 */
struct fw_cdev_event_phy_packet phy_packet; /* added in 2.6.36 */
+ struct fw_cdev_event_request3 request3; /* added in 6.5 */
+ struct fw_cdev_event_response2 response2; /* added in 6.5 */
+ struct fw_cdev_event_phy_packet2 phy_packet2; /* added in 6.5 */
};
/* available since kernel version 2.6.22 */
@@ -457,6 +586,11 @@ union fw_cdev_event {
* 5 (3.4) - send %FW_CDEV_EVENT_ISO_INTERRUPT events when needed to
* avoid dropping data
* - added %FW_CDEV_IOC_FLUSH_ISO
+ * 6 (6.5) - added some event for subactions of asynchronous transaction with time stamp
+ * - %FW_CDEV_EVENT_REQUEST3
+ * - %FW_CDEV_EVENT_RESPONSE2
+ * - %FW_CDEV_EVENT_PHY_PACKET_SENT2
+ * - %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2
*/
/**
@@ -502,11 +636,11 @@ struct fw_cdev_get_info {
* @data: Userspace pointer to payload
* @generation: The bus generation where packet is valid
*
- * Send a request to the device. This ioctl implements all outgoing requests.
- * Both quadlet and block request specify the payload as a pointer to the data
- * in the @data field. Once the transaction completes, the kernel writes an
- * &fw_cdev_event_response event back. The @closure field is passed back to
- * user space in the response event.
+ * Send a request to the device. This ioctl implements all outgoing requests. Both quadlet and
+ * block request specify the payload as a pointer to the data in the @data field. Once the
+ * transaction completes, the kernel writes either &fw_cdev_event_response event or
+ * &fw_cdev_event_response event back. The @closure field is passed back to user space in the
+ * response event.
*/
struct fw_cdev_send_request {
__u32 tcode;
@@ -989,10 +1123,9 @@ struct fw_cdev_allocate_iso_resource {
* @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 which is listening to the specified channel. The kernel
- * writes an &fw_cdev_event_response event which indicates success or failure of
- * the transmission.
+ * The %FW_CDEV_IOC_SEND_STREAM_PACKET ioctl sends an asynchronous stream packet to every device
+ * which is listening to the specified channel. The kernel writes either &fw_cdev_event_response
+ * event or &fw_cdev_event_response2 event which indicates success or failure of the transmission.
*/
struct fw_cdev_send_stream_packet {
__u32 length;
@@ -1011,8 +1144,8 @@ struct fw_cdev_send_stream_packet {
* @data: First and second quadlet of the PHY packet
* @generation: The bus generation where packet is valid
*
- * The %FW_CDEV_IOC_SEND_PHY_PACKET ioctl sends a PHY packet to all nodes
- * on the same card as this device. After transmission, an
+ * The %FW_CDEV_IOC_SEND_PHY_PACKET ioctl sends a PHY packet to all nodes on the same card as this
+ * device. After transmission, either %FW_CDEV_EVENT_PHY_PACKET_SENT event or
* %FW_CDEV_EVENT_PHY_PACKET_SENT event is generated.
*
* The payload @data\[\] shall be specified in host byte order. Usually,
@@ -1031,8 +1164,9 @@ struct fw_cdev_send_phy_packet {
* struct fw_cdev_receive_phy_packets - start reception of PHY packets
* @closure: Passed back to userspace in phy packet events
*
- * This ioctl activates issuing of %FW_CDEV_EVENT_PHY_PACKET_RECEIVED due to
- * incoming PHY packets from any node on the same bus as the device.
+ * This ioctl activates issuing of either %FW_CDEV_EVENT_PHY_PACKET_RECEIVED or
+ * %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2 due to incoming PHY packets from any node on the same bus
+ * as the device.
*
* The ioctl is only permitted on device files which represent a local node.
*/