summaryrefslogtreecommitdiff
path: root/drivers
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 /drivers
parentf1962207150c8b602e980616f04b37ea4e64bb9f (diff)
parent3ff256751a2853e1ffaa36958ff933ccc98c6cb5 (diff)
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 ...
Diffstat (limited to 'drivers')
-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
11 files changed, 461 insertions, 204 deletions
diff --git a/drivers/firewire/.kunitconfig b/drivers/firewire/.kunitconfig
new file mode 100644
index 000000000000..1599e069395f
--- /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 ec00a6f70da8..0a6596b027db 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 e58c8c794778..b24b2879ac34 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 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);
diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c
index aa597cda0d88..a3104e35412c 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 f40c81534381..88466b663482 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 a9f70c96323e..130b95aca629 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 eafa4eaae737..2a05f411328f 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 538bd677c254..7a4d1a478e33 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 17c9d825188b..7e88fd489741 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 000000000000..2fcbede4fab1
--- /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");