firewire updates for v6.10

During the development period of v6.8 kernel, it became evident that there
 was a lack of helper utilities to trace the initial state of bus, while
 investigating certain PHYs compliant with different versions of IEEE 1394
 specification.
 
 This series of changes includes the addition of tracepoints events,
 provided by 'firewire' subsystem. These events enable tracing of how
 firewire core functions during bus reset and asynchronous communication
 over IEEE 1394 bus.
 
 When implementing the tracepoints events, it was found that the existing
 serialization and deserialization helpers for several types of
 asynchronous packets are scattered across both firewire-core and
 firewire-ohci kernel modules. A set of inline functions is newly added
 to address it, along with some KUnit tests, serving as the foundation for
 the tracepoints events. This renders the dispersed code obsolete.
 
 The remaining changes constitute the final steps in phasing out the usage
 of deprecated PCI MSI APIs, in continuation from the previous version.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQE66IEYNDXNBPeGKSsLtaWM8LwEwUCZkM2QAAKCRCsLtaWM8Lw
 E44iAP9BWtYRNqRVR6eg+auUYro0ce5+0R5lmkfb7kkgv3AS7QEAsJjev7uF5Hfb
 kpCCFC8Imb29govdgH8sPT2lYdSk4AM=
 =78E1
 -----END PGP SIGNATURE-----

Merge tag 'firewire-updates-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394

Pull firewire updates from Takashi Sakamoto:
 "During the development period of v6.8 kernel, it became evident that
  there was a lack of helper utilities to trace the initial state of
  bus, while investigating certain PHYs compliant with different
  versions of IEEE 1394 specification.

  This series of changes includes the addition of tracepoints events,
  provided by 'firewire' subsystem. These events enable tracing of how
  firewire core functions during bus reset and asynchronous
  communication over IEEE 1394 bus.

  When implementing the tracepoints events, it was found that the
  existing serialization and deserialization helpers for several types
  of asynchronous packets are scattered across both firewire-core and
  firewire-ohci kernel modules. A set of inline functions is newly added
  to address it, along with some KUnit tests, serving as the foundation
  for the tracepoints events. This renders the dispersed code obsolete.

  The remaining changes constitute the final steps in phasing out the
  usage of deprecated PCI MSI APIs, in continuation from the previous
  version"

* tag 'firewire-updates-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394: (29 commits)
  firewire: obsolete usage of *-objs in Makefile for KUnit test
  firewire: core: remove flag and width from u64 formats of tracepoints events
  firewire: core: fix type of timestamp for async_inbound_template tracepoints events
  firewire: core: add tracepoint event for handling bus reset
  Revert "firewire: core: option to log bus reset initiation"
  firewire: core: add tracepoints events for initiating bus reset
  firewire: ohci: obsolete OHCI_PARAM_DEBUG_BUSRESETS from debug module parameter
  firewire: ohci: add bus-reset event for initial set of handled irq
  firewire: core: add tracepoints event for asynchronous inbound phy packet
  firewire: core/cdev: add tracepoints events for asynchronous phy packet
  firewire: core: add tracepoints events for asynchronous outbound response
  firewire: core: add tracepoint event for asynchronous inbound request
  firewire: core: add tracepoints event for asynchronous inbound response
  firewire: core: add tracepoints events for asynchronous outbound request
  firewire: core: add support for Linux kernel tracepoints
  firewire: core: replace local macros with common inline functions for isochronous packet header
  firewire: core: add common macro to serialize/deserialize isochronous packet header
  firewire: core: obsolete tcode check macros with inline functions
  firewire: ohci: replace hard-coded values with common macros
  firewire: ohci: replace hard-coded values with inline functions for asynchronous packet header
  ...
This commit is contained in:
Linus Torvalds 2024-05-14 18:57:22 -07:00
commit b850dc206a
14 changed files with 1421 additions and 196 deletions

View file

@ -3,3 +3,4 @@ CONFIG_PCI=y
CONFIG_FIREWIRE=y
CONFIG_FIREWIRE_KUNIT_UAPI_TEST=y
CONFIG_FIREWIRE_KUNIT_DEVICE_ATTRIBUTE_TEST=y
CONFIG_FIREWIRE_KUNIT_PACKET_SERDES_TEST=y

View file

@ -50,6 +50,22 @@ config FIREWIRE_KUNIT_DEVICE_ATTRIBUTE_TEST
For more information on KUnit and unit tests in general, refer
to the KUnit documentation in Documentation/dev-tools/kunit/.
config FIREWIRE_KUNIT_PACKET_SERDES_TEST
tristate "KUnit tests for packet serialization/deserialization" if !KUNIT_ALL_TESTS
depends on FIREWIRE && KUNIT
default KUNIT_ALL_TESTS
help
This builds the KUnit tests for packet serialization and
deserialization.
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

View file

@ -3,7 +3,7 @@
# Makefile for the Linux IEEE 1394 implementation
#
firewire-core-y += core-card.o core-cdev.o core-device.o \
firewire-core-y += core-trace.o core-card.o core-cdev.o core-device.o \
core-iso.o core-topology.o core-transaction.o
firewire-ohci-y += ohci.o
firewire-sbp2-y += sbp2.o
@ -16,5 +16,5 @@ 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
obj-$(CONFIG_FIREWIRE_KUNIT_UAPI_TEST) += uapi-test.o
obj-$(CONFIG_FIREWIRE_KUNIT_PACKET_SERDES_TEST) += packet-serdes-test.o

View file

@ -23,6 +23,7 @@
#include <asm/byteorder.h>
#include "core.h"
#include <trace/events/firewire.h>
#define define_fw_printk_level(func, kern_level) \
void func(const struct fw_card *card, const char *fmt, ...) \
@ -221,11 +222,15 @@ static int reset_bus(struct fw_card *card, bool short_reset)
int reg = short_reset ? 5 : 1;
int bit = short_reset ? PHY_BUS_SHORT_RESET : PHY_BUS_RESET;
trace_bus_reset_initiate(card->generation, short_reset);
return card->driver->update_phy_reg(card, reg, 0, bit);
}
void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset)
{
trace_bus_reset_schedule(card->generation, short_reset);
/* We don't try hard to sort out requests of long vs. short resets. */
card->br_short = short_reset;
@ -244,6 +249,8 @@ static void br_work(struct work_struct *work)
/* Delay for 2s after last reset per IEEE 1394 clause 8.2.1. */
if (card->reset_jiffies != 0 &&
time_before64(get_jiffies_64(), card->reset_jiffies + 2 * HZ)) {
trace_bus_reset_postpone(card->generation, card->br_short);
if (!queue_delayed_work(fw_workqueue, &card->br_work, 2 * HZ))
fw_card_put(card);
return;

View file

@ -35,6 +35,7 @@
#include "core.h"
#include <trace/events/firewire.h>
/*
* ABI version history is documented in linux/firewire-cdev.h.
@ -1558,6 +1559,9 @@ static void outbound_phy_packet_callback(struct fw_packet *packet,
struct client *e_client = e->client;
u32 rcode;
trace_async_phy_outbound_complete((uintptr_t)packet, status, packet->generation,
packet->timestamp);
switch (status) {
// expected:
case ACK_COMPLETE:
@ -1655,6 +1659,9 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg)
memcpy(pp->data, a->data, sizeof(a->data));
}
trace_async_phy_outbound_initiate((uintptr_t)&e->p, e->p.generation, e->p.header[1],
e->p.header[2]);
card->driver->send_request(card, &e->p);
return 0;

View file

@ -20,6 +20,7 @@
#include <asm/byteorder.h>
#include "core.h"
#include <trace/events/firewire.h>
#define SELF_ID_PHY_ID(q) (((q) >> 24) & 0x3f)
#define SELF_ID_EXTENDED(q) (((q) >> 23) & 0x01)
@ -507,6 +508,8 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation,
struct fw_node *local_node;
unsigned long flags;
trace_bus_reset_handle(generation, node_id, bm_abdicate, self_ids, self_id_count);
spin_lock_irqsave(&card->lock, flags);
/*

View file

@ -0,0 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2024 Takashi Sakamoto
#define CREATE_TRACE_POINTS
#include <trace/events/firewire.h>

View file

@ -29,29 +29,11 @@
#include <asm/byteorder.h>
#include "core.h"
#include <trace/events/firewire.h>
#include "packet-header-definitions.h"
#define HEADER_PRI(pri) ((pri) << 0)
#define HEADER_TCODE(tcode) ((tcode) << 4)
#define HEADER_RETRY(retry) ((retry) << 8)
#define HEADER_TLABEL(tlabel) ((tlabel) << 10)
#define HEADER_DESTINATION(destination) ((destination) << 16)
#define HEADER_SOURCE(source) ((source) << 16)
#define HEADER_RCODE(rcode) ((rcode) << 12)
#define HEADER_OFFSET_HIGH(offset_high) ((offset_high) << 0)
#define HEADER_DATA_LENGTH(length) ((length) << 16)
#define HEADER_EXTENDED_TCODE(tcode) ((tcode) << 0)
#define HEADER_GET_TCODE(q) (((q) >> 4) & 0x0f)
#define HEADER_GET_TLABEL(q) (((q) >> 10) & 0x3f)
#define HEADER_GET_RCODE(q) (((q) >> 12) & 0x0f)
#define HEADER_GET_DESTINATION(q) (((q) >> 16) & 0xffff)
#define HEADER_GET_SOURCE(q) (((q) >> 16) & 0xffff)
#define HEADER_GET_OFFSET_HIGH(q) (((q) >> 0) & 0xffff)
#define HEADER_GET_DATA_LENGTH(q) (((q) >> 16) & 0xffff)
#define HEADER_GET_EXTENDED_TCODE(q) (((q) >> 0) & 0xffff)
#define HEADER_DESTINATION_IS_BROADCAST(q) \
(((q) & HEADER_DESTINATION(0x3f)) == HEADER_DESTINATION(0x3f))
#define HEADER_DESTINATION_IS_BROADCAST(header) \
((async_header_get_destination(header) & 0x3f) == 0x3f)
#define PHY_PACKET_CONFIG 0x0
#define PHY_PACKET_LINK_ON 0x1
@ -192,6 +174,9 @@ static void transmit_complete_callback(struct fw_packet *packet,
struct fw_transaction *t =
container_of(packet, struct fw_transaction, packet);
trace_async_request_outbound_complete((uintptr_t)t, packet->generation, packet->speed,
status, packet->timestamp);
switch (status) {
case ACK_COMPLETE:
close_transaction(t, card, RCODE_COMPLETE, packet->timestamp);
@ -231,10 +216,11 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
int ext_tcode;
if (tcode == TCODE_STREAM_DATA) {
packet->header[0] =
HEADER_DATA_LENGTH(length) |
destination_id |
HEADER_TCODE(TCODE_STREAM_DATA);
// The value of destination_id argument should include tag, channel, and sy fields
// as isochronous packet header has.
packet->header[0] = destination_id;
isoc_header_set_data_length(packet->header, length);
isoc_header_set_tcode(packet->header, TCODE_STREAM_DATA);
packet->header_length = 4;
packet->payload = payload;
packet->payload_length = length;
@ -248,28 +234,24 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
} else
ext_tcode = 0;
packet->header[0] =
HEADER_RETRY(RETRY_X) |
HEADER_TLABEL(tlabel) |
HEADER_TCODE(tcode) |
HEADER_DESTINATION(destination_id);
packet->header[1] =
HEADER_OFFSET_HIGH(offset >> 32) | HEADER_SOURCE(source_id);
packet->header[2] =
offset;
async_header_set_retry(packet->header, RETRY_X);
async_header_set_tlabel(packet->header, tlabel);
async_header_set_tcode(packet->header, tcode);
async_header_set_destination(packet->header, destination_id);
async_header_set_source(packet->header, source_id);
async_header_set_offset(packet->header, offset);
switch (tcode) {
case TCODE_WRITE_QUADLET_REQUEST:
packet->header[3] = *(u32 *)payload;
async_header_set_quadlet_data(packet->header, *(u32 *)payload);
packet->header_length = 16;
packet->payload_length = 0;
break;
case TCODE_LOCK_REQUEST:
case TCODE_WRITE_BLOCK_REQUEST:
packet->header[3] =
HEADER_DATA_LENGTH(length) |
HEADER_EXTENDED_TCODE(ext_tcode);
async_header_set_data_length(packet->header, length);
async_header_set_extended_tcode(packet->header, ext_tcode);
packet->header_length = 16;
packet->payload = payload;
packet->payload_length = length;
@ -281,9 +263,8 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
break;
case TCODE_READ_BLOCK_REQUEST:
packet->header[3] =
HEADER_DATA_LENGTH(length) |
HEADER_EXTENDED_TCODE(ext_tcode);
async_header_set_data_length(packet->header, length);
async_header_set_extended_tcode(packet->header, ext_tcode);
packet->header_length = 16;
packet->payload_length = 0;
break;
@ -417,6 +398,9 @@ void __fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode
spin_unlock_irqrestore(&card->lock, flags);
trace_async_request_outbound_initiate((uintptr_t)t, generation, speed, t->packet.header, payload,
tcode_is_read_request(tcode) ? 0 : length / 4);
card->driver->send_request(card, &t->packet);
}
EXPORT_SYMBOL_GPL(__fw_send_request);
@ -479,6 +463,8 @@ static DECLARE_COMPLETION(phy_config_done);
static void transmit_phy_packet_callback(struct fw_packet *packet,
struct fw_card *card, int status)
{
trace_async_phy_outbound_complete((uintptr_t)packet, packet->generation, status,
packet->timestamp);
complete(&phy_config_done);
}
@ -517,6 +503,10 @@ void fw_send_phy_config(struct fw_card *card,
phy_config_packet.generation = generation;
reinit_completion(&phy_config_done);
trace_async_phy_outbound_initiate((uintptr_t)&phy_config_packet,
phy_config_packet.generation, phy_config_packet.header[1],
phy_config_packet.header[2]);
card->driver->send_request(card, &phy_config_packet);
wait_for_completion_timeout(&phy_config_done, timeout);
@ -655,7 +645,7 @@ EXPORT_SYMBOL(fw_core_remove_address_handler);
struct fw_request {
struct kref kref;
struct fw_packet response;
u32 request_header[4];
u32 request_header[ASYNC_HEADER_QUADLET_COUNT];
int ack;
u32 timestamp;
u32 length;
@ -684,6 +674,9 @@ static void free_response_callback(struct fw_packet *packet,
{
struct fw_request *request = container_of(packet, struct fw_request, response);
trace_async_response_outbound_complete((uintptr_t)request, packet->generation,
packet->speed, status, packet->timestamp);
// Decrease the reference count since not at in-flight.
fw_request_put(request);
@ -695,7 +688,7 @@ int fw_get_response_length(struct fw_request *r)
{
int tcode, ext_tcode, data_length;
tcode = HEADER_GET_TCODE(r->request_header[0]);
tcode = async_header_get_tcode(r->request_header);
switch (tcode) {
case TCODE_WRITE_QUADLET_REQUEST:
@ -706,12 +699,12 @@ int fw_get_response_length(struct fw_request *r)
return 4;
case TCODE_READ_BLOCK_REQUEST:
data_length = HEADER_GET_DATA_LENGTH(r->request_header[3]);
data_length = async_header_get_data_length(r->request_header);
return data_length;
case TCODE_LOCK_REQUEST:
ext_tcode = HEADER_GET_EXTENDED_TCODE(r->request_header[3]);
data_length = HEADER_GET_DATA_LENGTH(r->request_header[3]);
ext_tcode = async_header_get_extended_tcode(r->request_header);
data_length = async_header_get_data_length(r->request_header);
switch (ext_tcode) {
case EXTCODE_FETCH_ADD:
case EXTCODE_LITTLE_ADD:
@ -731,46 +724,42 @@ void fw_fill_response(struct fw_packet *response, u32 *request_header,
{
int tcode, tlabel, extended_tcode, source, destination;
tcode = HEADER_GET_TCODE(request_header[0]);
tlabel = HEADER_GET_TLABEL(request_header[0]);
source = HEADER_GET_DESTINATION(request_header[0]);
destination = HEADER_GET_SOURCE(request_header[1]);
extended_tcode = HEADER_GET_EXTENDED_TCODE(request_header[3]);
tcode = async_header_get_tcode(request_header);
tlabel = async_header_get_tlabel(request_header);
source = async_header_get_destination(request_header); // Exchange.
destination = async_header_get_source(request_header); // Exchange.
extended_tcode = async_header_get_extended_tcode(request_header);
response->header[0] =
HEADER_RETRY(RETRY_1) |
HEADER_TLABEL(tlabel) |
HEADER_DESTINATION(destination);
response->header[1] =
HEADER_SOURCE(source) |
HEADER_RCODE(rcode);
response->header[2] = 0;
async_header_set_retry(response->header, RETRY_1);
async_header_set_tlabel(response->header, tlabel);
async_header_set_destination(response->header, destination);
async_header_set_source(response->header, source);
async_header_set_rcode(response->header, rcode);
response->header[2] = 0; // The field is reserved.
switch (tcode) {
case TCODE_WRITE_QUADLET_REQUEST:
case TCODE_WRITE_BLOCK_REQUEST:
response->header[0] |= HEADER_TCODE(TCODE_WRITE_RESPONSE);
async_header_set_tcode(response->header, TCODE_WRITE_RESPONSE);
response->header_length = 12;
response->payload_length = 0;
break;
case TCODE_READ_QUADLET_REQUEST:
response->header[0] |=
HEADER_TCODE(TCODE_READ_QUADLET_RESPONSE);
async_header_set_tcode(response->header, TCODE_READ_QUADLET_RESPONSE);
if (payload != NULL)
response->header[3] = *(u32 *)payload;
async_header_set_quadlet_data(response->header, *(u32 *)payload);
else
response->header[3] = 0;
async_header_set_quadlet_data(response->header, 0);
response->header_length = 16;
response->payload_length = 0;
break;
case TCODE_READ_BLOCK_REQUEST:
case TCODE_LOCK_REQUEST:
response->header[0] |= HEADER_TCODE(tcode + 2);
response->header[3] =
HEADER_DATA_LENGTH(length) |
HEADER_EXTENDED_TCODE(extended_tcode);
async_header_set_tcode(response->header, tcode + 2);
async_header_set_data_length(response->header, length);
async_header_set_extended_tcode(response->header, extended_tcode);
response->header_length = 16;
response->payload = payload;
response->payload_length = length;
@ -807,7 +796,7 @@ static struct fw_request *allocate_request(struct fw_card *card,
u32 *data, length;
int request_tcode;
request_tcode = HEADER_GET_TCODE(p->header[0]);
request_tcode = async_header_get_tcode(p->header);
switch (request_tcode) {
case TCODE_WRITE_QUADLET_REQUEST:
data = &p->header[3];
@ -817,7 +806,7 @@ static struct fw_request *allocate_request(struct fw_card *card,
case TCODE_WRITE_BLOCK_REQUEST:
case TCODE_LOCK_REQUEST:
data = p->payload;
length = HEADER_GET_DATA_LENGTH(p->header[3]);
length = async_header_get_data_length(p->header);
break;
case TCODE_READ_QUADLET_REQUEST:
@ -827,7 +816,7 @@ static struct fw_request *allocate_request(struct fw_card *card,
case TCODE_READ_BLOCK_REQUEST:
data = NULL;
length = HEADER_GET_DATA_LENGTH(p->header[3]);
length = async_header_get_data_length(p->header);
break;
default:
@ -870,24 +859,30 @@ static struct fw_request *allocate_request(struct fw_card *card,
void fw_send_response(struct fw_card *card,
struct fw_request *request, int rcode)
{
u32 *data = NULL;
unsigned int data_length = 0;
/* unified transaction or broadcast transaction: don't respond */
if (request->ack != ACK_PENDING ||
HEADER_DESTINATION_IS_BROADCAST(request->request_header[0])) {
HEADER_DESTINATION_IS_BROADCAST(request->request_header)) {
fw_request_put(request);
return;
}
if (rcode == RCODE_COMPLETE)
fw_fill_response(&request->response, request->request_header,
rcode, request->data,
fw_get_response_length(request));
else
fw_fill_response(&request->response, request->request_header,
rcode, NULL, 0);
if (rcode == RCODE_COMPLETE) {
data = request->data;
data_length = fw_get_response_length(request);
}
fw_fill_response(&request->response, request->request_header, rcode, data, data_length);
// Increase the reference count so that the object is kept during in-flight.
fw_request_get(request);
trace_async_response_outbound_initiate((uintptr_t)request, request->response.generation,
request->response.speed, request->response.header,
data, data ? data_length / 4 : 0);
card->driver->send_response(card, &request->response);
}
EXPORT_SYMBOL(fw_send_response);
@ -926,11 +921,11 @@ static void handle_exclusive_region_request(struct fw_card *card,
struct fw_address_handler *handler;
int tcode, destination, source;
destination = HEADER_GET_DESTINATION(p->header[0]);
source = HEADER_GET_SOURCE(p->header[1]);
tcode = HEADER_GET_TCODE(p->header[0]);
destination = async_header_get_destination(p->header);
source = async_header_get_source(p->header);
tcode = async_header_get_tcode(p->header);
if (tcode == TCODE_LOCK_REQUEST)
tcode = 0x10 + HEADER_GET_EXTENDED_TCODE(p->header[3]);
tcode = 0x10 + async_header_get_extended_tcode(p->header);
rcu_read_lock();
handler = lookup_enclosing_address_handler(&address_handler_list,
@ -963,9 +958,9 @@ static void handle_fcp_region_request(struct fw_card *card,
return;
}
tcode = HEADER_GET_TCODE(p->header[0]);
destination = HEADER_GET_DESTINATION(p->header[0]);
source = HEADER_GET_SOURCE(p->header[1]);
tcode = async_header_get_tcode(p->header);
destination = async_header_get_destination(p->header);
source = async_header_get_source(p->header);
if (tcode != TCODE_WRITE_QUADLET_REQUEST &&
tcode != TCODE_WRITE_BLOCK_REQUEST) {
@ -993,11 +988,15 @@ void fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
{
struct fw_request *request;
unsigned long long offset;
unsigned int tcode;
if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE)
return;
if (TCODE_IS_LINK_INTERNAL(HEADER_GET_TCODE(p->header[0]))) {
tcode = async_header_get_tcode(p->header);
if (tcode_is_link_internal(tcode)) {
trace_async_phy_inbound((uintptr_t)p, p->generation, p->ack, p->timestamp,
p->header[1], p->header[2]);
fw_cdev_handle_phy_packet(card, p);
return;
}
@ -1008,8 +1007,11 @@ void fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
return;
}
offset = ((u64)HEADER_GET_OFFSET_HIGH(p->header[1]) << 32) |
p->header[2];
trace_async_request_inbound((uintptr_t)request, p->generation, p->speed, p->ack,
p->timestamp, p->header, request->data,
tcode_is_read_request(tcode) ? 0 : request->length / 4);
offset = async_header_get_offset(p->header);
if (!is_in_fcp_region(offset, request->length))
handle_exclusive_region_request(card, p, request, offset);
@ -1027,10 +1029,39 @@ void fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
size_t data_length;
int tcode, tlabel, source, rcode;
tcode = HEADER_GET_TCODE(p->header[0]);
tlabel = HEADER_GET_TLABEL(p->header[0]);
source = HEADER_GET_SOURCE(p->header[1]);
rcode = HEADER_GET_RCODE(p->header[1]);
tcode = async_header_get_tcode(p->header);
tlabel = async_header_get_tlabel(p->header);
source = async_header_get_source(p->header);
rcode = async_header_get_rcode(p->header);
// FIXME: sanity check packet, is length correct, does tcodes
// and addresses match to the transaction request queried later.
//
// For the tracepoints event, let us decode the header here against the concern.
switch (tcode) {
case TCODE_READ_QUADLET_RESPONSE:
data = (u32 *) &p->header[3];
data_length = 4;
break;
case TCODE_WRITE_RESPONSE:
data = NULL;
data_length = 0;
break;
case TCODE_READ_BLOCK_RESPONSE:
case TCODE_LOCK_RESPONSE:
data = p->payload;
data_length = async_header_get_data_length(p->header);
break;
default:
/* Should never happen, this is just to shut up gcc. */
data = NULL;
data_length = 0;
break;
}
spin_lock_irqsave(&card->lock, flags);
list_for_each_entry(iter, &card->transaction_list, link) {
@ -1047,6 +1078,9 @@ void fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
}
spin_unlock_irqrestore(&card->lock, flags);
trace_async_response_inbound((uintptr_t)t, p->generation, p->speed, p->ack, p->timestamp,
p->header, data, data_length / 4);
if (!t) {
timed_out:
fw_notice(card, "unsolicited response (source %x, tlabel %x)\n",
@ -1054,35 +1088,6 @@ void fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
return;
}
/*
* FIXME: sanity check packet, is length correct, does tcodes
* and addresses match.
*/
switch (tcode) {
case TCODE_READ_QUADLET_RESPONSE:
data = (u32 *) &p->header[3];
data_length = 4;
break;
case TCODE_WRITE_RESPONSE:
data = NULL;
data_length = 0;
break;
case TCODE_READ_BLOCK_RESPONSE:
case TCODE_LOCK_RESPONSE:
data = p->payload;
data_length = HEADER_GET_DATA_LENGTH(p->header[3]);
break;
default:
/* Should never happen, this is just to shut up gcc. */
data = NULL;
data_length = 0;
break;
}
/*
* The response handler may be executed while the request handler
* is still pending. Cancel the request handler.
@ -1135,7 +1140,7 @@ static void handle_topology_map(struct fw_card *card, struct fw_request *request
{
int start;
if (!TCODE_IS_READ_REQUEST(tcode)) {
if (!tcode_is_read_request(tcode)) {
fw_send_response(card, request, RCODE_TYPE_ERROR);
return;
}

View file

@ -225,13 +225,20 @@ static inline bool is_next_generation(int new_generation, int old_generation)
#define TCODE_LINK_INTERNAL 0xe
#define TCODE_IS_READ_REQUEST(tcode) (((tcode) & ~1) == 4)
#define TCODE_IS_BLOCK_PACKET(tcode) (((tcode) & 1) != 0)
#define TCODE_IS_LINK_INTERNAL(tcode) ((tcode) == TCODE_LINK_INTERNAL)
#define TCODE_IS_REQUEST(tcode) (((tcode) & 2) == 0)
#define TCODE_IS_RESPONSE(tcode) (((tcode) & 2) != 0)
#define TCODE_HAS_REQUEST_DATA(tcode) (((tcode) & 12) != 4)
#define TCODE_HAS_RESPONSE_DATA(tcode) (((tcode) & 12) != 0)
static inline bool tcode_is_read_request(unsigned int tcode)
{
return (tcode & ~1u) == 4u;
}
static inline bool tcode_is_block_packet(unsigned int tcode)
{
return (tcode & 1u) != 0u;
}
static inline bool tcode_is_link_internal(unsigned int tcode)
{
return (tcode == TCODE_LINK_INTERNAL);
}
#define LOCAL_BUS 0xffc0

View file

@ -40,6 +40,7 @@
#include "core.h"
#include "ohci.h"
#include "packet-header-definitions.h"
#define ohci_info(ohci, f, args...) dev_info(ohci->card.device, f, ##args)
#define ohci_notice(ohci, f, args...) dev_notice(ohci->card.device, f, ##args)
@ -393,7 +394,6 @@ MODULE_PARM_DESC(quirks, "Chip quirks (default = 0"
#define OHCI_PARAM_DEBUG_AT_AR 1
#define OHCI_PARAM_DEBUG_SELFIDS 2
#define OHCI_PARAM_DEBUG_IRQS 4
#define OHCI_PARAM_DEBUG_BUSRESETS 8 /* only effective before chip init */
static int param_debug;
module_param_named(debug, param_debug, int, 0644);
@ -401,7 +401,6 @@ MODULE_PARM_DESC(debug, "Verbose logging (default = 0"
", AT/AR events = " __stringify(OHCI_PARAM_DEBUG_AT_AR)
", self-IDs = " __stringify(OHCI_PARAM_DEBUG_SELFIDS)
", IRQs = " __stringify(OHCI_PARAM_DEBUG_IRQS)
", busReset events = " __stringify(OHCI_PARAM_DEBUG_BUSRESETS)
", or a combination, or all = -1)");
static bool param_remote_dma;
@ -410,12 +409,7 @@ MODULE_PARM_DESC(remote_dma, "Enable unfiltered remote DMA (default = N)");
static void log_irqs(struct fw_ohci *ohci, u32 evt)
{
if (likely(!(param_debug &
(OHCI_PARAM_DEBUG_IRQS | OHCI_PARAM_DEBUG_BUSRESETS))))
return;
if (!(param_debug & OHCI_PARAM_DEBUG_IRQS) &&
!(evt & OHCI1394_busReset))
if (likely(!(param_debug & OHCI_PARAM_DEBUG_IRQS)))
return;
ohci_notice(ohci, "IRQ %08x%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", evt,
@ -516,14 +510,14 @@ static const char *tcodes[] = {
static void log_ar_at_event(struct fw_ohci *ohci,
char dir, int speed, u32 *header, int evt)
{
int tcode = header[0] >> 4 & 0xf;
int tcode = async_header_get_tcode(header);
char specific[12];
if (likely(!(param_debug & OHCI_PARAM_DEBUG_AT_AR)))
return;
if (unlikely(evt >= ARRAY_SIZE(evts)))
evt = 0x1f;
evt = 0x1f;
if (evt == OHCI1394_evt_bus_reset) {
ohci_notice(ohci, "A%c evt_bus_reset, generation %d\n",
@ -532,20 +526,27 @@ static void log_ar_at_event(struct fw_ohci *ohci,
}
switch (tcode) {
case 0x0: case 0x6: case 0x8:
case TCODE_WRITE_QUADLET_REQUEST:
case TCODE_READ_QUADLET_RESPONSE:
case TCODE_CYCLE_START:
snprintf(specific, sizeof(specific), " = %08x",
be32_to_cpu((__force __be32)header[3]));
break;
case 0x1: case 0x5: case 0x7: case 0x9: case 0xb:
case TCODE_WRITE_BLOCK_REQUEST:
case TCODE_READ_BLOCK_REQUEST:
case TCODE_READ_BLOCK_RESPONSE:
case TCODE_LOCK_REQUEST:
case TCODE_LOCK_RESPONSE:
snprintf(specific, sizeof(specific), " %x,%x",
header[3] >> 16, header[3] & 0xffff);
async_header_get_data_length(header),
async_header_get_extended_tcode(header));
break;
default:
specific[0] = '\0';
}
switch (tcode) {
case 0xa:
case TCODE_STREAM_DATA:
ohci_notice(ohci, "A%c %s, %s\n",
dir, evts[evt], tcodes[tcode]);
break;
@ -553,19 +554,23 @@ static void log_ar_at_event(struct fw_ohci *ohci,
ohci_notice(ohci, "A%c %s, PHY %08x %08x\n",
dir, evts[evt], header[1], header[2]);
break;
case 0x0: case 0x1: case 0x4: case 0x5: case 0x9:
case TCODE_WRITE_QUADLET_REQUEST:
case TCODE_WRITE_BLOCK_REQUEST:
case TCODE_READ_QUADLET_REQUEST:
case TCODE_READ_BLOCK_REQUEST:
case TCODE_LOCK_REQUEST:
ohci_notice(ohci,
"A%c spd %x tl %02x, %04x -> %04x, %s, %s, %04x%08x%s\n",
dir, speed, header[0] >> 10 & 0x3f,
header[1] >> 16, header[0] >> 16, evts[evt],
tcodes[tcode], header[1] & 0xffff, header[2], specific);
"A%c spd %x tl %02x, %04x -> %04x, %s, %s, %012llx%s\n",
dir, speed, async_header_get_tlabel(header),
async_header_get_source(header), async_header_get_destination(header),
evts[evt], tcodes[tcode], async_header_get_offset(header), specific);
break;
default:
ohci_notice(ohci,
"A%c spd %x tl %02x, %04x -> %04x, %s, %s%s\n",
dir, speed, header[0] >> 10 & 0x3f,
header[1] >> 16, header[0] >> 16, evts[evt],
tcodes[tcode], specific);
dir, speed, async_header_get_tlabel(header),
async_header_get_source(header), async_header_get_destination(header),
evts[evt], tcodes[tcode], specific);
}
}
@ -853,7 +858,7 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
p.header[1] = cond_le32_to_cpu(buffer[1]);
p.header[2] = cond_le32_to_cpu(buffer[2]);
tcode = (p.header[0] >> 4) & 0x0f;
tcode = async_header_get_tcode(p.header);
switch (tcode) {
case TCODE_WRITE_QUADLET_REQUEST:
case TCODE_READ_QUADLET_RESPONSE:
@ -874,7 +879,7 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
case TCODE_LOCK_RESPONSE:
p.header[3] = cond_le32_to_cpu(buffer[3]);
p.header_length = 16;
p.payload_length = p.header[3] >> 16;
p.payload_length = async_header_get_data_length(p.header);
if (p.payload_length > MAX_ASYNC_PAYLOAD) {
ar_context_abort(ctx, "invalid packet length");
return NULL;
@ -911,8 +916,7 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
* Several controllers, notably from NEC and VIA, forget to
* write ack_complete status at PHY packet reception.
*/
if (evt == OHCI1394_evt_no_status &&
(p.header[0] & 0xff) == (OHCI1394_phy_tcode << 4))
if (evt == OHCI1394_evt_no_status && tcode == OHCI1394_phy_tcode)
p.ack = ACK_COMPLETE;
/*
@ -1353,7 +1357,7 @@ static int at_context_queue_packet(struct context *ctx,
* accordingly.
*/
tcode = (packet->header[0] >> 4) & 0x0f;
tcode = async_header_get_tcode(packet->header);
header = (__le32 *) &d[1];
switch (tcode) {
case TCODE_WRITE_QUADLET_REQUEST:
@ -1371,7 +1375,7 @@ static int at_context_queue_packet(struct context *ctx,
(packet->header[0] & 0xffff0000));
header[2] = cpu_to_le32(packet->header[2]);
if (TCODE_IS_BLOCK_PACKET(tcode))
if (tcode_is_block_packet(tcode))
header[3] = cpu_to_le32(packet->header[3]);
else
header[3] = (__force __le32) packet->header[3];
@ -1550,12 +1554,6 @@ static int handle_at_packet(struct context *context,
return 1;
}
#define HEADER_GET_DESTINATION(q) (((q) >> 16) & 0xffff)
#define HEADER_GET_TCODE(q) (((q) >> 4) & 0x0f)
#define HEADER_GET_OFFSET_HIGH(q) (((q) >> 0) & 0xffff)
#define HEADER_GET_DATA_LENGTH(q) (((q) >> 16) & 0xffff)
#define HEADER_GET_EXTENDED_TCODE(q) (((q) >> 0) & 0xffff)
static u32 get_cycle_time(struct fw_ohci *ohci);
static void handle_local_rom(struct fw_ohci *ohci,
@ -1564,9 +1562,9 @@ static void handle_local_rom(struct fw_ohci *ohci,
struct fw_packet response;
int tcode, length, i;
tcode = HEADER_GET_TCODE(packet->header[0]);
if (TCODE_IS_BLOCK_PACKET(tcode))
length = HEADER_GET_DATA_LENGTH(packet->header[3]);
tcode = async_header_get_tcode(packet->header);
if (tcode_is_block_packet(tcode))
length = async_header_get_data_length(packet->header);
else
length = 4;
@ -1574,7 +1572,7 @@ static void handle_local_rom(struct fw_ohci *ohci,
if (i + length > CONFIG_ROM_SIZE) {
fw_fill_response(&response, packet->header,
RCODE_ADDRESS_ERROR, NULL, 0);
} else if (!TCODE_IS_READ_REQUEST(tcode)) {
} else if (!tcode_is_read_request(tcode)) {
fw_fill_response(&response, packet->header,
RCODE_TYPE_ERROR, NULL, 0);
} else {
@ -1595,10 +1593,10 @@ static void handle_local_lock(struct fw_ohci *ohci,
__be32 *payload, lock_old;
u32 lock_arg, lock_data;
tcode = HEADER_GET_TCODE(packet->header[0]);
length = HEADER_GET_DATA_LENGTH(packet->header[3]);
tcode = async_header_get_tcode(packet->header);
length = async_header_get_data_length(packet->header);
payload = packet->payload;
ext_tcode = HEADER_GET_EXTENDED_TCODE(packet->header[3]);
ext_tcode = async_header_get_extended_tcode(packet->header);
if (tcode == TCODE_LOCK_REQUEST &&
ext_tcode == EXTCODE_COMPARE_SWAP && length == 8) {
@ -1646,10 +1644,7 @@ static void handle_local_request(struct context *ctx, struct fw_packet *packet)
packet->callback(packet, &ctx->ohci->card, packet->ack);
}
offset =
((unsigned long long)
HEADER_GET_OFFSET_HIGH(packet->header[1]) << 32) |
packet->header[2];
offset = async_header_get_offset(packet->header);
csr = offset - CSR_REGISTER_BASE;
/* Handle config rom reads. */
@ -1683,7 +1678,7 @@ static void at_context_transmit(struct context *ctx, struct fw_packet *packet)
spin_lock_irqsave(&ctx->ohci->lock, flags);
if (HEADER_GET_DESTINATION(packet->header[0]) == ctx->ohci->node_id &&
if (async_header_get_destination(packet->header) == ctx->ohci->node_id &&
ctx->ohci->generation == packet->generation) {
spin_unlock_irqrestore(&ctx->ohci->lock, flags);
@ -2064,8 +2059,7 @@ static void bus_reset_work(struct work_struct *work)
ohci->generation = generation;
reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
if (param_debug & OHCI_PARAM_DEBUG_BUSRESETS)
reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset);
reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset);
if (ohci->quirks & QUIRK_RESET_PACKET)
ohci->request_generation = generation;
@ -2137,6 +2131,7 @@ static irqreturn_t irq_handler(int irq, void *data)
reg_write(ohci, OHCI1394_IntEventClear,
event & ~(OHCI1394_busReset | OHCI1394_postedWriteErr));
log_irqs(ohci, event);
// The flag is masked again at bus_reset_work() scheduled by selfID event.
if (event & OHCI1394_busReset)
reg_write(ohci, OHCI1394_IntMaskClear, OHCI1394_busReset);
@ -2476,9 +2471,8 @@ static int ohci_enable(struct fw_card *card,
OHCI1394_cycleInconsistent |
OHCI1394_unrecoverableError |
OHCI1394_cycleTooLong |
OHCI1394_masterIntEnable;
if (param_debug & OHCI_PARAM_DEBUG_BUSRESETS)
irqs |= OHCI1394_busReset;
OHCI1394_masterIntEnable |
OHCI1394_busReset;
reg_write(ohci, OHCI1394_IntMaskSet, irqs);
reg_write(ohci, OHCI1394_HCControlSet,
@ -3631,7 +3625,7 @@ static int pci_probe(struct pci_dev *dev,
struct fw_ohci *ohci;
u32 bus_options, max_receive, link_speed, version;
u64 guid;
int i, err;
int i, flags, irq, err;
size_t size;
if (dev->vendor == PCI_VENDOR_ID_PINNACLE_SYSTEMS) {
@ -3756,18 +3750,29 @@ static int pci_probe(struct pci_dev *dev,
guid = ((u64) reg_read(ohci, OHCI1394_GUIDHi) << 32) |
reg_read(ohci, OHCI1394_GUIDLo);
flags = PCI_IRQ_INTX;
if (!(ohci->quirks & QUIRK_NO_MSI))
pci_enable_msi(dev);
err = devm_request_irq(&dev->dev, dev->irq, irq_handler,
pci_dev_msi_enabled(dev) ? 0 : IRQF_SHARED, ohci_driver_name, ohci);
flags |= PCI_IRQ_MSI;
err = pci_alloc_irq_vectors(dev, 1, 1, flags);
if (err < 0)
return err;
irq = pci_irq_vector(dev, 0);
if (irq < 0) {
err = irq;
goto fail_msi;
}
err = request_threaded_irq(irq, irq_handler, NULL,
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);
ohci_err(ohci, "failed to allocate interrupt %d\n", irq);
goto fail_msi;
}
err = fw_card_add(&ohci->card, max_receive, link_speed, guid);
if (err)
goto fail_msi;
goto fail_irq;
version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff;
ohci_notice(ohci,
@ -3780,9 +3785,10 @@ static int pci_probe(struct pci_dev *dev,
return 0;
fail_irq:
free_irq(irq, ohci);
fail_msi:
devm_free_irq(&dev->dev, dev->irq, ohci);
pci_disable_msi(dev);
pci_free_irq_vectors(dev);
return err;
}
@ -3790,6 +3796,7 @@ static int pci_probe(struct pci_dev *dev,
static void pci_remove(struct pci_dev *dev)
{
struct fw_ohci *ohci = pci_get_drvdata(dev);
int irq;
/*
* If the removal is happening from the suspend state, LPS won't be
@ -3809,8 +3816,10 @@ static void pci_remove(struct pci_dev *dev)
software_reset(ohci);
devm_free_irq(&dev->dev, dev->irq, ohci);
pci_disable_msi(dev);
irq = pci_irq_vector(dev, 0);
if (irq >= 0)
free_irq(irq, ohci);
pci_free_irq_vectors(dev);
dev_notice(&dev->dev, "removing fw-ohci device\n");
}

View file

@ -0,0 +1,234 @@
// SPDX-License-Identifier: GPL-2.0-or-later
//
// packet-header-definitions.h - The definitions of header fields for IEEE 1394 packet.
//
// Copyright (c) 2024 Takashi Sakamoto
#ifndef _FIREWIRE_PACKET_HEADER_DEFINITIONS_H
#define _FIREWIRE_PACKET_HEADER_DEFINITIONS_H
#define ASYNC_HEADER_QUADLET_COUNT 4
#define ASYNC_HEADER_Q0_DESTINATION_SHIFT 16
#define ASYNC_HEADER_Q0_DESTINATION_MASK 0xffff0000
#define ASYNC_HEADER_Q0_TLABEL_SHIFT 10
#define ASYNC_HEADER_Q0_TLABEL_MASK 0x0000fc00
#define ASYNC_HEADER_Q0_RETRY_SHIFT 8
#define ASYNC_HEADER_Q0_RETRY_MASK 0x00000300
#define ASYNC_HEADER_Q0_TCODE_SHIFT 4
#define ASYNC_HEADER_Q0_TCODE_MASK 0x000000f0
#define ASYNC_HEADER_Q0_PRIORITY_SHIFT 0
#define ASYNC_HEADER_Q0_PRIORITY_MASK 0x0000000f
#define ASYNC_HEADER_Q1_SOURCE_SHIFT 16
#define ASYNC_HEADER_Q1_SOURCE_MASK 0xffff0000
#define ASYNC_HEADER_Q1_RCODE_SHIFT 12
#define ASYNC_HEADER_Q1_RCODE_MASK 0x0000f000
#define ASYNC_HEADER_Q1_RCODE_SHIFT 12
#define ASYNC_HEADER_Q1_RCODE_MASK 0x0000f000
#define ASYNC_HEADER_Q1_OFFSET_HIGH_SHIFT 0
#define ASYNC_HEADER_Q1_OFFSET_HIGH_MASK 0x0000ffff
#define ASYNC_HEADER_Q3_DATA_LENGTH_SHIFT 16
#define ASYNC_HEADER_Q3_DATA_LENGTH_MASK 0xffff0000
#define ASYNC_HEADER_Q3_EXTENDED_TCODE_SHIFT 0
#define ASYNC_HEADER_Q3_EXTENDED_TCODE_MASK 0x0000ffff
static inline unsigned int async_header_get_destination(const u32 header[ASYNC_HEADER_QUADLET_COUNT])
{
return (header[0] & ASYNC_HEADER_Q0_DESTINATION_MASK) >> ASYNC_HEADER_Q0_DESTINATION_SHIFT;
}
static inline unsigned int async_header_get_tlabel(const u32 header[ASYNC_HEADER_QUADLET_COUNT])
{
return (header[0] & ASYNC_HEADER_Q0_TLABEL_MASK) >> ASYNC_HEADER_Q0_TLABEL_SHIFT;
}
static inline unsigned int async_header_get_retry(const u32 header[ASYNC_HEADER_QUADLET_COUNT])
{
return (header[0] & ASYNC_HEADER_Q0_RETRY_MASK) >> ASYNC_HEADER_Q0_RETRY_SHIFT;
}
static inline unsigned int async_header_get_tcode(const u32 header[ASYNC_HEADER_QUADLET_COUNT])
{
return (header[0] & ASYNC_HEADER_Q0_TCODE_MASK) >> ASYNC_HEADER_Q0_TCODE_SHIFT;
}
static inline unsigned int async_header_get_priority(const u32 header[ASYNC_HEADER_QUADLET_COUNT])
{
return (header[0] & ASYNC_HEADER_Q0_PRIORITY_MASK) >> ASYNC_HEADER_Q0_PRIORITY_SHIFT;
}
static inline unsigned int async_header_get_source(const u32 header[ASYNC_HEADER_QUADLET_COUNT])
{
return (header[1] & ASYNC_HEADER_Q1_SOURCE_MASK) >> ASYNC_HEADER_Q1_SOURCE_SHIFT;
}
static inline unsigned int async_header_get_rcode(const u32 header[ASYNC_HEADER_QUADLET_COUNT])
{
return (header[1] & ASYNC_HEADER_Q1_RCODE_MASK) >> ASYNC_HEADER_Q1_RCODE_SHIFT;
}
static inline u64 async_header_get_offset(const u32 header[ASYNC_HEADER_QUADLET_COUNT])
{
u32 hi = (header[1] & ASYNC_HEADER_Q1_OFFSET_HIGH_MASK) >> ASYNC_HEADER_Q1_OFFSET_HIGH_SHIFT;
return (((u64)hi) << 32) | ((u64)header[2]);
}
static inline u32 async_header_get_quadlet_data(const u32 header[ASYNC_HEADER_QUADLET_COUNT])
{
return header[3];
}
static inline unsigned int async_header_get_data_length(const u32 header[ASYNC_HEADER_QUADLET_COUNT])
{
return (header[3] & ASYNC_HEADER_Q3_DATA_LENGTH_MASK) >> ASYNC_HEADER_Q3_DATA_LENGTH_SHIFT;
}
static inline unsigned int async_header_get_extended_tcode(const u32 header[ASYNC_HEADER_QUADLET_COUNT])
{
return (header[3] & ASYNC_HEADER_Q3_EXTENDED_TCODE_MASK) >> ASYNC_HEADER_Q3_EXTENDED_TCODE_SHIFT;
}
static inline void async_header_set_destination(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int destination)
{
header[0] &= ~ASYNC_HEADER_Q0_DESTINATION_MASK;
header[0] |= (((u32)destination) << ASYNC_HEADER_Q0_DESTINATION_SHIFT) & ASYNC_HEADER_Q0_DESTINATION_MASK;
}
static inline void async_header_set_tlabel(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int tlabel)
{
header[0] &= ~ASYNC_HEADER_Q0_TLABEL_MASK;
header[0] |= (((u32)tlabel) << ASYNC_HEADER_Q0_TLABEL_SHIFT) & ASYNC_HEADER_Q0_TLABEL_MASK;
}
static inline void async_header_set_retry(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int retry)
{
header[0] &= ~ASYNC_HEADER_Q0_RETRY_MASK;
header[0] |= (((u32)retry) << ASYNC_HEADER_Q0_RETRY_SHIFT) & ASYNC_HEADER_Q0_RETRY_MASK;
}
static inline void async_header_set_tcode(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int tcode)
{
header[0] &= ~ASYNC_HEADER_Q0_TCODE_MASK;
header[0] |= (((u32)tcode) << ASYNC_HEADER_Q0_TCODE_SHIFT) & ASYNC_HEADER_Q0_TCODE_MASK;
}
static inline void async_header_set_priority(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int priority)
{
header[0] &= ~ASYNC_HEADER_Q0_PRIORITY_MASK;
header[0] |= (((u32)priority) << ASYNC_HEADER_Q0_PRIORITY_SHIFT) & ASYNC_HEADER_Q0_PRIORITY_MASK;
}
static inline void async_header_set_source(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int source)
{
header[1] &= ~ASYNC_HEADER_Q1_SOURCE_MASK;
header[1] |= (((u32)source) << ASYNC_HEADER_Q1_SOURCE_SHIFT) & ASYNC_HEADER_Q1_SOURCE_MASK;
}
static inline void async_header_set_rcode(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int rcode)
{
header[1] &= ~ASYNC_HEADER_Q1_RCODE_MASK;
header[1] |= (((u32)rcode) << ASYNC_HEADER_Q1_RCODE_SHIFT) & ASYNC_HEADER_Q1_RCODE_MASK;
}
static inline void async_header_set_offset(u32 header[ASYNC_HEADER_QUADLET_COUNT], u64 offset)
{
u32 hi = (u32)(offset >> 32);
header[1] &= ~ASYNC_HEADER_Q1_OFFSET_HIGH_MASK;
header[1] |= (hi << ASYNC_HEADER_Q1_OFFSET_HIGH_SHIFT) & ASYNC_HEADER_Q1_OFFSET_HIGH_MASK;
header[2] = (u32)(offset & 0x00000000ffffffff);
}
static inline void async_header_set_quadlet_data(u32 header[ASYNC_HEADER_QUADLET_COUNT], u32 quadlet_data)
{
header[3] = quadlet_data;
}
static inline void async_header_set_data_length(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int data_length)
{
header[3] &= ~ASYNC_HEADER_Q3_DATA_LENGTH_MASK;
header[3] |= (((u32)data_length) << ASYNC_HEADER_Q3_DATA_LENGTH_SHIFT) & ASYNC_HEADER_Q3_DATA_LENGTH_MASK;
}
static inline void async_header_set_extended_tcode(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int extended_tcode)
{
header[3] &= ~ASYNC_HEADER_Q3_EXTENDED_TCODE_MASK;
header[3] |= (((u32)extended_tcode) << ASYNC_HEADER_Q3_EXTENDED_TCODE_SHIFT) & ASYNC_HEADER_Q3_EXTENDED_TCODE_MASK;
}
#define ISOC_HEADER_DATA_LENGTH_SHIFT 16
#define ISOC_HEADER_DATA_LENGTH_MASK 0xffff0000
#define ISOC_HEADER_TAG_SHIFT 14
#define ISOC_HEADER_TAG_MASK 0x0000c000
#define ISOC_HEADER_CHANNEL_SHIFT 8
#define ISOC_HEADER_CHANNEL_MASK 0x00003f00
#define ISOC_HEADER_TCODE_SHIFT 4
#define ISOC_HEADER_TCODE_MASK 0x000000f0
#define ISOC_HEADER_SY_SHIFT 0
#define ISOC_HEADER_SY_MASK 0x0000000f
static inline unsigned int isoc_header_get_data_length(u32 header)
{
return (header & ISOC_HEADER_DATA_LENGTH_MASK) >> ISOC_HEADER_DATA_LENGTH_SHIFT;
}
static inline unsigned int isoc_header_get_tag(u32 header)
{
return (header & ISOC_HEADER_TAG_MASK) >> ISOC_HEADER_TAG_SHIFT;
}
static inline unsigned int isoc_header_get_channel(u32 header)
{
return (header & ISOC_HEADER_CHANNEL_MASK) >> ISOC_HEADER_CHANNEL_SHIFT;
}
static inline unsigned int isoc_header_get_tcode(u32 header)
{
return (header & ISOC_HEADER_TCODE_MASK) >> ISOC_HEADER_TCODE_SHIFT;
}
static inline unsigned int isoc_header_get_sy(u32 header)
{
return (header & ISOC_HEADER_SY_MASK) >> ISOC_HEADER_SY_SHIFT;
}
static inline void isoc_header_set_data_length(u32 *header, unsigned int data_length)
{
*header &= ~ISOC_HEADER_DATA_LENGTH_MASK;
*header |= (((u32)data_length) << ISOC_HEADER_DATA_LENGTH_SHIFT) & ISOC_HEADER_DATA_LENGTH_MASK;
}
static inline void isoc_header_set_tag(u32 *header, unsigned int tag)
{
*header &= ~ISOC_HEADER_TAG_MASK;
*header |= (((u32)tag) << ISOC_HEADER_TAG_SHIFT) & ISOC_HEADER_TAG_MASK;
}
static inline void isoc_header_set_channel(u32 *header, unsigned int channel)
{
*header &= ~ISOC_HEADER_CHANNEL_MASK;
*header |= (((u32)channel) << ISOC_HEADER_CHANNEL_SHIFT) & ISOC_HEADER_CHANNEL_MASK;
}
static inline void isoc_header_set_tcode(u32 *header, unsigned int tcode)
{
*header &= ~ISOC_HEADER_TCODE_MASK;
*header |= (((u32)tcode) << ISOC_HEADER_TCODE_SHIFT) & ISOC_HEADER_TCODE_MASK;
}
static inline void isoc_header_set_sy(u32 *header, unsigned int sy)
{
*header &= ~ISOC_HEADER_SY_MASK;
*header |= (((u32)sy) << ISOC_HEADER_SY_SHIFT) & ISOC_HEADER_SY_MASK;
}
#endif // _FIREWIRE_PACKET_HEADER_DEFINITIONS_H

View file

@ -0,0 +1,582 @@
// SPDX-License-Identifier: GPL-2.0-or-later
//
// packet-serdes-test.c - An application of Kunit to check serialization/deserialization of packets
// defined by IEEE 1394.
//
// Copyright (c) 2024 Takashi Sakamoto
#include <kunit/test.h>
#include <linux/firewire-constants.h>
#include "packet-header-definitions.h"
static void serialize_async_header_common(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int dst_id, unsigned int tlabel,
unsigned int retry, unsigned int tcode,
unsigned int priority, unsigned int src_id)
{
async_header_set_destination(header, dst_id);
async_header_set_tlabel(header, tlabel);
async_header_set_retry(header, retry);
async_header_set_tcode(header, tcode);
async_header_set_priority(header, priority);
async_header_set_source(header, src_id);
}
static void serialize_async_header_request(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int dst_id, unsigned int tlabel,
unsigned int retry, unsigned int tcode,
unsigned int priority, unsigned int src_id, u64 offset)
{
serialize_async_header_common(header, dst_id, tlabel, retry, tcode, priority, src_id);
async_header_set_offset(header, offset);
}
static void serialize_async_header_quadlet_request(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int dst_id, unsigned int tlabel,
unsigned int retry, unsigned int tcode,
unsigned int priority, unsigned int src_id,
u64 offset)
{
serialize_async_header_request(header, dst_id, tlabel, retry, tcode, priority, src_id,
offset);
}
static void serialize_async_header_block_request(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int dst_id, unsigned int tlabel,
unsigned int retry, unsigned int tcode,
unsigned int priority, unsigned int src_id,
u64 offset, unsigned int data_length,
unsigned int extended_tcode)
{
serialize_async_header_request(header, dst_id, tlabel, retry, tcode, priority, src_id,
offset);
async_header_set_data_length(header, data_length);
async_header_set_extended_tcode(header, extended_tcode);
}
static void serialize_async_header_response(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int dst_id, unsigned int tlabel,
unsigned int retry, unsigned int tcode,
unsigned int priority, unsigned int src_id,
unsigned int rcode)
{
serialize_async_header_common(header, dst_id, tlabel, retry, tcode, priority, src_id);
async_header_set_rcode(header, rcode);
}
static void serialize_async_header_quadlet_response(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int dst_id, unsigned int tlabel,
unsigned int retry, unsigned int tcode,
unsigned int priority, unsigned int src_id,
unsigned int rcode)
{
serialize_async_header_response(header, dst_id, tlabel, retry, tcode, priority, src_id,
rcode);
}
static void serialize_async_header_block_response(u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int dst_id, unsigned int tlabel,
unsigned int retry, unsigned int tcode,
unsigned int priority, unsigned int src_id,
unsigned int rcode, unsigned int data_length,
unsigned int extended_tcode)
{
serialize_async_header_response(header, dst_id, tlabel, retry, tcode, priority, src_id,
rcode);
async_header_set_data_length(header, data_length);
async_header_set_extended_tcode(header, extended_tcode);
}
static void deserialize_async_header_common(const u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int *dst_id, unsigned int *tlabel,
unsigned int *retry, unsigned int *tcode,
unsigned int *priority, unsigned int *src_id)
{
*dst_id = async_header_get_destination(header);
*tlabel = async_header_get_tlabel(header);
*retry = async_header_get_retry(header);
*tcode = async_header_get_tcode(header);
*priority = async_header_get_priority(header);
*src_id = async_header_get_source(header);
}
static void deserialize_async_header_request(const u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int *dst_id, unsigned int *tlabel,
unsigned int *retry, unsigned int *tcode,
unsigned int *priority, unsigned int *src_id,
u64 *offset)
{
deserialize_async_header_common(header, dst_id, tlabel, retry, tcode, priority, src_id);
*offset = async_header_get_offset(header);
}
static void deserialize_async_header_quadlet_request(const u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int *dst_id, unsigned int *tlabel,
unsigned int *retry, unsigned int *tcode,
unsigned int *priority, unsigned int *src_id,
u64 *offset)
{
deserialize_async_header_request(header, dst_id, tlabel, retry, tcode, priority, src_id,
offset);
}
static void deserialize_async_header_block_request(const u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int *dst_id, unsigned int *tlabel,
unsigned int *retry, unsigned int *tcode,
unsigned int *priority, unsigned int *src_id,
u64 *offset,
unsigned int *data_length,
unsigned int *extended_tcode)
{
deserialize_async_header_request(header, dst_id, tlabel, retry, tcode, priority, src_id,
offset);
*data_length = async_header_get_data_length(header);
*extended_tcode = async_header_get_extended_tcode(header);
}
static void deserialize_async_header_response(const u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int *dst_id, unsigned int *tlabel,
unsigned int *retry, unsigned int *tcode,
unsigned int *priority, unsigned int *src_id,
unsigned int *rcode)
{
deserialize_async_header_common(header, dst_id, tlabel, retry, tcode, priority, src_id);
*rcode = async_header_get_rcode(header);
}
static void deserialize_async_header_quadlet_response(const u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int *dst_id, unsigned int *tlabel,
unsigned int *retry, unsigned int *tcode,
unsigned int *priority, unsigned int *src_id,
unsigned int *rcode)
{
deserialize_async_header_response(header, dst_id, tlabel, retry, tcode, priority, src_id, rcode);
}
static void deserialize_async_header_block_response(const u32 header[ASYNC_HEADER_QUADLET_COUNT],
unsigned int *dst_id, unsigned int *tlabel,
unsigned int *retry, unsigned int *tcode,
unsigned int *priority, unsigned int *src_id,
unsigned int *rcode, unsigned int *data_length,
unsigned int *extended_tcode)
{
deserialize_async_header_response(header, dst_id, tlabel, retry, tcode, priority, src_id, rcode);
*data_length = async_header_get_data_length(header);
*extended_tcode = async_header_get_extended_tcode(header);
}
static void serialize_isoc_header(u32 *header, unsigned int data_length, unsigned int tag,
unsigned int channel, unsigned int tcode, unsigned int sy)
{
isoc_header_set_data_length(header, data_length);
isoc_header_set_tag(header, tag);
isoc_header_set_channel(header, channel);
isoc_header_set_tcode(header, tcode);
isoc_header_set_sy(header, sy);
}
static void deserialize_isoc_header(u32 header, unsigned int *data_length, unsigned int *tag,
unsigned int *channel, unsigned int *tcode, unsigned int *sy)
{
*data_length = isoc_header_get_data_length(header);
*tag = isoc_header_get_tag(header);
*channel = isoc_header_get_channel(header);
*tcode = isoc_header_get_tcode(header);
*sy = isoc_header_get_sy(header);
}
static void test_async_header_write_quadlet_request(struct kunit *test)
{
static const u32 expected[ASYNC_HEADER_QUADLET_COUNT] = {
0xffc05100,
0xffc1ffff,
0xf0000234,
0x1f0000c0,
};
u32 header[ASYNC_HEADER_QUADLET_COUNT] = {0, 0, 0, 0};
unsigned int dst_id;
unsigned int tlabel;
unsigned int retry;
unsigned int tcode;
unsigned int priority;
unsigned int src_id;
u64 offset;
u32 quadlet_data;
deserialize_async_header_quadlet_request(expected, &dst_id, &tlabel, &retry, &tcode,
&priority, &src_id, &offset);
quadlet_data = async_header_get_quadlet_data(expected);
KUNIT_EXPECT_EQ(test, 0xffc0, dst_id);
KUNIT_EXPECT_EQ(test, 0x14, tlabel);
KUNIT_EXPECT_EQ(test, 0x01, retry);
KUNIT_EXPECT_EQ(test, TCODE_WRITE_QUADLET_REQUEST, tcode);
KUNIT_EXPECT_EQ(test, 0x00, priority);
KUNIT_EXPECT_EQ(test, 0xffc1, src_id);
KUNIT_EXPECT_EQ(test, 0xfffff0000234, offset);
KUNIT_EXPECT_EQ(test, 0x1f0000c0, quadlet_data);
serialize_async_header_quadlet_request(header, dst_id, tlabel, retry, tcode, priority,
src_id, offset);
async_header_set_quadlet_data(header, quadlet_data);
KUNIT_EXPECT_MEMEQ(test, header, expected, sizeof(expected));
}
static void test_async_header_write_block_request(struct kunit *test)
{
static const u32 expected[ASYNC_HEADER_QUADLET_COUNT] = {
0xffc06510,
0xffc1ecc0,
0x00000000,
0x00180000,
};
u32 header[ASYNC_HEADER_QUADLET_COUNT] = {0, 0, 0, 0};
unsigned int dst_id;
unsigned int tlabel;
unsigned int retry;
unsigned int tcode;
unsigned int priority;
unsigned int src_id;
u64 offset;
unsigned int data_length;
unsigned int extended_tcode;
deserialize_async_header_block_request(expected, &dst_id, &tlabel, &retry, &tcode,
&priority, &src_id, &offset, &data_length,
&extended_tcode);
KUNIT_EXPECT_EQ(test, 0xffc0, dst_id);
KUNIT_EXPECT_EQ(test, 0x19, tlabel);
KUNIT_EXPECT_EQ(test, 0x01, retry);
KUNIT_EXPECT_EQ(test, TCODE_WRITE_BLOCK_REQUEST, tcode);
KUNIT_EXPECT_EQ(test, 0x00, priority);
KUNIT_EXPECT_EQ(test, 0xffc1, src_id);
KUNIT_EXPECT_EQ(test, 0xecc000000000, offset);
KUNIT_EXPECT_EQ(test, 0x0018, data_length);
KUNIT_EXPECT_EQ(test, 0x0000, extended_tcode);
serialize_async_header_block_request(header, dst_id, tlabel, retry, tcode, priority, src_id,
offset, data_length, extended_tcode);
KUNIT_EXPECT_MEMEQ(test, header, expected, sizeof(expected));
}
static void test_async_header_write_response(struct kunit *test)
{
static const u32 expected[ASYNC_HEADER_QUADLET_COUNT] = {
0xffc15120,
0xffc00000,
0x00000000,
0x00000000,
};
u32 header[ASYNC_HEADER_QUADLET_COUNT] = {0, 0, 0, 0};
unsigned int dst_id;
unsigned int tlabel;
unsigned int retry;
unsigned int tcode;
unsigned int priority;
unsigned int src_id;
unsigned int rcode;
deserialize_async_header_quadlet_response(expected, &dst_id, &tlabel, &retry, &tcode,
&priority, &src_id, &rcode);
KUNIT_EXPECT_EQ(test, 0xffc1, dst_id);
KUNIT_EXPECT_EQ(test, 0x14, tlabel);
KUNIT_EXPECT_EQ(test, 0x01, retry);
KUNIT_EXPECT_EQ(test, TCODE_WRITE_RESPONSE, tcode);
KUNIT_EXPECT_EQ(test, 0x00, priority);
KUNIT_EXPECT_EQ(test, 0xffc0, src_id);
KUNIT_EXPECT_EQ(test, RCODE_COMPLETE, rcode);
serialize_async_header_quadlet_response(header, dst_id, tlabel, retry, tcode, priority,
src_id, rcode);
KUNIT_EXPECT_MEMEQ(test, header, expected, sizeof(expected) - sizeof(expected[0]));
}
static void test_async_header_read_quadlet_request(struct kunit *test)
{
static const u32 expected[ASYNC_HEADER_QUADLET_COUNT] = {
0xffc0f140,
0xffc1ffff,
0xf0000984,
0x00000000,
};
u32 header[ASYNC_HEADER_QUADLET_COUNT] = {0, 0, 0, 0};
unsigned int dst_id;
unsigned int tlabel;
unsigned int retry;
unsigned int tcode;
unsigned int priority;
unsigned int src_id;
u64 offset;
deserialize_async_header_quadlet_request(expected, &dst_id, &tlabel, &retry, &tcode,
&priority, &src_id, &offset);
KUNIT_EXPECT_EQ(test, 0xffc0, dst_id);
KUNIT_EXPECT_EQ(test, 0x3c, tlabel);
KUNIT_EXPECT_EQ(test, 0x01, retry);
KUNIT_EXPECT_EQ(test, TCODE_READ_QUADLET_REQUEST, tcode);
KUNIT_EXPECT_EQ(test, 0x00, priority);
KUNIT_EXPECT_EQ(test, 0xffc1, src_id);
KUNIT_EXPECT_EQ(test, 0xfffff0000984, offset);
serialize_async_header_quadlet_request(header, dst_id, tlabel, retry, tcode, priority,
src_id, offset);
KUNIT_EXPECT_MEMEQ(test, header, expected, sizeof(expected));
}
static void test_async_header_read_quadlet_response(struct kunit *test)
{
static const u32 expected[ASYNC_HEADER_QUADLET_COUNT] = {
0xffc1f160,
0xffc00000,
0x00000000,
0x00000180,
};
u32 header[ASYNC_HEADER_QUADLET_COUNT] = {0, 0, 0, 0};
unsigned int dst_id;
unsigned int tlabel;
unsigned int retry;
unsigned int tcode;
unsigned int priority;
unsigned int src_id;
unsigned int rcode;
u32 quadlet_data;
deserialize_async_header_quadlet_response(expected, &dst_id, &tlabel, &retry, &tcode,
&priority, &src_id, &rcode);
quadlet_data = async_header_get_quadlet_data(expected);
KUNIT_EXPECT_EQ(test, 0xffc1, dst_id);
KUNIT_EXPECT_EQ(test, 0x3c, tlabel);
KUNIT_EXPECT_EQ(test, 0x01, retry);
KUNIT_EXPECT_EQ(test, TCODE_READ_QUADLET_RESPONSE, tcode);
KUNIT_EXPECT_EQ(test, 0x00, priority);
KUNIT_EXPECT_EQ(test, 0xffc0, src_id);
KUNIT_EXPECT_EQ(test, RCODE_COMPLETE, rcode);
KUNIT_EXPECT_EQ(test, 0x00000180, quadlet_data);
serialize_async_header_quadlet_response(header, dst_id, tlabel, retry, tcode, priority,
src_id, rcode);
async_header_set_quadlet_data(header, quadlet_data);
KUNIT_EXPECT_MEMEQ(test, header, expected, sizeof(expected));
}
static void test_async_header_read_block_request(struct kunit *test)
{
static const u32 expected[ASYNC_HEADER_QUADLET_COUNT] = {
0xffc0e150,
0xffc1ffff,
0xf0000400,
0x00200000,
};
u32 header[ASYNC_HEADER_QUADLET_COUNT] = {0, 0, 0, 0};
unsigned int dst_id;
unsigned int tlabel;
unsigned int retry;
unsigned int tcode;
unsigned int priority;
unsigned int src_id;
u64 offset;
unsigned int data_length;
unsigned int extended_tcode;
deserialize_async_header_block_request(expected, &dst_id, &tlabel, &retry, &tcode,
&priority, &src_id, &offset, &data_length,
&extended_tcode);
KUNIT_EXPECT_EQ(test, 0xffc0, dst_id);
KUNIT_EXPECT_EQ(test, 0x38, tlabel);
KUNIT_EXPECT_EQ(test, 0x01, retry);
KUNIT_EXPECT_EQ(test, TCODE_READ_BLOCK_REQUEST, tcode);
KUNIT_EXPECT_EQ(test, 0x00, priority);
KUNIT_EXPECT_EQ(test, 0xffc1, src_id);
KUNIT_EXPECT_EQ(test, 0xfffff0000400, offset);
KUNIT_EXPECT_EQ(test, 0x0020, data_length);
KUNIT_EXPECT_EQ(test, 0x0000, extended_tcode);
serialize_async_header_block_request(header, dst_id, tlabel, retry, tcode, priority, src_id,
offset, data_length, extended_tcode);
KUNIT_EXPECT_MEMEQ(test, header, expected, sizeof(expected));
}
static void test_async_header_read_block_response(struct kunit *test)
{
static const u32 expected[ASYNC_HEADER_QUADLET_COUNT] = {
0xffc1e170,
0xffc00000,
0x00000000,
0x00200000,
};
u32 header[ASYNC_HEADER_QUADLET_COUNT] = {0, 0, 0, 0};
unsigned int dst_id;
unsigned int tlabel;
unsigned int retry;
unsigned int tcode;
unsigned int priority;
unsigned int src_id;
unsigned int rcode;
unsigned int data_length;
unsigned int extended_tcode;
deserialize_async_header_block_response(expected, &dst_id, &tlabel, &retry, &tcode,
&priority, &src_id, &rcode, &data_length,
&extended_tcode);
KUNIT_EXPECT_EQ(test, 0xffc1, dst_id);
KUNIT_EXPECT_EQ(test, 0x38, tlabel);
KUNIT_EXPECT_EQ(test, 0x01, retry);
KUNIT_EXPECT_EQ(test, TCODE_READ_BLOCK_RESPONSE, tcode);
KUNIT_EXPECT_EQ(test, 0x00, priority);
KUNIT_EXPECT_EQ(test, 0xffc0, src_id);
KUNIT_EXPECT_EQ(test, RCODE_COMPLETE, rcode);
KUNIT_EXPECT_EQ(test, 0x0020, data_length);
KUNIT_EXPECT_EQ(test, 0x0000, extended_tcode);
serialize_async_header_block_response(header, dst_id, tlabel, retry, tcode, priority,
src_id, rcode, data_length, extended_tcode);
KUNIT_EXPECT_MEMEQ(test, header, expected, sizeof(expected));
}
static void test_async_header_lock_request(struct kunit *test)
{
static const u32 expected[ASYNC_HEADER_QUADLET_COUNT] = {
0xffc02d90,
0xffc1ffff,
0xf0000984,
0x00080002,
};
u32 header[ASYNC_HEADER_QUADLET_COUNT] = {0, 0, 0, 0};
unsigned int dst_id;
unsigned int tlabel;
unsigned int retry;
unsigned int tcode;
unsigned int priority;
unsigned int src_id;
u64 offset;
unsigned int data_length;
unsigned int extended_tcode;
deserialize_async_header_block_request(expected, &dst_id, &tlabel, &retry, &tcode,
&priority, &src_id, &offset, &data_length,
&extended_tcode);
KUNIT_EXPECT_EQ(test, 0xffc0, dst_id);
KUNIT_EXPECT_EQ(test, 0x0b, tlabel);
KUNIT_EXPECT_EQ(test, 0x01, retry);
KUNIT_EXPECT_EQ(test, TCODE_LOCK_REQUEST, tcode);
KUNIT_EXPECT_EQ(test, 0x00, priority);
KUNIT_EXPECT_EQ(test, 0xffc1, src_id);
KUNIT_EXPECT_EQ(test, 0xfffff0000984, offset);
KUNIT_EXPECT_EQ(test, 0x0008, data_length);
KUNIT_EXPECT_EQ(test, EXTCODE_COMPARE_SWAP, extended_tcode);
serialize_async_header_block_request(header, dst_id, tlabel, retry, tcode, priority, src_id,
offset, data_length, extended_tcode);
KUNIT_EXPECT_MEMEQ(test, header, expected, sizeof(expected));
}
static void test_async_header_lock_response(struct kunit *test)
{
static const u32 expected[ASYNC_HEADER_QUADLET_COUNT] = {
0xffc12db0,
0xffc00000,
0x00000000,
0x00040002,
};
u32 header[ASYNC_HEADER_QUADLET_COUNT] = {0, 0, 0, 0};
unsigned int dst_id;
unsigned int tlabel;
unsigned int retry;
unsigned int tcode;
unsigned int priority;
unsigned int src_id;
unsigned int rcode;
unsigned int data_length;
unsigned int extended_tcode;
deserialize_async_header_block_response(expected, &dst_id, &tlabel, &retry, &tcode,
&priority, &src_id, &rcode, &data_length,
&extended_tcode);
KUNIT_EXPECT_EQ(test, 0xffc1, dst_id);
KUNIT_EXPECT_EQ(test, 0x0b, tlabel);
KUNIT_EXPECT_EQ(test, 0x01, retry);
KUNIT_EXPECT_EQ(test, TCODE_LOCK_RESPONSE, tcode);
KUNIT_EXPECT_EQ(test, 0x00, priority);
KUNIT_EXPECT_EQ(test, 0xffc0, src_id);
KUNIT_EXPECT_EQ(test, RCODE_COMPLETE, rcode);
KUNIT_EXPECT_EQ(test, 0x0004, data_length);
KUNIT_EXPECT_EQ(test, EXTCODE_COMPARE_SWAP, extended_tcode);
serialize_async_header_block_response(header, dst_id, tlabel, retry, tcode, priority,
src_id, rcode, data_length, extended_tcode);
KUNIT_EXPECT_MEMEQ(test, header, expected, sizeof(expected));
}
static void test_isoc_header(struct kunit *test)
{
const u32 expected = 0x00d08dec;
u32 header = 0;
unsigned int data_length;
unsigned int tag;
unsigned int channel;
unsigned int tcode;
unsigned int sy;
deserialize_isoc_header(expected, &data_length, &tag, &channel, &tcode, &sy);
KUNIT_EXPECT_EQ(test, 0xd0, data_length);
KUNIT_EXPECT_EQ(test, 0x02, tag);
KUNIT_EXPECT_EQ(test, 0x0d, channel);
KUNIT_EXPECT_EQ(test, 0x0e, tcode);
KUNIT_EXPECT_EQ(test, 0x0c, sy);
serialize_isoc_header(&header, data_length, tag, channel, tcode, sy);
KUNIT_EXPECT_EQ(test, header, expected);
}
static struct kunit_case packet_serdes_test_cases[] = {
KUNIT_CASE(test_async_header_write_quadlet_request),
KUNIT_CASE(test_async_header_write_block_request),
KUNIT_CASE(test_async_header_write_response),
KUNIT_CASE(test_async_header_read_quadlet_request),
KUNIT_CASE(test_async_header_read_quadlet_response),
KUNIT_CASE(test_async_header_read_block_request),
KUNIT_CASE(test_async_header_read_block_response),
KUNIT_CASE(test_async_header_lock_request),
KUNIT_CASE(test_async_header_lock_response),
KUNIT_CASE(test_isoc_header),
{}
};
static struct kunit_suite packet_serdes_test_suite = {
.name = "firewire-packet-serdes",
.test_cases = packet_serdes_test_cases,
};
kunit_test_suite(packet_serdes_test_suite);
MODULE_LICENSE("GPL");

View file

@ -463,7 +463,8 @@ struct fw_iso_packet {
u32 tag:2; /* tx: Tag in packet header */
u32 sy:4; /* tx: Sy in packet header */
u32 header_length:8; /* Length of immediate header */
u32 header[]; /* tx: Top of 1394 isoch. data_block */
/* tx: Top of 1394 isoch. data_block */
u32 header[] __counted_by(header_length);
};
#define FW_ISO_CONTEXT_TRANSMIT 0

View file

@ -0,0 +1,348 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2024 Takashi Sakamoto
#define TRACE_SYSTEM firewire
#if !defined(_FIREWIRE_TRACE_EVENT_H) || defined(TRACE_HEADER_MULTI_READ)
#define _FIREWIRE_TRACE_EVENT_H
#include <linux/tracepoint.h>
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include "../../../drivers/firewire/packet-header-definitions.h"
// The content of TP_printk field is preprocessed, then put to the module binary.
#define ASYNC_HEADER_GET_DESTINATION(header) \
(((header)[0] & ASYNC_HEADER_Q0_DESTINATION_MASK) >> ASYNC_HEADER_Q0_DESTINATION_SHIFT)
#define ASYNC_HEADER_GET_TLABEL(header) \
(((header)[0] & ASYNC_HEADER_Q0_TLABEL_MASK) >> ASYNC_HEADER_Q0_TLABEL_SHIFT)
#define ASYNC_HEADER_GET_TCODE(header) \
(((header)[0] & ASYNC_HEADER_Q0_TCODE_MASK) >> ASYNC_HEADER_Q0_TCODE_SHIFT)
#define ASYNC_HEADER_GET_SOURCE(header) \
(((header)[1] & ASYNC_HEADER_Q1_SOURCE_MASK) >> ASYNC_HEADER_Q1_SOURCE_SHIFT)
#define ASYNC_HEADER_GET_OFFSET(header) \
((((unsigned long long)((header)[1] & ASYNC_HEADER_Q1_OFFSET_HIGH_MASK)) >> ASYNC_HEADER_Q1_OFFSET_HIGH_SHIFT) << 32)| \
(header)[2]
#define ASYNC_HEADER_GET_RCODE(header) \
(((header)[1] & ASYNC_HEADER_Q1_RCODE_MASK) >> ASYNC_HEADER_Q1_RCODE_SHIFT)
#define QUADLET_SIZE 4
DECLARE_EVENT_CLASS(async_outbound_initiate_template,
TP_PROTO(u64 transaction, unsigned int generation, unsigned int scode, const u32 *header, const u32 *data, unsigned int data_count),
TP_ARGS(transaction, generation, scode, header, data, data_count),
TP_STRUCT__entry(
__field(u64, transaction)
__field(u8, generation)
__field(u8, scode)
__array(u32, header, ASYNC_HEADER_QUADLET_COUNT)
__dynamic_array(u32, data, data_count)
),
TP_fast_assign(
__entry->transaction = transaction;
__entry->generation = generation;
__entry->scode = scode;
memcpy(__entry->header, header, QUADLET_SIZE * ASYNC_HEADER_QUADLET_COUNT);
memcpy(__get_dynamic_array(data), data, __get_dynamic_array_len(data));
),
// This format is for the request subaction.
TP_printk(
"transaction=0x%llx generation=%u scode=%u dst_id=0x%04x tlabel=%u tcode=%u src_id=0x%04x offset=0x%012llx header=%s data=%s",
__entry->transaction,
__entry->generation,
__entry->scode,
ASYNC_HEADER_GET_DESTINATION(__entry->header),
ASYNC_HEADER_GET_TLABEL(__entry->header),
ASYNC_HEADER_GET_TCODE(__entry->header),
ASYNC_HEADER_GET_SOURCE(__entry->header),
ASYNC_HEADER_GET_OFFSET(__entry->header),
__print_array(__entry->header, ASYNC_HEADER_QUADLET_COUNT, QUADLET_SIZE),
__print_array(__get_dynamic_array(data),
__get_dynamic_array_len(data) / QUADLET_SIZE, QUADLET_SIZE)
)
);
// The value of status is one of ack codes and rcodes specific to Linux FireWire subsystem.
DECLARE_EVENT_CLASS(async_outbound_complete_template,
TP_PROTO(u64 transaction, unsigned int generation, unsigned int scode, unsigned int status, unsigned int timestamp),
TP_ARGS(transaction, generation, scode, status, timestamp),
TP_STRUCT__entry(
__field(u64, transaction)
__field(u8, generation)
__field(u8, scode)
__field(u8, status)
__field(u16, timestamp)
),
TP_fast_assign(
__entry->transaction = transaction;
__entry->generation = generation;
__entry->scode = scode;
__entry->status = status;
__entry->timestamp = timestamp;
),
TP_printk(
"transaction=0x%llx generation=%u scode=%u status=%u timestamp=0x%04x",
__entry->transaction,
__entry->generation,
__entry->scode,
__entry->status,
__entry->timestamp
)
);
// The value of status is one of ack codes and rcodes specific to Linux FireWire subsystem.
DECLARE_EVENT_CLASS(async_inbound_template,
TP_PROTO(u64 transaction, unsigned int generation, unsigned int scode, unsigned int status, unsigned int timestamp, const u32 *header, const u32 *data, unsigned int data_count),
TP_ARGS(transaction, generation, scode, status, timestamp, header, data, data_count),
TP_STRUCT__entry(
__field(u64, transaction)
__field(u8, generation)
__field(u8, scode)
__field(u8, status)
__field(u16, timestamp)
__array(u32, header, ASYNC_HEADER_QUADLET_COUNT)
__dynamic_array(u32, data, data_count)
),
TP_fast_assign(
__entry->transaction = transaction;
__entry->generation = generation;
__entry->scode = scode;
__entry->status = status;
__entry->timestamp = timestamp;
memcpy(__entry->header, header, QUADLET_SIZE * ASYNC_HEADER_QUADLET_COUNT);
memcpy(__get_dynamic_array(data), data, __get_dynamic_array_len(data));
),
// This format is for the response subaction.
TP_printk(
"transaction=0x%llx generation=%u scode=%u status=%u timestamp=0x%04x dst_id=0x%04x tlabel=%u tcode=%u src_id=0x%04x rcode=%u header=%s data=%s",
__entry->transaction,
__entry->generation,
__entry->scode,
__entry->status,
__entry->timestamp,
ASYNC_HEADER_GET_DESTINATION(__entry->header),
ASYNC_HEADER_GET_TLABEL(__entry->header),
ASYNC_HEADER_GET_TCODE(__entry->header),
ASYNC_HEADER_GET_SOURCE(__entry->header),
ASYNC_HEADER_GET_RCODE(__entry->header),
__print_array(__entry->header, ASYNC_HEADER_QUADLET_COUNT, QUADLET_SIZE),
__print_array(__get_dynamic_array(data),
__get_dynamic_array_len(data) / QUADLET_SIZE, QUADLET_SIZE)
)
);
DEFINE_EVENT(async_outbound_initiate_template, async_request_outbound_initiate,
TP_PROTO(u64 transaction, unsigned int generation, unsigned int scode, const u32 *header, const u32 *data, unsigned int data_count),
TP_ARGS(transaction, generation, scode, header, data, data_count)
);
DEFINE_EVENT(async_outbound_complete_template, async_request_outbound_complete,
TP_PROTO(u64 transaction, unsigned int generation, unsigned int scode, unsigned int status, unsigned int timestamp),
TP_ARGS(transaction, generation, scode, status, timestamp)
);
DEFINE_EVENT(async_inbound_template, async_response_inbound,
TP_PROTO(u64 transaction, unsigned int generation, unsigned int scode, unsigned int status, unsigned int timestamp, const u32 *header, const u32 *data, unsigned int data_count),
TP_ARGS(transaction, generation, scode, status, timestamp, header, data, data_count)
);
DEFINE_EVENT_PRINT(async_inbound_template, async_request_inbound,
TP_PROTO(u64 transaction, unsigned int generation, unsigned int scode, unsigned int status, unsigned int timestamp, const u32 *header, const u32 *data, unsigned int data_count),
TP_ARGS(transaction, generation, scode, status, timestamp, header, data, data_count),
TP_printk(
"transaction=0x%llx generation=%u scode=%u status=%u timestamp=0x%04x dst_id=0x%04x tlabel=%u tcode=%u src_id=0x%04x offset=0x%012llx header=%s data=%s",
__entry->transaction,
__entry->generation,
__entry->scode,
__entry->status,
__entry->timestamp,
ASYNC_HEADER_GET_DESTINATION(__entry->header),
ASYNC_HEADER_GET_TLABEL(__entry->header),
ASYNC_HEADER_GET_TCODE(__entry->header),
ASYNC_HEADER_GET_SOURCE(__entry->header),
ASYNC_HEADER_GET_OFFSET(__entry->header),
__print_array(__entry->header, ASYNC_HEADER_QUADLET_COUNT, QUADLET_SIZE),
__print_array(__get_dynamic_array(data),
__get_dynamic_array_len(data) / QUADLET_SIZE, QUADLET_SIZE)
)
);
DEFINE_EVENT_PRINT(async_outbound_initiate_template, async_response_outbound_initiate,
TP_PROTO(u64 transaction, unsigned int generation, unsigned int scode, const u32 *header, const u32 *data, unsigned int data_count),
TP_ARGS(transaction, generation, scode, header, data, data_count),
TP_printk(
"transaction=0x%llx generation=%u scode=%u dst_id=0x%04x tlabel=%u tcode=%u src_id=0x%04x rcode=%u header=%s data=%s",
__entry->transaction,
__entry->generation,
__entry->scode,
ASYNC_HEADER_GET_DESTINATION(__entry->header),
ASYNC_HEADER_GET_TLABEL(__entry->header),
ASYNC_HEADER_GET_TCODE(__entry->header),
ASYNC_HEADER_GET_SOURCE(__entry->header),
ASYNC_HEADER_GET_RCODE(__entry->header),
__print_array(__entry->header, ASYNC_HEADER_QUADLET_COUNT, QUADLET_SIZE),
__print_array(__get_dynamic_array(data),
__get_dynamic_array_len(data) / QUADLET_SIZE, QUADLET_SIZE)
)
);
DEFINE_EVENT(async_outbound_complete_template, async_response_outbound_complete,
TP_PROTO(u64 transaction, unsigned int generation, unsigned int scode, unsigned int status, unsigned int timestamp),
TP_ARGS(transaction, generation, scode, status, timestamp)
);
#undef ASYNC_HEADER_GET_DESTINATION
#undef ASYNC_HEADER_GET_TLABEL
#undef ASYNC_HEADER_GET_TCODE
#undef ASYNC_HEADER_GET_SOURCE
#undef ASYNC_HEADER_GET_OFFSET
#undef ASYNC_HEADER_GET_RCODE
TRACE_EVENT(async_phy_outbound_initiate,
TP_PROTO(u64 packet, unsigned int generation, u32 first_quadlet, u32 second_quadlet),
TP_ARGS(packet, generation, first_quadlet, second_quadlet),
TP_STRUCT__entry(
__field(u64, packet)
__field(u8, generation)
__field(u32, first_quadlet)
__field(u32, second_quadlet)
),
TP_fast_assign(
__entry->packet = packet;
__entry->generation = generation;
__entry->first_quadlet = first_quadlet;
__entry->second_quadlet = second_quadlet
),
TP_printk(
"packet=0x%llx generation=%u first_quadlet=0x%08x second_quadlet=0x%08x",
__entry->packet,
__entry->generation,
__entry->first_quadlet,
__entry->second_quadlet
)
);
TRACE_EVENT(async_phy_outbound_complete,
TP_PROTO(u64 packet, unsigned int generation, unsigned int status, unsigned int timestamp),
TP_ARGS(packet, generation, status, timestamp),
TP_STRUCT__entry(
__field(u64, packet)
__field(u8, generation)
__field(u8, status)
__field(u16, timestamp)
),
TP_fast_assign(
__entry->packet = packet;
__entry->generation = generation;
__entry->status = status;
__entry->timestamp = timestamp;
),
TP_printk(
"packet=0x%llx generation=%u status=%u timestamp=0x%04x",
__entry->packet,
__entry->generation,
__entry->status,
__entry->timestamp
)
);
TRACE_EVENT(async_phy_inbound,
TP_PROTO(u64 packet, unsigned int generation, unsigned int status, unsigned int timestamp, u32 first_quadlet, u32 second_quadlet),
TP_ARGS(packet, generation, status, timestamp, first_quadlet, second_quadlet),
TP_STRUCT__entry(
__field(u64, packet)
__field(u8, generation)
__field(u8, status)
__field(u16, timestamp)
__field(u32, first_quadlet)
__field(u32, second_quadlet)
),
TP_fast_assign(
__entry->packet = packet;
__entry->generation = generation;
__entry->status = status;
__entry->timestamp = timestamp;
__entry->first_quadlet = first_quadlet;
__entry->second_quadlet = second_quadlet
),
TP_printk(
"packet=0x%llx generation=%u status=%u timestamp=0x%04x first_quadlet=0x%08x second_quadlet=0x%08x",
__entry->packet,
__entry->generation,
__entry->status,
__entry->timestamp,
__entry->first_quadlet,
__entry->second_quadlet
)
);
DECLARE_EVENT_CLASS(bus_reset_arrange_template,
TP_PROTO(unsigned int generation, bool short_reset),
TP_ARGS(generation, short_reset),
TP_STRUCT__entry(
__field(u8, generation)
__field(bool, short_reset)
),
TP_fast_assign(
__entry->generation = generation;
__entry->short_reset = short_reset;
),
TP_printk(
"generation=%u short_reset=%s",
__entry->generation,
__entry->short_reset ? "true" : "false"
)
);
DEFINE_EVENT(bus_reset_arrange_template, bus_reset_initiate,
TP_PROTO(unsigned int generation, bool short_reset),
TP_ARGS(generation, short_reset)
);
DEFINE_EVENT(bus_reset_arrange_template, bus_reset_schedule,
TP_PROTO(unsigned int generation, bool short_reset),
TP_ARGS(generation, short_reset)
);
DEFINE_EVENT(bus_reset_arrange_template, bus_reset_postpone,
TP_PROTO(unsigned int generation, bool short_reset),
TP_ARGS(generation, short_reset)
);
TRACE_EVENT(bus_reset_handle,
TP_PROTO(unsigned int generation, unsigned int node_id, bool bm_abdicate, u32 *self_ids, unsigned int self_id_count),
TP_ARGS(generation, node_id, bm_abdicate, self_ids, self_id_count),
TP_STRUCT__entry(
__field(u8, generation)
__field(u8, node_id)
__field(bool, bm_abdicate)
__dynamic_array(u32, self_ids, self_id_count)
),
TP_fast_assign(
__entry->generation = generation;
__entry->node_id = node_id;
__entry->bm_abdicate = bm_abdicate;
memcpy(__get_dynamic_array(self_ids), self_ids, __get_dynamic_array_len(self_ids));
),
TP_printk(
"generation=%u node_id=0x%04x bm_abdicate=%s self_ids=%s",
__entry->generation,
__entry->node_id,
__entry->bm_abdicate ? "true" : "false",
__print_array(__get_dynamic_array(self_ids),
__get_dynamic_array_len(self_ids) / QUADLET_SIZE, QUADLET_SIZE)
)
);
#undef QUADLET_SIZE
#endif // _FIREWIRE_TRACE_EVENT_H
#include <trace/define_trace.h>