Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,8 @@
CONFIG_KUNIT=y
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
CONFIG_FIREWIRE_KUNIT_SELF_ID_SEQUENCE_HELPER_TEST=y
CONFIG_FIREWIRE_KUNIT_OHCI_SERDES_TEST=y
+162
View File
@@ -0,0 +1,162 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "IEEE 1394 (FireWire) support"
depends on PCI || COMPILE_TEST
# firewire-core does not depend on PCI but is
# not useful without PCI controller driver
config FIREWIRE
tristate "FireWire driver stack"
select CRC_ITU_T
help
This is the new-generation IEEE 1394 (FireWire) driver stack
a.k.a. Juju, a new implementation designed for robustness and
simplicity.
See http://ieee1394.docs.kernel.org/en/latest/migration.html
for information about migration from the older Linux 1394 stack
to the new driver stack.
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_KUNIT_DEVICE_ATTRIBUTE_TEST
tristate "KUnit tests for device attributes" if !KUNIT_ALL_TESTS
depends on FIREWIRE && KUNIT
default KUNIT_ALL_TESTS
help
This builds the KUnit tests for device attribute for node and
unit.
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_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_KUNIT_SELF_ID_SEQUENCE_HELPER_TEST
tristate "KUnit tests for helpers of self ID sequence" if !KUNIT_ALL_TESTS
depends on FIREWIRE && KUNIT
default KUNIT_ALL_TESTS
help
This builds the KUnit tests for helpers of self ID sequence.
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
help
Enable this driver if you have a FireWire controller based
on the OHCI specification. For all practical purposes, this
is the only chipset in use, so say Y here.
To compile this driver as a module, say M here: The module will be
called firewire-ohci.
config FIREWIRE_KUNIT_OHCI_SERDES_TEST
tristate "KUnit tests for serialization/deserialization of data in buffers/registers" if !KUNIT_ALL_TESTS
depends on FIREWIRE && KUNIT
default KUNIT_ALL_TESTS
help
This builds the KUnit tests to check serialization and deserialization
of data in buffers and registers defined in 1394 OHCI specification.
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_SBP2
tristate "Storage devices (SBP-2 protocol)"
depends on FIREWIRE && SCSI
help
This option enables you to use SBP-2 devices connected to a
FireWire bus. SBP-2 devices include storage devices like
harddisks and DVD drives, also some other FireWire devices
like scanners.
To compile this driver as a module, say M here: The module will be
called firewire-sbp2.
You should also enable support for disks, CD-ROMs, etc. in the SCSI
configuration section.
config FIREWIRE_NET
tristate "IP networking over 1394"
depends on FIREWIRE && INET
help
This enables IPv4/IPv6 over IEEE 1394, providing IP connectivity
with other implementations of RFC 2734/3146 as found on several
operating systems. Multicast support is currently limited.
To compile this driver as a module, say M here: The module will be
called firewire-net.
config FIREWIRE_NOSY
tristate "Nosy - a FireWire traffic sniffer for PCILynx cards"
depends on PCI
help
Nosy is an IEEE 1394 packet sniffer that is used for protocol
analysis and in development of IEEE 1394 drivers, applications,
or firmwares.
This driver lets you use a Texas Instruments PCILynx 1394 to PCI
link layer controller TSB12LV21/A/B as a low-budget bus analyzer.
PCILynx is a nowadays very rare IEEE 1394 controller which is
not OHCI 1394 compliant.
The following cards are known to be based on PCILynx or PCILynx-2:
IOI IOI-1394TT (PCI card), Unibrain Fireboard 400 PCI Lynx-2
(PCI card), Newer Technology FireWire 2 Go (CardBus card),
Apple Power Mac G3 blue & white and G4 with PCI graphics
(onboard controller).
To compile this driver as a module, say M here: The module will be
called nosy. Source code of a userspace interface to nosy, called
nosy-dump, can be found in tools/firewire/ of the kernel sources.
If unsure, say N.
endmenu
+22
View File
@@ -0,0 +1,22 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the Linux IEEE 1394 implementation
#
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
firewire-net-y += net.o
obj-$(CONFIG_FIREWIRE) += firewire-core.o
obj-$(CONFIG_FIREWIRE_OHCI) += firewire-ohci.o
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
obj-$(CONFIG_FIREWIRE_KUNIT_UAPI_TEST) += uapi-test.o
obj-$(CONFIG_FIREWIRE_KUNIT_PACKET_SERDES_TEST) += packet-serdes-test.o
obj-$(CONFIG_FIREWIRE_KUNIT_SELF_ID_SEQUENCE_HELPER_TEST) += self-id-sequence-helper-test.o
obj-$(CONFIG_FIREWIRE_KUNIT_OHCI_SERDES_TEST) += ohci-serdes-test.o
+837
View File
@@ -0,0 +1,837 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2005-2007 Kristian Hoegsberg <krh@bitplanet.net>
*/
#include <linux/bug.h>
#include <linux/completion.h>
#include <linux/crc-itu-t.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/atomic.h>
#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, ...) \
{ \
struct va_format vaf; \
va_list args; \
\
va_start(args, fmt); \
vaf.fmt = fmt; \
vaf.va = &args; \
printk(kern_level KBUILD_MODNAME " %s: %pV", \
dev_name(card->device), &vaf); \
va_end(args); \
}
define_fw_printk_level(fw_err, KERN_ERR);
define_fw_printk_level(fw_notice, KERN_NOTICE);
int fw_compute_block_crc(__be32 *block)
{
int length;
u16 crc;
length = (be32_to_cpu(block[0]) >> 16) & 0xff;
crc = crc_itu_t(0, (u8 *)&block[1], length * 4);
*block |= cpu_to_be32(crc);
return length;
}
static DEFINE_MUTEX(card_mutex);
static LIST_HEAD(card_list);
static LIST_HEAD(descriptor_list);
static int descriptor_count;
static __be32 tmp_config_rom[256];
/* ROM header, bus info block, root dir header, capabilities = 7 quadlets */
static size_t config_rom_length = 1 + 4 + 1 + 1;
#define BIB_CRC(v) ((v) << 0)
#define BIB_CRC_LENGTH(v) ((v) << 16)
#define BIB_INFO_LENGTH(v) ((v) << 24)
#define BIB_BUS_NAME 0x31333934 /* "1394" */
#define BIB_LINK_SPEED(v) ((v) << 0)
#define BIB_GENERATION(v) ((v) << 4)
#define BIB_MAX_ROM(v) ((v) << 8)
#define BIB_MAX_RECEIVE(v) ((v) << 12)
#define BIB_CYC_CLK_ACC(v) ((v) << 16)
#define BIB_PMC ((1) << 27)
#define BIB_BMC ((1) << 28)
#define BIB_ISC ((1) << 29)
#define BIB_CMC ((1) << 30)
#define BIB_IRMC ((1) << 31)
#define NODE_CAPABILITIES 0x0c0083c0 /* per IEEE 1394 clause 8.3.2.6.5.2 */
/*
* IEEE-1394 specifies a default SPLIT_TIMEOUT value of 800 cycles (100 ms),
* but we have to make it longer because there are many devices whose firmware
* is just too slow for that.
*/
#define DEFAULT_SPLIT_TIMEOUT (2 * 8000)
static void generate_config_rom(struct fw_card *card, __be32 *config_rom)
{
struct fw_descriptor *desc;
int i, j, k, length;
/*
* Initialize contents of config rom buffer. On the OHCI
* controller, block reads to the config rom accesses the host
* memory, but quadlet read access the hardware bus info block
* registers. That's just crack, but it means we should make
* sure the contents of bus info block in host memory matches
* the version stored in the OHCI registers.
*/
config_rom[0] = cpu_to_be32(
BIB_CRC_LENGTH(4) | BIB_INFO_LENGTH(4) | BIB_CRC(0));
config_rom[1] = cpu_to_be32(BIB_BUS_NAME);
config_rom[2] = cpu_to_be32(
BIB_LINK_SPEED(card->link_speed) |
BIB_GENERATION(card->config_rom_generation++ % 14 + 2) |
BIB_MAX_ROM(2) |
BIB_MAX_RECEIVE(card->max_receive) |
BIB_BMC | BIB_ISC | BIB_CMC | BIB_IRMC);
config_rom[3] = cpu_to_be32(card->guid >> 32);
config_rom[4] = cpu_to_be32(card->guid);
/* Generate root directory. */
config_rom[6] = cpu_to_be32(NODE_CAPABILITIES);
i = 7;
j = 7 + descriptor_count;
/* Generate root directory entries for descriptors. */
list_for_each_entry (desc, &descriptor_list, link) {
if (desc->immediate > 0)
config_rom[i++] = cpu_to_be32(desc->immediate);
config_rom[i] = cpu_to_be32(desc->key | (j - i));
i++;
j += desc->length;
}
/* Update root directory length. */
config_rom[5] = cpu_to_be32((i - 5 - 1) << 16);
/* End of root directory, now copy in descriptors. */
list_for_each_entry (desc, &descriptor_list, link) {
for (k = 0; k < desc->length; k++)
config_rom[i + k] = cpu_to_be32(desc->data[k]);
i += desc->length;
}
/* Calculate CRCs for all blocks in the config rom. This
* assumes that CRC length and info length are identical for
* the bus info block, which is always the case for this
* implementation. */
for (i = 0; i < j; i += length + 1)
length = fw_compute_block_crc(config_rom + i);
WARN_ON(j != config_rom_length);
}
static void update_config_roms(void)
{
struct fw_card *card;
list_for_each_entry (card, &card_list, link) {
generate_config_rom(card, tmp_config_rom);
card->driver->set_config_rom(card, tmp_config_rom,
config_rom_length);
}
}
static size_t required_space(struct fw_descriptor *desc)
{
/* descriptor + entry into root dir + optional immediate entry */
return desc->length + 1 + (desc->immediate > 0 ? 1 : 0);
}
int fw_core_add_descriptor(struct fw_descriptor *desc)
{
size_t i;
/*
* Check descriptor is valid; the length of all blocks in the
* descriptor has to add up to exactly the length of the
* block.
*/
i = 0;
while (i < desc->length)
i += (desc->data[i] >> 16) + 1;
if (i != desc->length)
return -EINVAL;
guard(mutex)(&card_mutex);
if (config_rom_length + required_space(desc) > 256)
return -EBUSY;
list_add_tail(&desc->link, &descriptor_list);
config_rom_length += required_space(desc);
descriptor_count++;
if (desc->immediate > 0)
descriptor_count++;
update_config_roms();
return 0;
}
EXPORT_SYMBOL(fw_core_add_descriptor);
void fw_core_remove_descriptor(struct fw_descriptor *desc)
{
guard(mutex)(&card_mutex);
list_del(&desc->link);
config_rom_length -= required_space(desc);
descriptor_count--;
if (desc->immediate > 0)
descriptor_count--;
update_config_roms();
}
EXPORT_SYMBOL(fw_core_remove_descriptor);
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->index, 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->index, card->generation, short_reset);
/* We don't try hard to sort out requests of long vs. short resets. */
card->br_short = short_reset;
/* Use an arbitrary short delay to combine multiple reset requests. */
fw_card_get(card);
if (!queue_delayed_work(fw_workqueue, &card->br_work, delayed ? msecs_to_jiffies(10) : 0))
fw_card_put(card);
}
EXPORT_SYMBOL(fw_schedule_bus_reset);
static void br_work(struct work_struct *work)
{
struct fw_card *card = from_work(card, work, br_work.work);
/* Delay for 2s after last reset per IEEE 1394 clause 8.2.1. */
if (card->reset_jiffies != 0 &&
time_is_after_jiffies64(card->reset_jiffies + secs_to_jiffies(2))) {
trace_bus_reset_postpone(card->index, card->generation, card->br_short);
if (!queue_delayed_work(fw_workqueue, &card->br_work, secs_to_jiffies(2)))
fw_card_put(card);
return;
}
fw_send_phy_config(card, FW_PHY_CONFIG_NO_NODE_ID, card->generation,
FW_PHY_CONFIG_CURRENT_GAP_COUNT);
reset_bus(card, card->br_short);
fw_card_put(card);
}
static void allocate_broadcast_channel(struct fw_card *card, int generation)
{
int channel, bandwidth = 0;
if (!card->broadcast_channel_allocated) {
fw_iso_resource_manage(card, generation, 1ULL << 31,
&channel, &bandwidth, true);
if (channel != 31) {
fw_notice(card, "failed to allocate broadcast channel\n");
return;
}
card->broadcast_channel_allocated = true;
}
device_for_each_child(card->device, (void *)(long)generation,
fw_device_set_broadcast_channel);
}
void fw_schedule_bm_work(struct fw_card *card, unsigned long delay)
{
fw_card_get(card);
if (!schedule_delayed_work(&card->bm_work, delay))
fw_card_put(card);
}
enum bm_contention_outcome {
// The bus management contention window is not expired.
BM_CONTENTION_OUTCOME_WITHIN_WINDOW = 0,
// The IRM node has link off.
BM_CONTENTION_OUTCOME_IRM_HAS_LINK_OFF,
// The IRM node complies IEEE 1394:1994 only.
BM_CONTENTION_OUTCOME_IRM_COMPLIES_1394_1995_ONLY,
// Another bus reset, BM work has been rescheduled.
BM_CONTENTION_OUTCOME_AT_NEW_GENERATION,
// We have been unable to send the lock request to IRM node due to some local problem.
BM_CONTENTION_OUTCOME_LOCAL_PROBLEM_AT_TRANSACTION,
// The lock request failed, maybe the IRM isn't really IRM capable after all.
BM_CONTENTION_OUTCOME_IRM_IS_NOT_CAPABLE_FOR_IRM,
// Somebody else is BM.
BM_CONTENTION_OUTCOME_IRM_HOLDS_ANOTHER_NODE_AS_BM,
// The local node succeeds after contending for bus manager.
BM_CONTENTION_OUTCOME_IRM_HOLDS_LOCAL_NODE_AS_BM,
};
static enum bm_contention_outcome contend_for_bm(struct fw_card *card)
__must_hold(&card->lock)
{
int generation = card->generation;
int local_id = card->local_node->node_id;
__be32 data[2] = {
cpu_to_be32(BUS_MANAGER_ID_NOT_REGISTERED),
cpu_to_be32(local_id),
};
bool grace = time_is_before_jiffies64(card->reset_jiffies + msecs_to_jiffies(125));
struct fw_node *irm_node;
struct fw_device *irm_device;
int irm_node_id, irm_device_quirks = 0;
int rcode;
lockdep_assert_held(&card->lock);
if (!grace) {
if (!is_next_generation(generation, card->bm_generation) || card->bm_abdicate)
return BM_CONTENTION_OUTCOME_WITHIN_WINDOW;
}
irm_node = card->irm_node;
if (!irm_node->link_on) {
fw_notice(card, "IRM has link off, making local node (%02x) root\n", local_id);
return BM_CONTENTION_OUTCOME_IRM_HAS_LINK_OFF;
}
// NOTE: It is likely that the quirk detection for IRM device has not done yet.
irm_device = fw_node_get_device(irm_node);
if (irm_device)
irm_device_quirks = READ_ONCE(irm_device->quirks);
if ((irm_device_quirks & FW_DEVICE_QUIRK_IRM_IS_1394_1995_ONLY) &&
!(irm_device_quirks & FW_DEVICE_QUIRK_IRM_IGNORES_BUS_MANAGER)) {
fw_notice(card, "IRM is not 1394a compliant, making local node (%02x) root\n",
local_id);
return BM_CONTENTION_OUTCOME_IRM_COMPLIES_1394_1995_ONLY;
}
irm_node_id = irm_node->node_id;
spin_unlock_irq(&card->lock);
rcode = fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP, irm_node_id, generation,
SCODE_100, CSR_REGISTER_BASE + CSR_BUS_MANAGER_ID, data,
sizeof(data));
spin_lock_irq(&card->lock);
switch (rcode) {
case RCODE_GENERATION:
return BM_CONTENTION_OUTCOME_AT_NEW_GENERATION;
case RCODE_SEND_ERROR:
return BM_CONTENTION_OUTCOME_LOCAL_PROBLEM_AT_TRANSACTION;
case RCODE_COMPLETE:
{
int bm_id = be32_to_cpu(data[0]);
// Used by cdev layer for "struct fw_cdev_event_bus_reset".
if (bm_id != BUS_MANAGER_ID_NOT_REGISTERED)
card->bm_node_id = 0xffc0 & bm_id;
else
card->bm_node_id = local_id;
if (bm_id != BUS_MANAGER_ID_NOT_REGISTERED)
return BM_CONTENTION_OUTCOME_IRM_HOLDS_ANOTHER_NODE_AS_BM;
else
return BM_CONTENTION_OUTCOME_IRM_HOLDS_LOCAL_NODE_AS_BM;
}
default:
if (!(irm_device_quirks & FW_DEVICE_QUIRK_IRM_IGNORES_BUS_MANAGER)) {
fw_notice(card, "BM lock failed (%s), making local node (%02x) root\n",
fw_rcode_string(rcode), local_id);
return BM_CONTENTION_OUTCOME_IRM_COMPLIES_1394_1995_ONLY;
} else {
return BM_CONTENTION_OUTCOME_IRM_IS_NOT_CAPABLE_FOR_IRM;
}
}
}
DEFINE_FREE(node_unref, struct fw_node *, if (_T) fw_node_put(_T))
DEFINE_FREE(card_unref, struct fw_card *, if (_T) fw_card_put(_T))
static void bm_work(struct work_struct *work)
{
static const char gap_count_table[] = {
63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40
};
struct fw_card *card __free(card_unref) = from_work(card, work, bm_work.work);
struct fw_node *root_node __free(node_unref) = NULL;
int root_id, new_root_id, irm_id, local_id;
int expected_gap_count, generation;
bool stand_for_root = false;
spin_lock_irq(&card->lock);
if (card->local_node == NULL) {
spin_unlock_irq(&card->lock);
return;
}
generation = card->generation;
root_node = fw_node_get(card->root_node);
root_id = root_node->node_id;
irm_id = card->irm_node->node_id;
local_id = card->local_node->node_id;
if (card->bm_generation != generation) {
enum bm_contention_outcome result = contend_for_bm(card);
switch (result) {
case BM_CONTENTION_OUTCOME_WITHIN_WINDOW:
spin_unlock_irq(&card->lock);
fw_schedule_bm_work(card, msecs_to_jiffies(125));
return;
case BM_CONTENTION_OUTCOME_IRM_HAS_LINK_OFF:
stand_for_root = true;
break;
case BM_CONTENTION_OUTCOME_IRM_COMPLIES_1394_1995_ONLY:
stand_for_root = true;
break;
case BM_CONTENTION_OUTCOME_AT_NEW_GENERATION:
// BM work has been rescheduled.
spin_unlock_irq(&card->lock);
return;
case BM_CONTENTION_OUTCOME_LOCAL_PROBLEM_AT_TRANSACTION:
// Let's try again later and hope that the local problem has gone away by
// then.
spin_unlock_irq(&card->lock);
fw_schedule_bm_work(card, msecs_to_jiffies(125));
return;
case BM_CONTENTION_OUTCOME_IRM_IS_NOT_CAPABLE_FOR_IRM:
// Let's do a bus reset and pick the local node as root, and thus, IRM.
stand_for_root = true;
break;
case BM_CONTENTION_OUTCOME_IRM_HOLDS_ANOTHER_NODE_AS_BM:
if (local_id == irm_id) {
// Only acts as IRM.
spin_unlock_irq(&card->lock);
allocate_broadcast_channel(card, generation);
spin_lock_irq(&card->lock);
}
fallthrough;
case BM_CONTENTION_OUTCOME_IRM_HOLDS_LOCAL_NODE_AS_BM:
default:
card->bm_generation = generation;
break;
}
}
// We're bus manager for this generation, so next step is to make sure we have an active
// cycle master and do gap count optimization.
if (!stand_for_root) {
if (card->gap_count == GAP_COUNT_MISMATCHED) {
// If self IDs have inconsistent gap counts, do a
// bus reset ASAP. The config rom read might never
// complete, so don't wait for it. However, still
// send a PHY configuration packet prior to the
// bus reset. The PHY configuration packet might
// fail, but 1394-2008 8.4.5.2 explicitly permits
// it in this case, so it should be safe to try.
stand_for_root = true;
// We must always send a bus reset if the gap count
// is inconsistent, so bypass the 5-reset limit.
card->bm_retries = 0;
} else {
// Now investigate root node.
struct fw_device *root_device = fw_node_get_device(root_node);
if (root_device == NULL) {
// Either link_on is false, or we failed to read the
// config rom. In either case, pick another root.
stand_for_root = true;
} else {
bool root_device_is_running =
atomic_read(&root_device->state) == FW_DEVICE_RUNNING;
if (!root_device_is_running) {
// If we haven't probed this device yet, bail out now
// and let's try again once that's done.
spin_unlock_irq(&card->lock);
return;
} else if (!root_device->cmc) {
// Current root has an active link layer and we
// successfully read the config rom, but it's not
// cycle master capable.
stand_for_root = true;
}
}
}
}
if (stand_for_root) {
new_root_id = local_id;
} else {
// We will send out a force root packet for this node as part of the gap count
// optimization on behalf of the node.
new_root_id = root_id;
}
/*
* Pick a gap count from 1394a table E-1. The table doesn't cover
* the typically much larger 1394b beta repeater delays though.
*/
if (!card->beta_repeaters_present &&
root_node->max_hops < ARRAY_SIZE(gap_count_table))
expected_gap_count = gap_count_table[root_node->max_hops];
else
expected_gap_count = 63;
// Finally, figure out if we should do a reset or not. If we have done less than 5 resets
// with the same physical topology and we have either a new root or a new gap count
// setting, let's do it.
if (card->bm_retries++ < 5 && (card->gap_count != expected_gap_count || new_root_id != root_id)) {
int card_gap_count = card->gap_count;
spin_unlock_irq(&card->lock);
fw_notice(card, "phy config: new root=%x, gap_count=%d\n",
new_root_id, expected_gap_count);
fw_send_phy_config(card, new_root_id, generation, expected_gap_count);
/*
* Where possible, use a short bus reset to minimize
* disruption to isochronous transfers. But in the event
* of a gap count inconsistency, use a long bus reset.
*
* As noted in 1394a 8.4.6.2, nodes on a mixed 1394/1394a bus
* may set different gap counts after a bus reset. On a mixed
* 1394/1394a bus, a short bus reset can get doubled. Some
* nodes may treat the double reset as one bus reset and others
* may treat it as two, causing a gap count inconsistency
* again. Using a long bus reset prevents this.
*/
reset_bus(card, card_gap_count != 0);
/* Will allocate broadcast channel after the reset. */
} else {
struct fw_device *root_device = fw_node_get_device(root_node);
spin_unlock_irq(&card->lock);
if (root_device && root_device->cmc) {
// Make sure that the cycle master sends cycle start packets.
__be32 data = cpu_to_be32(CSR_STATE_BIT_CMSTR);
int rcode = fw_run_transaction(card, TCODE_WRITE_QUADLET_REQUEST,
root_id, generation, SCODE_100,
CSR_REGISTER_BASE + CSR_STATE_SET,
&data, sizeof(data));
if (rcode == RCODE_GENERATION)
return;
}
if (local_id == irm_id)
allocate_broadcast_channel(card, generation);
}
}
void fw_card_initialize(struct fw_card *card,
const struct fw_card_driver *driver,
struct device *device)
{
static atomic_t index = ATOMIC_INIT(-1);
card->index = atomic_inc_return(&index);
card->driver = driver;
card->device = device;
card->transactions.current_tlabel = 0;
card->transactions.tlabel_mask = 0;
INIT_LIST_HEAD(&card->transactions.list);
spin_lock_init(&card->transactions.lock);
spin_lock_init(&card->topology_map.lock);
card->split_timeout.hi = DEFAULT_SPLIT_TIMEOUT / 8000;
card->split_timeout.lo = (DEFAULT_SPLIT_TIMEOUT % 8000) << 19;
card->split_timeout.cycles = DEFAULT_SPLIT_TIMEOUT;
card->split_timeout.jiffies = isoc_cycles_to_jiffies(DEFAULT_SPLIT_TIMEOUT);
spin_lock_init(&card->split_timeout.lock);
card->color = 0;
card->broadcast_channel = BROADCAST_CHANNEL_INITIAL;
kref_init(&card->kref);
init_completion(&card->done);
spin_lock_init(&card->lock);
card->local_node = NULL;
INIT_DELAYED_WORK(&card->br_work, br_work);
INIT_DELAYED_WORK(&card->bm_work, bm_work);
}
EXPORT_SYMBOL(fw_card_initialize);
DEFINE_FREE(workqueue_destroy, struct workqueue_struct *, if (_T) destroy_workqueue(_T))
int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid,
unsigned int supported_isoc_contexts)
{
struct workqueue_struct *isoc_wq __free(workqueue_destroy) = NULL;
struct workqueue_struct *async_wq __free(workqueue_destroy) = NULL;
int ret;
// This workqueue should be:
// * != WQ_BH Sleepable.
// * == WQ_UNBOUND Any core can process data for isoc context. The
// implementation of unit protocol could consumes the core
// longer somehow.
// * != WQ_MEM_RECLAIM Not used for any backend of block device.
// * == WQ_FREEZABLE Isochronous communication is at regular interval in real
// time, thus should be drained if possible at freeze phase.
// * == WQ_HIGHPRI High priority to process semi-realtime timestamped data.
// * == WQ_SYSFS Parameters are available via sysfs.
// * max_active == n_it + n_ir A hardIRQ could notify events for multiple isochronous
// contexts if they are scheduled to the same cycle.
isoc_wq = alloc_workqueue("firewire-isoc-card%u",
WQ_UNBOUND | WQ_FREEZABLE | WQ_HIGHPRI | WQ_SYSFS,
supported_isoc_contexts, card->index);
if (!isoc_wq)
return -ENOMEM;
// This workqueue should be:
// * != WQ_BH Sleepable.
// * == WQ_UNBOUND Any core can process data for asynchronous context.
// * == WQ_MEM_RECLAIM Used for any backend of block device.
// * == WQ_FREEZABLE The target device would not be available when being freezed.
// * == WQ_HIGHPRI High priority to process semi-realtime timestamped data.
// * == WQ_SYSFS Parameters are available via sysfs.
// * max_active == 4 A hardIRQ could notify events for a pair of requests and
// response AR/AT contexts.
async_wq = alloc_workqueue("firewire-async-card%u",
WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE | WQ_HIGHPRI | WQ_SYSFS,
4, card->index);
if (!async_wq)
return -ENOMEM;
card->isoc_wq = isoc_wq;
card->async_wq = async_wq;
card->max_receive = max_receive;
card->link_speed = link_speed;
card->guid = guid;
scoped_guard(mutex, &card_mutex) {
generate_config_rom(card, tmp_config_rom);
ret = card->driver->enable(card, tmp_config_rom, config_rom_length);
if (ret < 0) {
card->isoc_wq = NULL;
card->async_wq = NULL;
return ret;
}
retain_and_null_ptr(isoc_wq);
retain_and_null_ptr(async_wq);
list_add_tail(&card->link, &card_list);
}
return 0;
}
EXPORT_SYMBOL(fw_card_add);
/*
* The next few functions implement a dummy driver that is used once a card
* driver shuts down an fw_card. This allows the driver to cleanly unload,
* as all IO to the card will be handled (and failed) by the dummy driver
* instead of calling into the module. Only functions for iso context
* shutdown still need to be provided by the card driver.
*
* .read/write_csr() should never be called anymore after the dummy driver
* was bound since they are only used within request handler context.
* .set_config_rom() is never called since the card is taken out of card_list
* before switching to the dummy driver.
*/
static int dummy_read_phy_reg(struct fw_card *card, int address)
{
return -ENODEV;
}
static int dummy_update_phy_reg(struct fw_card *card, int address,
int clear_bits, int set_bits)
{
return -ENODEV;
}
static void dummy_send_request(struct fw_card *card, struct fw_packet *packet)
{
packet->callback(packet, card, RCODE_CANCELLED);
}
static void dummy_send_response(struct fw_card *card, struct fw_packet *packet)
{
packet->callback(packet, card, RCODE_CANCELLED);
}
static int dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet)
{
return -ENOENT;
}
static int dummy_enable_phys_dma(struct fw_card *card,
int node_id, int generation)
{
return -ENODEV;
}
static struct fw_iso_context *dummy_allocate_iso_context(struct fw_card *card, int type,
int channel, size_t header_size, size_t header_storage_size)
{
return ERR_PTR(-ENODEV);
}
static u32 dummy_read_csr(struct fw_card *card, int csr_offset)
{
return 0;
}
static void dummy_write_csr(struct fw_card *card, int csr_offset, u32 value)
{
}
static int dummy_start_iso(struct fw_iso_context *ctx,
s32 cycle, u32 sync, u32 tags)
{
return -ENODEV;
}
static int dummy_set_iso_channels(struct fw_iso_context *ctx, u64 *channels)
{
return -ENODEV;
}
static int dummy_queue_iso(struct fw_iso_context *ctx, struct fw_iso_packet *p,
struct fw_iso_buffer *buffer, unsigned long payload)
{
return -ENODEV;
}
static void dummy_flush_queue_iso(struct fw_iso_context *ctx)
{
}
static int dummy_flush_iso_completions(struct fw_iso_context *ctx)
{
return -ENODEV;
}
static const struct fw_card_driver dummy_driver_template = {
.read_phy_reg = dummy_read_phy_reg,
.update_phy_reg = dummy_update_phy_reg,
.send_request = dummy_send_request,
.send_response = dummy_send_response,
.cancel_packet = dummy_cancel_packet,
.enable_phys_dma = dummy_enable_phys_dma,
.read_csr = dummy_read_csr,
.write_csr = dummy_write_csr,
.allocate_iso_context = dummy_allocate_iso_context,
.start_iso = dummy_start_iso,
.set_iso_channels = dummy_set_iso_channels,
.queue_iso = dummy_queue_iso,
.flush_queue_iso = dummy_flush_queue_iso,
.flush_iso_completions = dummy_flush_iso_completions,
};
void fw_card_release(struct kref *kref)
{
struct fw_card *card = container_of(kref, struct fw_card, kref);
complete(&card->done);
}
EXPORT_SYMBOL_GPL(fw_card_release);
void fw_core_remove_card(struct fw_card *card)
{
struct fw_card_driver dummy_driver = dummy_driver_template;
might_sleep();
card->driver->update_phy_reg(card, 4,
PHY_LINK_ACTIVE | PHY_CONTENDER, 0);
fw_schedule_bus_reset(card, false, true);
scoped_guard(mutex, &card_mutex)
list_del_init(&card->link);
/* Switch off most of the card driver interface. */
dummy_driver.free_iso_context = card->driver->free_iso_context;
dummy_driver.stop_iso = card->driver->stop_iso;
dummy_driver.disable = card->driver->disable;
card->driver = &dummy_driver;
drain_workqueue(card->isoc_wq);
drain_workqueue(card->async_wq);
card->driver->disable(card);
fw_cancel_pending_transactions(card);
scoped_guard(spinlock_irqsave, &card->lock)
fw_destroy_nodes(card);
/* Wait for all users, especially device workqueue jobs, to finish. */
fw_card_put(card);
wait_for_completion(&card->done);
destroy_workqueue(card->isoc_wq);
destroy_workqueue(card->async_wq);
WARN_ON(!list_empty(&card->transactions.list));
}
EXPORT_SYMBOL(fw_core_remove_card);
/**
* fw_card_read_cycle_time: read from Isochronous Cycle Timer Register of 1394 OHCI in MMIO region
* for controller card.
* @card: The instance of card for 1394 OHCI controller.
* @cycle_time: The mutual reference to value of cycle time for the read operation.
*
* Read value from Isochronous Cycle Timer Register of 1394 OHCI in MMIO region for the given
* controller card. This function accesses the region without any lock primitives or IRQ mask.
* When returning successfully, the content of @value argument has value aligned to host endianness,
* formetted by CYCLE_TIME CSR Register of IEEE 1394 std.
*
* Context: Any context.
* Return:
* * 0 - Read successfully.
* * -ENODEV - The controller is unavailable due to being removed or unbound.
*/
int fw_card_read_cycle_time(struct fw_card *card, u32 *cycle_time)
{
if (card->driver->read_csr == dummy_read_csr)
return -ENODEV;
// It's possible to switch to dummy driver between the above and the below. This is the best
// effort to return -ENODEV.
*cycle_time = card->driver->read_csr(card, CSR_CYCLE_TIME);
return 0;
}
EXPORT_SYMBOL_GPL(fw_card_read_cycle_time);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+459
View File
@@ -0,0 +1,459 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Isochronous I/O functionality:
* - Isochronous DMA context management
* - Isochronous bus resource management (channels, bandwidth), client side
*
* Copyright (C) 2006 Kristian Hoegsberg <krh@bitplanet.net>
*/
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/vmalloc.h>
#include <linux/export.h>
#include <asm/byteorder.h>
#include "core.h"
#include <trace/events/firewire.h>
/*
* Isochronous DMA context management
*/
int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count)
{
struct page **page_array __free(kfree) = kzalloc_objs(page_array[0],
page_count);
if (!page_array)
return -ENOMEM;
// Retrieve noncontiguous pages. The descriptors for 1394 OHCI isochronous DMA contexts
// have a set of address and length per each, while the reason to use pages is the
// convenience to map them into virtual address space of user process.
unsigned long nr_populated = alloc_pages_bulk(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO,
page_count, page_array);
if (nr_populated != page_count) {
// Assuming the above call fills page_array sequentially from the beginning.
release_pages(page_array, nr_populated);
return -ENOMEM;
}
buffer->page_count = page_count;
buffer->pages = no_free_ptr(page_array);
return 0;
}
int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card,
enum dma_data_direction direction)
{
dma_addr_t *dma_addrs __free(kfree) = kzalloc_objs(dma_addrs[0],
buffer->page_count);
int i;
if (!dma_addrs)
return -ENOMEM;
// Retrieve DMA mapping addresses for the pages. They are not contiguous. Maintain the cache
// coherency for the pages by hand.
for (i = 0; i < buffer->page_count; i++) {
// The dma_map_phys() with a physical address per page is available here, instead.
dma_addr_t dma_addr = dma_map_page(card->device, buffer->pages[i], 0, PAGE_SIZE,
direction);
if (dma_mapping_error(card->device, dma_addr))
break;
dma_addrs[i] = dma_addr;
}
if (i < buffer->page_count) {
while (i-- > 0)
dma_unmap_page(card->device, dma_addrs[i], PAGE_SIZE, buffer->direction);
return -ENOMEM;
}
buffer->direction = direction;
buffer->dma_addrs = no_free_ptr(dma_addrs);
return 0;
}
int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
int page_count, enum dma_data_direction direction)
{
int ret;
ret = fw_iso_buffer_alloc(buffer, page_count);
if (ret < 0)
return ret;
ret = fw_iso_buffer_map_dma(buffer, card, direction);
if (ret < 0)
fw_iso_buffer_destroy(buffer, card);
return ret;
}
EXPORT_SYMBOL(fw_iso_buffer_init);
void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer,
struct fw_card *card)
{
if (buffer->dma_addrs) {
for (int i = 0; i < buffer->page_count; ++i) {
dma_addr_t dma_addr = buffer->dma_addrs[i];
dma_unmap_page(card->device, dma_addr, PAGE_SIZE, buffer->direction);
}
kfree(buffer->dma_addrs);
buffer->dma_addrs = NULL;
}
if (buffer->pages) {
release_pages(buffer->pages, buffer->page_count);
kfree(buffer->pages);
buffer->pages = NULL;
}
buffer->page_count = 0;
}
EXPORT_SYMBOL(fw_iso_buffer_destroy);
/* Convert DMA address to offset into virtually contiguous buffer. */
size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed)
{
for (int i = 0; i < buffer->page_count; i++) {
dma_addr_t dma_addr = buffer->dma_addrs[i];
ssize_t offset = (ssize_t)completed - (ssize_t)dma_addr;
if (offset > 0 && offset <= PAGE_SIZE)
return (i << PAGE_SHIFT) + offset;
}
return 0;
}
struct fw_iso_context *__fw_iso_context_create(struct fw_card *card, int type, int channel,
int speed, size_t header_size, size_t header_storage_size,
union fw_iso_callback callback, void *callback_data)
{
struct fw_iso_context *ctx;
ctx = card->driver->allocate_iso_context(card, type, channel, header_size,
header_storage_size);
if (IS_ERR(ctx))
return ctx;
ctx->card = card;
ctx->type = type;
ctx->channel = channel;
ctx->speed = speed;
ctx->flags = 0;
ctx->header_size = header_size;
ctx->header_storage_size = header_storage_size;
ctx->callback = callback;
ctx->callback_data = callback_data;
trace_isoc_outbound_allocate(ctx, channel, speed);
trace_isoc_inbound_single_allocate(ctx, channel, header_size);
trace_isoc_inbound_multiple_allocate(ctx);
return ctx;
}
EXPORT_SYMBOL(__fw_iso_context_create);
void fw_iso_context_destroy(struct fw_iso_context *ctx)
{
trace_isoc_outbound_destroy(ctx);
trace_isoc_inbound_single_destroy(ctx);
trace_isoc_inbound_multiple_destroy(ctx);
ctx->card->driver->free_iso_context(ctx);
}
EXPORT_SYMBOL(fw_iso_context_destroy);
int fw_iso_context_start(struct fw_iso_context *ctx,
int cycle, int sync, int tags)
{
trace_isoc_outbound_start(ctx, cycle);
trace_isoc_inbound_single_start(ctx, cycle, sync, tags);
trace_isoc_inbound_multiple_start(ctx, cycle, sync, tags);
return ctx->card->driver->start_iso(ctx, cycle, sync, tags);
}
EXPORT_SYMBOL(fw_iso_context_start);
int fw_iso_context_set_channels(struct fw_iso_context *ctx, u64 *channels)
{
trace_isoc_inbound_multiple_channels(ctx, *channels);
return ctx->card->driver->set_iso_channels(ctx, channels);
}
int fw_iso_context_queue(struct fw_iso_context *ctx,
struct fw_iso_packet *packet,
struct fw_iso_buffer *buffer,
unsigned long payload)
{
trace_isoc_outbound_queue(ctx, payload, packet);
trace_isoc_inbound_single_queue(ctx, payload, packet);
trace_isoc_inbound_multiple_queue(ctx, payload, packet);
return ctx->card->driver->queue_iso(ctx, packet, buffer, payload);
}
EXPORT_SYMBOL(fw_iso_context_queue);
void fw_iso_context_queue_flush(struct fw_iso_context *ctx)
{
trace_isoc_outbound_flush(ctx);
trace_isoc_inbound_single_flush(ctx);
trace_isoc_inbound_multiple_flush(ctx);
ctx->card->driver->flush_queue_iso(ctx);
}
EXPORT_SYMBOL(fw_iso_context_queue_flush);
/**
* fw_iso_context_flush_completions() - process isochronous context in current process context.
* @ctx: the isochronous context
*
* Process the isochronous context in the current process context. The registered callback function
* is called when a queued packet buffer with the interrupt flag is completed, either after
* transmission in the IT context or after being filled in the IR context. Additionally, the
* callback function is also called for the packet buffer completed at last. Furthermore, the
* callback function is called as well when the header buffer in the context becomes full. If it is
* required to process the context asynchronously, fw_iso_context_schedule_flush_completions() is
* available instead.
*
* Context: Process context. May sleep due to disable_work_sync().
*/
int fw_iso_context_flush_completions(struct fw_iso_context *ctx)
{
int err;
trace_isoc_outbound_flush_completions(ctx);
trace_isoc_inbound_single_flush_completions(ctx);
trace_isoc_inbound_multiple_flush_completions(ctx);
might_sleep();
// Avoid dead lock due to programming mistake.
if (WARN_ON_ONCE(current_work() == &ctx->work))
return 0;
disable_work_sync(&ctx->work);
err = ctx->card->driver->flush_iso_completions(ctx);
enable_work(&ctx->work);
return err;
}
EXPORT_SYMBOL(fw_iso_context_flush_completions);
int fw_iso_context_stop(struct fw_iso_context *ctx)
{
int err;
trace_isoc_outbound_stop(ctx);
trace_isoc_inbound_single_stop(ctx);
trace_isoc_inbound_multiple_stop(ctx);
might_sleep();
// Avoid dead lock due to programming mistake.
if (WARN_ON_ONCE(current_work() == &ctx->work))
return 0;
err = ctx->card->driver->stop_iso(ctx);
cancel_work_sync(&ctx->work);
return err;
}
EXPORT_SYMBOL(fw_iso_context_stop);
/*
* Isochronous bus resource management (channels, bandwidth), client side
*/
static int manage_bandwidth(struct fw_card *card, int irm_id, int generation,
int bandwidth, bool allocate)
{
int try, new, old = allocate ? BANDWIDTH_AVAILABLE_INITIAL : 0;
__be32 data[2];
/*
* On a 1394a IRM with low contention, try < 1 is enough.
* On a 1394-1995 IRM, we need at least try < 2.
* Let's just do try < 5.
*/
for (try = 0; try < 5; try++) {
new = allocate ? old - bandwidth : old + bandwidth;
if (new < 0 || new > BANDWIDTH_AVAILABLE_INITIAL)
return -EBUSY;
data[0] = cpu_to_be32(old);
data[1] = cpu_to_be32(new);
switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP,
irm_id, generation, SCODE_100,
CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE,
data, 8)) {
case RCODE_GENERATION:
/* A generation change frees all bandwidth. */
return allocate ? -EAGAIN : bandwidth;
case RCODE_COMPLETE:
if (be32_to_cpup(data) == old)
return bandwidth;
old = be32_to_cpup(data);
/* Fall through. */
}
}
return -EIO;
}
static int manage_channel(struct fw_card *card, int irm_id, int generation,
u32 channels_mask, u64 offset, bool allocate)
{
__be32 bit, all, old;
__be32 data[2];
int channel, ret = -EIO, retry = 5;
old = all = allocate ? cpu_to_be32(~0) : 0;
for (channel = 0; channel < 32; channel++) {
if (!(channels_mask & 1 << channel))
continue;
ret = -EBUSY;
bit = cpu_to_be32(1 << (31 - channel));
if ((old & bit) != (all & bit))
continue;
data[0] = old;
data[1] = old ^ bit;
switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP,
irm_id, generation, SCODE_100,
offset, data, 8)) {
case RCODE_GENERATION:
/* A generation change frees all channels. */
return allocate ? -EAGAIN : channel;
case RCODE_COMPLETE:
if (data[0] == old)
return channel;
old = data[0];
/* Is the IRM 1394a-2000 compliant? */
if ((data[0] & bit) == (data[1] & bit))
continue;
fallthrough; /* It's a 1394-1995 IRM, retry */
default:
if (retry) {
retry--;
channel--;
} else {
ret = -EIO;
}
}
}
return ret;
}
static void deallocate_channel(struct fw_card *card, int irm_id,
int generation, int channel)
{
u32 mask;
u64 offset;
mask = channel < 32 ? 1 << channel : 1 << (channel - 32);
offset = channel < 32 ? CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI :
CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO;
manage_channel(card, irm_id, generation, mask, offset, false);
}
/**
* fw_iso_resource_manage() - Allocate or deallocate a channel and/or bandwidth
* @card: card interface for this action
* @generation: bus generation
* @channels_mask: bitmask for channel allocation
* @channel: pointer for returning channel allocation result
* @bandwidth: pointer for returning bandwidth allocation result
* @allocate: whether to allocate (true) or deallocate (false)
*
* In parameters: card, generation, channels_mask, bandwidth, allocate
* Out parameters: channel, bandwidth
*
* This function blocks (sleeps) during communication with the IRM.
*
* Allocates or deallocates at most one channel out of channels_mask.
* channels_mask is a bitfield with MSB for channel 63 and LSB for channel 0.
* (Note, the IRM's CHANNELS_AVAILABLE is a big-endian bitfield with MSB for
* channel 0 and LSB for channel 63.)
* Allocates or deallocates as many bandwidth allocation units as specified.
*
* Returns channel < 0 if no channel was allocated or deallocated.
* Returns bandwidth = 0 if no bandwidth was allocated or deallocated.
*
* If generation is stale, deallocations succeed but allocations fail with
* channel = -EAGAIN.
*
* If channel allocation fails, no bandwidth will be allocated either.
* If bandwidth allocation fails, no channel will be allocated either.
* But deallocations of channel and bandwidth are tried independently
* of each other's success.
*/
void fw_iso_resource_manage(struct fw_card *card, int generation,
u64 channels_mask, int *channel, int *bandwidth,
bool allocate)
{
u32 channels_hi = channels_mask; /* channels 31...0 */
u32 channels_lo = channels_mask >> 32; /* channels 63...32 */
int irm_id, ret, c = -EINVAL;
scoped_guard(spinlock_irq, &card->lock)
irm_id = card->irm_node->node_id;
if (channels_hi)
c = manage_channel(card, irm_id, generation, channels_hi,
CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI,
allocate);
if (channels_lo && c < 0) {
c = manage_channel(card, irm_id, generation, channels_lo,
CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO,
allocate);
if (c >= 0)
c += 32;
}
*channel = c;
if (allocate && channels_mask != 0 && c < 0)
*bandwidth = 0;
if (*bandwidth == 0)
return;
ret = manage_bandwidth(card, irm_id, generation, *bandwidth, allocate);
if (ret < 0)
*bandwidth = 0;
if (allocate && ret < 0) {
if (c >= 0)
deallocate_channel(card, irm_id, generation, c);
*channel = ret;
}
}
EXPORT_SYMBOL(fw_iso_resource_manage);
@@ -0,0 +1,509 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Incremental bus scan, based on bus topology
*
* Copyright (C) 2004-2006 Kristian Hoegsberg <krh@bitplanet.net>
*/
#include <linux/bug.h>
#include <linux/errno.h>
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/atomic.h>
#include <asm/byteorder.h>
#include "core.h"
#include "phy-packet-definitions.h"
#include <trace/events/firewire.h>
static struct fw_node *fw_node_create(u32 sid, int port_count, int color)
{
struct fw_node *node;
node = kzalloc_flex(*node, ports, port_count, GFP_ATOMIC);
if (node == NULL)
return NULL;
node->color = color;
node->node_id = LOCAL_BUS | phy_packet_self_id_get_phy_id(sid);
node->link_on = phy_packet_self_id_zero_get_link_active(sid);
// NOTE: Only two bits, thus only for SCODE_100, SCODE_200, SCODE_400, and SCODE_BETA.
node->phy_speed = phy_packet_self_id_zero_get_scode(sid);
node->initiated_reset = phy_packet_self_id_zero_get_initiated_reset(sid);
node->port_count = port_count;
kref_init(&node->kref);
INIT_LIST_HEAD(&node->link);
return node;
}
/*
* Compute the maximum hop count for this node and it's children. The
* maximum hop count is the maximum number of connections between any
* two nodes in the subtree rooted at this node. We need this for
* setting the gap count. As we build the tree bottom up in
* build_tree() below, this is fairly easy to do: for each node we
* maintain the max hop count and the max depth, ie the number of hops
* to the furthest leaf. Computing the max hop count breaks down into
* two cases: either the path goes through this node, in which case
* the hop count is the sum of the two biggest child depths plus 2.
* Or it could be the case that the max hop path is entirely
* contained in a child tree, in which case the max hop count is just
* the max hop count of this child.
*/
static void update_hop_count(struct fw_node *node)
{
int depths[2] = { -1, -1 };
int max_child_hops = 0;
int i;
for (i = 0; i < node->port_count; i++) {
if (node->ports[i] == NULL)
continue;
if (node->ports[i]->max_hops > max_child_hops)
max_child_hops = node->ports[i]->max_hops;
if (node->ports[i]->max_depth > depths[0]) {
depths[1] = depths[0];
depths[0] = node->ports[i]->max_depth;
} else if (node->ports[i]->max_depth > depths[1])
depths[1] = node->ports[i]->max_depth;
}
node->max_depth = depths[0] + 1;
node->max_hops = max(max_child_hops, depths[0] + depths[1] + 2);
}
static inline struct fw_node *fw_node(struct list_head *l)
{
return list_entry(l, struct fw_node, link);
}
/*
* This function builds the tree representation of the topology given
* by the self IDs from the latest bus reset. During the construction
* of the tree, the function checks that the self IDs are valid and
* internally consistent. On success this function returns the
* fw_node corresponding to the local card otherwise NULL.
*/
static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self_id_count,
unsigned int generation)
{
struct self_id_sequence_enumerator enumerator = {
.cursor = sid,
.quadlet_count = self_id_count,
};
struct fw_node *node, *child, *local_node, *irm_node;
struct list_head stack;
int phy_id, stack_depth;
int gap_count;
bool beta_repeaters_present;
local_node = NULL;
node = NULL;
INIT_LIST_HEAD(&stack);
stack_depth = 0;
phy_id = 0;
irm_node = NULL;
gap_count = phy_packet_self_id_zero_get_gap_count(*sid);
beta_repeaters_present = false;
while (enumerator.quadlet_count > 0) {
unsigned int child_port_count = 0;
unsigned int total_port_count = 0;
unsigned int parent_count = 0;
unsigned int quadlet_count;
const u32 *self_id_sequence;
unsigned int port_capacity;
enum phy_packet_self_id_port_status port_status;
unsigned int port_index;
struct list_head *h;
int i;
self_id_sequence = self_id_sequence_enumerator_next(&enumerator, &quadlet_count);
if (IS_ERR(self_id_sequence)) {
if (PTR_ERR(self_id_sequence) != -ENODATA) {
fw_err(card, "inconsistent extended self IDs: %ld\n",
PTR_ERR(self_id_sequence));
return NULL;
}
break;
}
port_capacity = self_id_sequence_get_port_capacity(quadlet_count);
trace_self_id_sequence(card->index, self_id_sequence, quadlet_count, generation);
for (port_index = 0; port_index < port_capacity; ++port_index) {
port_status = self_id_sequence_get_port_status(self_id_sequence, quadlet_count,
port_index);
switch (port_status) {
case PHY_PACKET_SELF_ID_PORT_STATUS_CHILD:
++child_port_count;
fallthrough;
case PHY_PACKET_SELF_ID_PORT_STATUS_PARENT:
case PHY_PACKET_SELF_ID_PORT_STATUS_NCONN:
++total_port_count;
fallthrough;
case PHY_PACKET_SELF_ID_PORT_STATUS_NONE:
default:
break;
}
}
if (phy_id != phy_packet_self_id_get_phy_id(self_id_sequence[0])) {
fw_err(card, "PHY ID mismatch in self ID: %d != %d\n",
phy_id, phy_packet_self_id_get_phy_id(self_id_sequence[0]));
return NULL;
}
if (child_port_count > stack_depth) {
fw_err(card, "topology stack underflow\n");
return NULL;
}
/*
* Seek back from the top of our stack to find the
* start of the child nodes for this node.
*/
for (i = 0, h = &stack; i < child_port_count; i++)
h = h->prev;
/*
* When the stack is empty, this yields an invalid value,
* but that pointer will never be dereferenced.
*/
child = fw_node(h);
node = fw_node_create(self_id_sequence[0], total_port_count, card->color);
if (node == NULL) {
fw_err(card, "out of memory while building topology\n");
return NULL;
}
if (phy_id == (card->node_id & 0x3f))
local_node = node;
if (phy_packet_self_id_zero_get_contender(self_id_sequence[0]))
irm_node = node;
for (port_index = 0; port_index < total_port_count; ++port_index) {
port_status = self_id_sequence_get_port_status(self_id_sequence, quadlet_count,
port_index);
switch (port_status) {
case PHY_PACKET_SELF_ID_PORT_STATUS_PARENT:
// Who's your daddy? We dont know the parent node at this time, so
// we temporarily abuse node->color for remembering the entry in
// the node->ports array where the parent node should be. Later,
// when we handle the parent node, we fix up the reference.
++parent_count;
node->color = port_index;
break;
case PHY_PACKET_SELF_ID_PORT_STATUS_CHILD:
node->ports[port_index] = child;
// Fix up parent reference for this child node.
child->ports[child->color] = node;
child->color = card->color;
child = fw_node(child->link.next);
break;
case PHY_PACKET_SELF_ID_PORT_STATUS_NCONN:
case PHY_PACKET_SELF_ID_PORT_STATUS_NONE:
default:
break;
}
}
// Check that the node reports exactly one parent port, except for the root, which
// of course should have no parents.
if ((enumerator.quadlet_count == 0 && parent_count != 0) ||
(enumerator.quadlet_count > 0 && parent_count != 1)) {
fw_err(card, "parent port inconsistency for node %d: "
"parent_count=%d\n", phy_id, parent_count);
return NULL;
}
/* Pop the child nodes off the stack and push the new node. */
__list_del(h->prev, &stack);
list_add_tail(&node->link, &stack);
stack_depth += 1 - child_port_count;
if (node->phy_speed == SCODE_BETA && parent_count + child_port_count > 1)
beta_repeaters_present = true;
// If PHYs report different gap counts, set an invalid count which will force a gap
// count reconfiguration and a reset.
if (phy_packet_self_id_zero_get_gap_count(self_id_sequence[0]) != gap_count)
gap_count = GAP_COUNT_MISMATCHED;
update_hop_count(node);
phy_id++;
}
card->root_node = node;
card->irm_node = irm_node;
card->gap_count = gap_count;
card->beta_repeaters_present = beta_repeaters_present;
return local_node;
}
typedef void (*fw_node_callback_t)(struct fw_card * card,
struct fw_node * node,
struct fw_node * parent);
static void for_each_fw_node(struct fw_card *card, struct fw_node *root,
fw_node_callback_t callback)
{
struct list_head list;
struct fw_node *node, *next, *child, *parent;
int i;
INIT_LIST_HEAD(&list);
fw_node_get(root);
list_add_tail(&root->link, &list);
parent = NULL;
for (node = list_first_entry(&list, typeof(*node), link);
!list_entry_is_head(node, &list, link);
node = list_next_entry(node, link)) {
node->color = card->color;
for (i = 0; i < node->port_count; i++) {
child = node->ports[i];
if (!child)
continue;
if (child->color == card->color)
parent = child;
else {
fw_node_get(child);
list_add_tail(&child->link, &list);
}
}
callback(card, node, parent);
}
list_for_each_entry_safe(node, next, &list, link)
fw_node_put(node);
}
static void report_lost_node(struct fw_card *card,
struct fw_node *node, struct fw_node *parent)
{
fw_node_event(card, node, FW_NODE_DESTROYED);
fw_node_put(node);
/* Topology has changed - reset bus manager retry counter */
card->bm_retries = 0;
}
static void report_found_node(struct fw_card *card,
struct fw_node *node, struct fw_node *parent)
{
int b_path = (node->phy_speed == SCODE_BETA);
if (parent != NULL) {
/* min() macro doesn't work here with gcc 3.4 */
node->max_speed = parent->max_speed < node->phy_speed ?
parent->max_speed : node->phy_speed;
node->b_path = parent->b_path && b_path;
} else {
node->max_speed = node->phy_speed;
node->b_path = b_path;
}
fw_node_event(card, node, FW_NODE_CREATED);
/* Topology has changed - reset bus manager retry counter */
card->bm_retries = 0;
}
void fw_destroy_nodes(struct fw_card *card)
__must_hold(&card->lock)
{
lockdep_assert_held(&card->lock);
card->color++;
if (card->local_node != NULL)
for_each_fw_node(card, card->local_node, report_lost_node);
card->local_node = NULL;
}
static void move_tree(struct fw_node *node0, struct fw_node *node1, int port)
{
struct fw_node *tree;
int i;
tree = node1->ports[port];
node0->ports[port] = tree;
for (i = 0; i < tree->port_count; i++) {
if (tree->ports[i] == node1) {
tree->ports[i] = node0;
break;
}
}
}
/*
* Compare the old topology tree for card with the new one specified by root.
* Queue the nodes and mark them as either found, lost or updated.
* Update the nodes in the card topology tree as we go.
*/
static void update_tree(struct fw_card *card, struct fw_node *root)
{
struct list_head list0, list1;
struct fw_node *node0, *node1, *next1;
int i, event;
INIT_LIST_HEAD(&list0);
list_add_tail(&card->local_node->link, &list0);
INIT_LIST_HEAD(&list1);
list_add_tail(&root->link, &list1);
node0 = fw_node(list0.next);
node1 = fw_node(list1.next);
while (&node0->link != &list0) {
WARN_ON(node0->port_count != node1->port_count);
if (node0->link_on && !node1->link_on)
event = FW_NODE_LINK_OFF;
else if (!node0->link_on && node1->link_on)
event = FW_NODE_LINK_ON;
else if (node1->initiated_reset && node1->link_on)
event = FW_NODE_INITIATED_RESET;
else
event = FW_NODE_UPDATED;
node0->node_id = node1->node_id;
node0->color = card->color;
node0->link_on = node1->link_on;
node0->initiated_reset = node1->initiated_reset;
node0->max_hops = node1->max_hops;
node1->color = card->color;
fw_node_event(card, node0, event);
if (card->root_node == node1)
card->root_node = node0;
if (card->irm_node == node1)
card->irm_node = node0;
for (i = 0; i < node0->port_count; i++) {
if (node0->ports[i] && node1->ports[i]) {
/*
* This port didn't change, queue the
* connected node for further
* investigation.
*/
if (node0->ports[i]->color == card->color)
continue;
list_add_tail(&node0->ports[i]->link, &list0);
list_add_tail(&node1->ports[i]->link, &list1);
} else if (node0->ports[i]) {
/*
* The nodes connected here were
* unplugged; unref the lost nodes and
* queue FW_NODE_LOST callbacks for
* them.
*/
for_each_fw_node(card, node0->ports[i],
report_lost_node);
node0->ports[i] = NULL;
} else if (node1->ports[i]) {
/*
* One or more node were connected to
* this port. Move the new nodes into
* the tree and queue FW_NODE_CREATED
* callbacks for them.
*/
move_tree(node0, node1, i);
for_each_fw_node(card, node0->ports[i],
report_found_node);
}
}
node0 = fw_node(node0->link.next);
next1 = fw_node(node1->link.next);
fw_node_put(node1);
node1 = next1;
}
}
static void update_topology_map(__be32 *buffer, size_t buffer_size, int root_node_id,
const u32 *self_ids, int self_id_count)
{
__be32 *map = buffer;
u32 next_generation = be32_to_cpu(buffer[1]) + 1;
int node_count = (root_node_id & 0x3f) + 1;
memset(map, 0, buffer_size);
*map++ = cpu_to_be32((self_id_count + 2) << 16);
*map++ = cpu_to_be32(next_generation);
*map++ = cpu_to_be32((node_count << 16) | self_id_count);
while (self_id_count--)
*map++ = cpu_to_be32p(self_ids++);
fw_compute_block_crc(buffer);
}
void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation,
int self_id_count, u32 *self_ids, bool bm_abdicate)
{
struct fw_node *local_node;
trace_bus_reset_handle(card->index, generation, node_id, bm_abdicate, self_ids, self_id_count);
scoped_guard(spinlock, &card->lock) {
// If the selfID buffer is not the immediate successor of the
// previously processed one, we cannot reliably compare the
// old and new topologies.
if (!is_next_generation(generation, card->generation) && card->local_node != NULL) {
fw_destroy_nodes(card);
card->bm_retries = 0;
}
card->broadcast_channel_allocated = card->broadcast_channel_auto_allocated;
card->node_id = node_id;
// Update node_id before generation to prevent anybody from using
// a stale node_id together with a current generation.
smp_wmb();
card->generation = generation;
card->reset_jiffies = get_jiffies_64();
card->bm_node_id = 0xffff;
card->bm_abdicate = bm_abdicate;
local_node = build_tree(card, self_ids, self_id_count, generation);
card->color++;
if (local_node == NULL) {
fw_err(card, "topology build failed\n");
// FIXME: We need to issue a bus reset in this case.
} else if (card->local_node == NULL) {
card->local_node = local_node;
for_each_fw_node(card, local_node, report_found_node);
} else {
update_tree(card, local_node);
}
}
fw_schedule_bm_work(card, 0);
// Just used by transaction layer.
scoped_guard(spinlock, &card->topology_map.lock) {
update_topology_map(card->topology_map.buffer, sizeof(card->topology_map.buffer),
card->root_node->node_id, self_ids, self_id_count);
}
}
EXPORT_SYMBOL(fw_core_handle_bus_reset);
+16
View File
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2024 Takashi Sakamoto
#include <linux/types.h>
#include <linux/err.h>
#include "packet-header-definitions.h"
#include "phy-packet-definitions.h"
#define CREATE_TRACE_POINTS
#include <trace/events/firewire.h>
#ifdef TRACEPOINTS_ENABLED
EXPORT_TRACEPOINT_SYMBOL_GPL(isoc_inbound_single_completions);
EXPORT_TRACEPOINT_SYMBOL_GPL(isoc_inbound_multiple_completions);
EXPORT_TRACEPOINT_SYMBOL_GPL(isoc_outbound_completions);
#endif
File diff suppressed because it is too large Load Diff
+325
View File
@@ -0,0 +1,325 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _FIREWIRE_CORE_H
#define _FIREWIRE_CORE_H
#include <linux/compiler.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/xarray.h>
#include <linux/mm_types.h>
#include <linux/rwsem.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/refcount.h>
struct device;
struct fw_card;
struct fw_device;
struct fw_iso_buffer;
struct fw_iso_context;
struct fw_iso_packet;
struct fw_node;
struct fw_packet;
/* -card */
// This is the arbitrary value we use to indicate a mismatched gap count.
#define GAP_COUNT_MISMATCHED 0
#define isoc_cycles_to_jiffies(cycles) usecs_to_jiffies((u32)div_u64((u64)cycles * USEC_PER_SEC, 8000))
extern __printf(2, 3)
void fw_err(const struct fw_card *card, const char *fmt, ...);
extern __printf(2, 3)
void fw_notice(const struct fw_card *card, const char *fmt, ...);
/* bitfields within the PHY registers */
#define PHY_LINK_ACTIVE 0x80
#define PHY_CONTENDER 0x40
#define PHY_BUS_RESET 0x40
#define PHY_EXTENDED_REGISTERS 0xe0
#define PHY_BUS_SHORT_RESET 0x40
#define PHY_INT_STATUS_BITS 0x3c
#define PHY_ENABLE_ACCEL 0x02
#define PHY_ENABLE_MULTI 0x01
#define PHY_PAGE_SELECT 0xe0
#define BANDWIDTH_AVAILABLE_INITIAL 4915
#define BROADCAST_CHANNEL_INITIAL (1 << 31 | 31)
#define BROADCAST_CHANNEL_VALID (1 << 30)
#define CSR_STATE_BIT_CMSTR (1 << 8)
#define CSR_STATE_BIT_ABDICATE (1 << 10)
struct fw_card_driver {
/*
* Enable the given card with the given initial config rom.
* This function is expected to activate the card, and either
* enable the PHY or set the link_on bit and initiate a bus
* reset.
*/
int (*enable)(struct fw_card *card,
const __be32 *config_rom, size_t length);
// After returning the call, any function is no longer triggered to handle hardware event.
void (*disable)(struct fw_card *card);
int (*read_phy_reg)(struct fw_card *card, int address);
int (*update_phy_reg)(struct fw_card *card, int address,
int clear_bits, int set_bits);
/*
* Update the config rom for an enabled card. This function
* should change the config rom that is presented on the bus
* and initiate a bus reset.
*/
int (*set_config_rom)(struct fw_card *card,
const __be32 *config_rom, size_t length);
void (*send_request)(struct fw_card *card, struct fw_packet *packet);
void (*send_response)(struct fw_card *card, struct fw_packet *packet);
/* Calling cancel is valid once a packet has been submitted. */
int (*cancel_packet)(struct fw_card *card, struct fw_packet *packet);
/*
* Allow the specified node ID to do direct DMA out and in of
* host memory. The card will disable this for all node when
* a bus reset happens, so driver need to re-enable this after
* bus reset. Returns 0 on success, -ENODEV if the card
* doesn't support this, -ESTALE if the generation doesn't
* match.
*/
int (*enable_phys_dma)(struct fw_card *card,
int node_id, int generation);
u32 (*read_csr)(struct fw_card *card, int csr_offset);
void (*write_csr)(struct fw_card *card, int csr_offset, u32 value);
struct fw_iso_context *
(*allocate_iso_context)(struct fw_card *card, int type, int channel, size_t header_size,
size_t header_storage_size);
void (*free_iso_context)(struct fw_iso_context *ctx);
int (*start_iso)(struct fw_iso_context *ctx,
s32 cycle, u32 sync, u32 tags);
int (*set_iso_channels)(struct fw_iso_context *ctx, u64 *channels);
int (*queue_iso)(struct fw_iso_context *ctx,
struct fw_iso_packet *packet,
struct fw_iso_buffer *buffer,
unsigned long payload);
void (*flush_queue_iso)(struct fw_iso_context *ctx);
int (*flush_iso_completions)(struct fw_iso_context *ctx);
int (*stop_iso)(struct fw_iso_context *ctx);
};
void fw_card_initialize(struct fw_card *card,
const struct fw_card_driver *driver, struct device *device);
int fw_card_add(struct fw_card *card, u32 max_receive, u32 link_speed, u64 guid,
unsigned int supported_isoc_contexts);
void fw_core_remove_card(struct fw_card *card);
int fw_compute_block_crc(__be32 *block);
void fw_schedule_bm_work(struct fw_card *card, unsigned long delay);
/* -cdev */
extern const struct file_operations fw_device_ops;
void fw_device_cdev_update(struct fw_device *device);
void fw_device_cdev_remove(struct fw_device *device);
void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p);
/* -device */
extern struct rw_semaphore fw_device_rwsem;
extern struct xarray fw_device_xa;
extern int fw_cdev_major;
static inline struct fw_device *fw_device_get(struct fw_device *device)
{
get_device(&device->device);
return device;
}
static inline void fw_device_put(struct fw_device *device)
{
put_device(&device->device);
}
struct fw_device *fw_device_get_by_devt(dev_t devt);
int fw_device_set_broadcast_channel(struct device *dev, void *gen);
void fw_node_event(struct fw_card *card, struct fw_node *node, int event);
/* -iso */
int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count);
int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card,
enum dma_data_direction direction);
size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed);
static inline void fw_iso_context_init_work(struct fw_iso_context *ctx, work_func_t func)
{
INIT_WORK(&ctx->work, func);
}
static inline struct fw_iso_context *fw_iso_mc_context_create(struct fw_card *card,
fw_iso_mc_callback_t callback, void *callback_data)
{
union fw_iso_callback cb = { .mc = callback };
return __fw_iso_context_create(card, FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL, 0, 0, 0, 0, cb,
callback_data);
}
/* -topology */
// The initial value of BUS_MANAGER_ID register, to express nothing registered.
#define BUS_MANAGER_ID_NOT_REGISTERED 0x3f
enum {
FW_NODE_CREATED,
FW_NODE_UPDATED,
FW_NODE_DESTROYED,
FW_NODE_LINK_ON,
FW_NODE_LINK_OFF,
FW_NODE_INITIATED_RESET,
};
struct fw_node {
u16 node_id;
u8 color;
u8 port_count;
u8 link_on:1;
u8 initiated_reset:1;
u8 b_path:1;
u8 phy_speed:2; /* As in the self ID packet. */
u8 max_speed:2; /* Minimum of all phy-speeds on the path from the
* local node to this node. */
u8 max_depth:4; /* Maximum depth to any leaf node */
u8 max_hops:4; /* Max hops in this sub tree */
struct kref kref;
/* For serializing node topology into a list. */
struct list_head link;
// The device when already associated, else NULL.
struct fw_device *device;
struct fw_node *ports[] __counted_by(port_count);
};
static inline struct fw_node *fw_node_get(struct fw_node *node)
{
kref_get(&node->kref);
return node;
}
static void release_node(struct kref *kref)
{
struct fw_node *node = container_of(kref, struct fw_node, kref);
kfree(node);
}
static inline void fw_node_put(struct fw_node *node)
{
kref_put(&node->kref, release_node);
}
static inline struct fw_device *fw_node_get_device(struct fw_node *node)
{
return node->device;
}
static inline void fw_node_set_device(struct fw_node *node, struct fw_device *device)
{
node->device = device;
}
void fw_core_handle_bus_reset(struct fw_card *card, int node_id,
int generation, int self_id_count, u32 *self_ids, bool bm_abdicate);
void fw_destroy_nodes(struct fw_card *card);
/*
* Check whether new_generation is the immediate successor of old_generation.
* Take counter roll-over at 255 (as per OHCI) into account.
*/
static inline bool is_next_generation(int new_generation, int old_generation)
{
return (new_generation & 0xff) == ((old_generation + 1) & 0xff);
}
/* -transaction */
#define TCODE_LINK_INTERNAL 0xe
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
/* OHCI-1394's default upper bound for physical DMA: 4 GB */
#define FW_MAX_PHYSICAL_RANGE (1ULL << 32)
void fw_core_handle_request(struct fw_card *card, struct fw_packet *request);
void fw_core_handle_response(struct fw_card *card, struct fw_packet *packet);
int fw_get_response_length(struct fw_request *request);
void fw_fill_response(struct fw_packet *response, u32 *request_header,
int rcode, void *payload, size_t length);
void fw_request_get(struct fw_request *request);
void fw_request_put(struct fw_request *request);
void fw_cancel_pending_transactions(struct fw_card *card);
// 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,
int node_id, int generation, int gap_count);
static inline bool is_ping_packet(u32 *data)
{
return (data[0] & 0xc0ffffff) == 0 && ~data[0] == data[1];
}
static inline bool is_in_fcp_region(u64 offset, size_t length)
{
return offset >= (CSR_REGISTER_BASE | CSR_FCP_COMMAND) &&
offset + length <= (CSR_REGISTER_BASE | CSR_FCP_END);
}
#endif /* _FIREWIRE_CORE_H */
@@ -0,0 +1,253 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// device-attribute-test.c - An application of Kunit to test implementation for device attributes.
//
// Copyright (c) 2023 Takashi Sakamoto
//
// This file can not be built independently since it is intentionally included in core-device.c.
#include <kunit/test.h>
// Configuration ROM for AV/C Devices 1.0 (Dec. 12, 2000, 1394 Trading Association)
// Annex C:Configuration ROM example(informative)
// C.1 Simple AV/C device
//
// Copied from the documentation.
static const u32 simple_avc_config_rom[] = {
0x0404eabf,
0x31333934,
0xe0646102,
0xffffffff,
0xffffffff,
0x00063287, // root directory.
0x03ffffff,
0x8100000a,
0x17ffffff,
0x8100000e,
0x0c0083c0,
0xd1000001,
0x0004442d, // unit 0 directory.
0x1200a02d,
0x13010001,
0x17ffffff,
0x81000007,
0x0005c915, // leaf for textual descriptor.
0x00000000,
0x00000000,
0x56656e64,
0x6f72204e,
0x616d6500,
0x00057f16, // leaf for textual descriptor.
0x00000000,
0x00000000,
0x4d6f6465,
0x6c204e61,
0x6d650000,
};
// Ibid.
// Annex A:Consideration for configuration ROM reader design (informative)
// A.1 Vendor directory
//
// Written by hand.
static const u32 legacy_avc_config_rom[] = {
0x04199fe7,
0x31333934,
0xe0644000,
0x00112233,
0x44556677,
0x0005dace, // root directory.
0x03012345,
0x0c0083c0,
0x8d000009,
0xd1000002,
0xc3000004,
0x0002e107, // unit 0 directory.
0x12abcdef,
0x13543210,
0x0002cb73, // vendor directory.
0x17fedcba,
0x81000004,
0x00026dc1, // leaf for EUI-64.
0x00112233,
0x44556677,
0x00050e84, // leaf for textual descriptor.
0x00000000,
0x00000000,
0x41424344,
0x45464748,
0x494a0000,
};
static void device_attr_simple_avc(struct kunit *test)
{
static const struct fw_device node = {
.device = {
.type = &fw_device_type,
},
.config_rom = simple_avc_config_rom,
.config_rom_length = sizeof(simple_avc_config_rom),
};
static const struct fw_unit unit0 = {
.device = {
.type = &fw_unit_type,
.parent = (struct device *)&node.device,
},
.directory = &simple_avc_config_rom[12],
};
struct device *node_dev = (struct device *)&node.device;
struct device *unit0_dev = (struct device *)&unit0.device;
static const int unit0_expected_ids[] = {0x00ffffff, 0x00ffffff, 0x0000a02d, 0x00010001};
char *buf = kunit_kzalloc(test, PAGE_SIZE, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf);
int ids[4] = {0, 0, 0, 0};
// Ensure associations for node and unit devices.
KUNIT_ASSERT_TRUE(test, is_fw_device(node_dev));
KUNIT_ASSERT_FALSE(test, is_fw_unit(node_dev));
KUNIT_ASSERT_PTR_EQ(test, fw_device(node_dev), &node);
KUNIT_ASSERT_FALSE(test, is_fw_device(unit0_dev));
KUNIT_ASSERT_TRUE(test, is_fw_unit(unit0_dev));
KUNIT_ASSERT_PTR_EQ(test, fw_parent_device((&unit0)), &node);
KUNIT_ASSERT_PTR_EQ(test, fw_unit(unit0_dev), &unit0);
// For entries in root directory.
// Vendor immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(node_dev, &config_rom_attributes[0].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0xffffff\n");
// Model immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(node_dev, &config_rom_attributes[4].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0xffffff\n");
// Descriptor leaf entry for vendor is found.
KUNIT_EXPECT_GT(test, show_text_leaf(node_dev, &config_rom_attributes[5].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "Vendor Name\n");
// Descriptor leaf entry for model is found.
KUNIT_EXPECT_GT(test, show_text_leaf(node_dev, &config_rom_attributes[6].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "Model Name\n");
// For entries in unit 0 directory.
// Vendor immediate entry is not found.
KUNIT_EXPECT_LT(test, show_immediate(unit0_dev, &config_rom_attributes[0].attr, buf), 0);
// Model immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(unit0_dev, &config_rom_attributes[4].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0xffffff\n");
// Descriptor leaf entry for vendor is not found.
KUNIT_EXPECT_LT(test, show_text_leaf(unit0_dev, &config_rom_attributes[5].attr, buf), 0);
// Descriptor leaf entry for model is found.
KUNIT_EXPECT_GT(test, show_text_leaf(unit0_dev, &config_rom_attributes[6].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "Model Name\n");
// Specifier_ID immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(unit0_dev, &config_rom_attributes[2].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0x00a02d\n");
// Version immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(unit0_dev, &config_rom_attributes[3].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0x010001\n");
kunit_kfree(test, buf);
get_modalias_ids(&unit0, ids);
KUNIT_EXPECT_MEMEQ(test, ids, unit0_expected_ids, sizeof(ids));
}
static void device_attr_legacy_avc(struct kunit *test)
{
static const struct fw_device node = {
.device = {
.type = &fw_device_type,
},
.config_rom = legacy_avc_config_rom,
.config_rom_length = sizeof(legacy_avc_config_rom),
};
static const struct fw_unit unit0 = {
.device = {
.type = &fw_unit_type,
.parent = (struct device *)&node.device,
},
.directory = &legacy_avc_config_rom[11],
};
struct device *node_dev = (struct device *)&node.device;
struct device *unit0_dev = (struct device *)&unit0.device;
static const int unit0_expected_ids[] = {0x00012345, 0x00fedcba, 0x00abcdef, 0x00543210};
char *buf = kunit_kzalloc(test, PAGE_SIZE, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf);
int ids[4] = {0, 0, 0, 0};
// Ensure associations for node and unit devices.
KUNIT_ASSERT_TRUE(test, is_fw_device(node_dev));
KUNIT_ASSERT_FALSE(test, is_fw_unit(node_dev));
KUNIT_ASSERT_PTR_EQ(test, fw_device((node_dev)), &node);
KUNIT_ASSERT_FALSE(test, is_fw_device(unit0_dev));
KUNIT_ASSERT_TRUE(test, is_fw_unit(unit0_dev));
KUNIT_ASSERT_PTR_EQ(test, fw_parent_device((&unit0)), &node);
KUNIT_ASSERT_PTR_EQ(test, fw_unit(unit0_dev), &unit0);
// For entries in root directory.
// Vendor immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(node_dev, &config_rom_attributes[0].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0x012345\n");
// Model immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(node_dev, &config_rom_attributes[4].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0xfedcba\n");
// Descriptor leaf entry for vendor is not found.
KUNIT_EXPECT_LT(test, show_text_leaf(node_dev, &config_rom_attributes[5].attr, buf), 0);
// Descriptor leaf entry for model is found.
KUNIT_EXPECT_GT(test, show_text_leaf(node_dev, &config_rom_attributes[6].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "ABCDEFGHIJ\n");
// For entries in unit 0 directory.
// Vendor immediate entry is not found.
KUNIT_EXPECT_LT(test, show_immediate(unit0_dev, &config_rom_attributes[0].attr, buf), 0);
// Model immediate entry is not found.
KUNIT_EXPECT_LT(test, show_immediate(unit0_dev, &config_rom_attributes[4].attr, buf), 0);
// Descriptor leaf entry for vendor is not found.
KUNIT_EXPECT_LT(test, show_text_leaf(unit0_dev, &config_rom_attributes[5].attr, buf), 0);
// Descriptor leaf entry for model is not found.
KUNIT_EXPECT_LT(test, show_text_leaf(unit0_dev, &config_rom_attributes[6].attr, buf), 0);
// Specifier_ID immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(unit0_dev, &config_rom_attributes[2].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0xabcdef\n");
// Version immediate entry is found.
KUNIT_EXPECT_GT(test, show_immediate(unit0_dev, &config_rom_attributes[3].attr, buf), 0);
KUNIT_EXPECT_STREQ(test, buf, "0x543210\n");
kunit_kfree(test, buf);
get_modalias_ids(&unit0, ids);
KUNIT_EXPECT_MEMEQ(test, ids, unit0_expected_ids, sizeof(ids));
}
static struct kunit_case device_attr_test_cases[] = {
KUNIT_CASE(device_attr_simple_avc),
KUNIT_CASE(device_attr_legacy_avc),
{}
};
static struct kunit_suite device_attr_test_suite = {
.name = "firewire-device-attribute",
.test_cases = device_attr_test_cases,
};
kunit_test_suite(device_attr_test_suite);
@@ -0,0 +1,306 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* init_ohci1394_dma.c - Initializes physical DMA on all OHCI 1394 controllers
*
* Copyright (C) 2006-2007 Bernhard Kaindl <bk@suse.de>
*
* Derived from drivers/ieee1394/ohci1394.c and arch/x86/kernel/early-quirks.c
* this file has functions to:
* - scan the PCI very early on boot for all OHCI 1394-compliant controllers
* - reset and initialize them and make them join the IEEE1394 bus and
* - enable physical DMA on them to allow remote debugging
*
* All code and data is marked as __init and __initdata, respective as
* during boot, all OHCI1394 controllers may be claimed by the firewire
* stack and at this point, this code should not touch them anymore.
*
* To use physical DMA after the initialization of the firewire stack,
* be sure that the stack enables it and (re-)attach after the bus reset
* which may be caused by the firewire stack initialization.
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/pci.h> /* for PCI defines */
#include <linux/string.h>
#include <asm/pci-direct.h> /* for direct PCI config space access */
#include <asm/fixmap.h>
#include <linux/init_ohci1394_dma.h>
#include "ohci.h"
int __initdata init_ohci1394_dma_early;
struct ohci {
void __iomem *registers;
};
static inline void reg_write(const struct ohci *ohci, int offset, u32 data)
{
writel(data, ohci->registers + offset);
}
static inline u32 reg_read(const struct ohci *ohci, int offset)
{
return readl(ohci->registers + offset);
}
#define OHCI_LOOP_COUNT 100 /* Number of loops for reg read waits */
/* Reads a PHY register of an OHCI-1394 controller */
static inline u8 __init get_phy_reg(struct ohci *ohci, u8 addr)
{
int i;
u32 r;
reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | 0x00008000);
for (i = 0; i < OHCI_LOOP_COUNT; i++) {
if (reg_read(ohci, OHCI1394_PhyControl) & 0x80000000)
break;
mdelay(1);
}
r = reg_read(ohci, OHCI1394_PhyControl);
return (r & 0x00ff0000) >> 16;
}
/* Writes to a PHY register of an OHCI-1394 controller */
static inline void __init set_phy_reg(struct ohci *ohci, u8 addr, u8 data)
{
int i;
reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | data | 0x00004000);
for (i = 0; i < OHCI_LOOP_COUNT; i++) {
if (!(reg_read(ohci, OHCI1394_PhyControl) & 0x00004000))
break;
mdelay(1);
}
}
/* Resets an OHCI-1394 controller (for sane state before initialization) */
static inline void __init init_ohci1394_soft_reset(struct ohci *ohci)
{
int i;
reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset);
for (i = 0; i < OHCI_LOOP_COUNT; i++) {
if (!(reg_read(ohci, OHCI1394_HCControlSet)
& OHCI1394_HCControl_softReset))
break;
mdelay(1);
}
}
#define OHCI1394_MAX_AT_REQ_RETRIES 0xf
#define OHCI1394_MAX_AT_RESP_RETRIES 0x2
#define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8
/* Basic OHCI-1394 register and port inititalization */
static inline void __init init_ohci1394_initialize(struct ohci *ohci)
{
u32 bus_options;
int num_ports, i;
/* Put some defaults to these undefined bus options */
bus_options = reg_read(ohci, OHCI1394_BusOptions);
bus_options |= 0x60000000; /* Enable CMC and ISC */
bus_options &= ~0x00ff0000; /* XXX: Set cyc_clk_acc to zero for now */
bus_options &= ~0x18000000; /* Disable PMC and BMC */
reg_write(ohci, OHCI1394_BusOptions, bus_options);
/* Set the bus number */
reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0);
/* Enable posted writes */
reg_write(ohci, OHCI1394_HCControlSet,
OHCI1394_HCControl_postedWriteEnable);
/* Clear link control register */
reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff);
/* enable phys */
reg_write(ohci, OHCI1394_LinkControlSet,
OHCI1394_LinkControl_rcvPhyPkt);
/* Don't accept phy packets into AR request context */
reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400);
/* Clear the Isochonouys interrupt masks */
reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff);
reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff);
reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff);
reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff);
/* Accept asynchronous transfer requests from all nodes for now */
reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000);
/* Specify asynchronous transfer retries */
reg_write(ohci, OHCI1394_ATRetries,
OHCI1394_MAX_AT_REQ_RETRIES |
(OHCI1394_MAX_AT_RESP_RETRIES<<4) |
(OHCI1394_MAX_PHYS_RESP_RETRIES<<8));
/* We don't want hardware swapping */
reg_write(ohci, OHCI1394_HCControlClear,
OHCI1394_HCControl_noByteSwapData);
/* Enable link */
reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable);
/* If anything is connected to a port, make sure it is enabled */
num_ports = get_phy_reg(ohci, 2) & 0xf;
for (i = 0; i < num_ports; i++) {
unsigned int status;
set_phy_reg(ohci, 7, i);
status = get_phy_reg(ohci, 8);
if (status & 0x20)
set_phy_reg(ohci, 8, status & ~1);
}
}
/**
* init_ohci1394_wait_for_busresets - wait until bus resets are completed
* @ohci: Pointer to the OHCI-1394 controller structure
*
* OHCI1394 initialization itself and any device going on- or offline
* and any cable issue cause a IEEE1394 bus reset. The OHCI1394 spec
* specifies that physical DMA is disabled on each bus reset and it
* has to be enabled after each bus reset when needed. We resort
* to polling here because on early boot, we have no interrupts.
*/
static inline void __init init_ohci1394_wait_for_busresets(struct ohci *ohci)
{
int i, events;
for (i = 0; i < 9; i++) {
mdelay(200);
events = reg_read(ohci, OHCI1394_IntEventSet);
if (events & OHCI1394_busReset)
reg_write(ohci, OHCI1394_IntEventClear,
OHCI1394_busReset);
}
}
/**
* init_ohci1394_enable_physical_dma - Enable physical DMA for remote debugging
* @ohci: Pointer to the OHCI-1394 controller structure
*
* This enables remote DMA access over IEEE1394 from every host for the low
* 4GB of address space. DMA accesses above 4GB are not available currently.
*/
static inline void __init init_ohci1394_enable_physical_dma(struct ohci *ohci)
{
reg_write(ohci, OHCI1394_PhyReqFilterHiSet, 0xffffffff);
reg_write(ohci, OHCI1394_PhyReqFilterLoSet, 0xffffffff);
reg_write(ohci, OHCI1394_PhyUpperBound, 0xffff0000);
}
/**
* init_ohci1394_reset_and_init_dma - init controller and enable DMA
* @ohci: Pointer to the OHCI-1394 controller structure
*
* This initializes the given controller and enables physical DMA engine in it.
*/
static inline void __init init_ohci1394_reset_and_init_dma(struct ohci *ohci)
{
/* Start off with a soft reset, clears everything to a sane state. */
init_ohci1394_soft_reset(ohci);
/* Accessing some registers without LPS enabled may cause lock up */
reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_LPS);
/* Disable and clear interrupts */
reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff);
mdelay(50); /* Wait 50msec to make sure we have full link enabled */
init_ohci1394_initialize(ohci);
/*
* The initialization causes at least one IEEE1394 bus reset. Enabling
* physical DMA only works *after* *all* bus resets have calmed down:
*/
init_ohci1394_wait_for_busresets(ohci);
/* We had to wait and do this now if we want to debug early problems */
init_ohci1394_enable_physical_dma(ohci);
}
/**
* init_ohci1394_controller - Map the registers of the controller and init DMA
* @num: PCI bus number
* @slot: PCI device number
* @func: PCI function number
*
* This maps the registers of the specified controller and initializes it
*/
static inline void __init init_ohci1394_controller(int num, int slot, int func)
{
unsigned long ohci_base;
struct ohci ohci;
printk(KERN_INFO "init_ohci1394_dma: initializing OHCI-1394"
" at %02x:%02x.%x\n", num, slot, func);
ohci_base = read_pci_config(num, slot, func, PCI_BASE_ADDRESS_0+(0<<2))
& PCI_BASE_ADDRESS_MEM_MASK;
set_fixmap_nocache(FIX_OHCI1394_BASE, ohci_base);
ohci.registers = (void __iomem *)fix_to_virt(FIX_OHCI1394_BASE);
init_ohci1394_reset_and_init_dma(&ohci);
}
/**
* init_ohci1394_dma_on_all_controllers - scan for OHCI1394 controllers and init DMA on them
* Scans the whole PCI space for OHCI1394 controllers and inits DMA on them
*/
void __init init_ohci1394_dma_on_all_controllers(void)
{
int num, slot, func;
u32 class;
if (!early_pci_allowed())
return;
/* Poor man's PCI discovery, the only thing we can do at early boot */
for (num = 0; num < 32; num++) {
for (slot = 0; slot < 32; slot++) {
for (func = 0; func < 8; func++) {
class = read_pci_config(num, slot, func,
PCI_CLASS_REVISION);
if (class == 0xffffffff)
continue; /* No device at this func */
if (class>>8 != PCI_CLASS_SERIAL_FIREWIRE_OHCI)
continue; /* Not an OHCI-1394 device */
init_ohci1394_controller(num, slot, func);
break; /* Assume one controller per device */
}
}
}
printk(KERN_INFO "init_ohci1394_dma: finished initializing OHCI DMA\n");
}
/**
* setup_ohci1394_dma - enables early OHCI1394 DMA initialization
* @opt: Kernel command line parameter string
*/
static int __init setup_ohci1394_dma(char *opt)
{
if (!strcmp(opt, "early"))
init_ohci1394_dma_early = 1;
return 0;
}
/* passing ohci1394_dma=early on boot causes early OHCI1394 DMA initialization */
early_param("ohci1394_dma", setup_ohci1394_dma);
File diff suppressed because it is too large Load Diff
+26
View File
@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __nosy_user_h
#define __nosy_user_h
#include <linux/ioctl.h>
#include <linux/types.h>
#define NOSY_IOC_GET_STATS _IOR('&', 0, struct nosy_stats)
#define NOSY_IOC_START _IO('&', 1)
#define NOSY_IOC_STOP _IO('&', 2)
#define NOSY_IOC_FILTER _IOW('&', 2, __u32)
struct nosy_stats {
__u32 total_packet_count;
__u32 lost_packet_count;
};
/*
* Format of packets returned from the kernel driver:
*
* quadlet with timestamp (microseconds, CPU endian)
* quadlet-padded packet data... (little endian)
* quadlet with ack (little endian)
*/
#endif /* __nosy_user_h */
+719
View File
@@ -0,0 +1,719 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* nosy - Snoop mode driver for TI PCILynx 1394 controllers
* Copyright (C) 2002-2007 Kristian Høgsberg
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/poll.h>
#include <linux/sched.h> /* required for linux/wait.h */
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/time64.h>
#include <linux/timex.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/atomic.h>
#include <asm/byteorder.h>
#include "nosy.h"
#include "nosy-user.h"
#define TCODE_PHY_PACKET 0x10
#define PCI_DEVICE_ID_TI_PCILYNX 0x8000
static char driver_name[] = KBUILD_MODNAME;
#define RCV_BUFFER_SIZE (16 * 1024)
/* this is the physical layout of a PCL, its size is 128 bytes */
struct pcl {
__le32 next;
__le32 async_error_next;
u32 user_data;
__le32 pcl_status;
__le32 remaining_transfer_count;
__le32 next_data_buffer;
struct {
__le32 control;
__le32 pointer;
} buffer[13];
};
struct packet {
unsigned int length;
char data[];
};
struct packet_buffer {
char *data;
size_t capacity;
long total_packet_count, lost_packet_count;
atomic_t size;
struct packet *head, *tail;
wait_queue_head_t wait;
};
struct pcilynx {
struct pci_dev *pci_device;
__iomem char *registers;
struct pcl *rcv_start_pcl, *rcv_pcl;
__le32 *rcv_buffer;
dma_addr_t rcv_start_pcl_bus, rcv_pcl_bus, rcv_buffer_bus;
spinlock_t client_list_lock;
struct list_head client_list;
struct miscdevice misc;
struct list_head link;
struct kref kref;
};
static inline struct pcilynx *
lynx_get(struct pcilynx *lynx)
{
kref_get(&lynx->kref);
return lynx;
}
static void
lynx_release(struct kref *kref)
{
kfree(container_of(kref, struct pcilynx, kref));
}
static inline void
lynx_put(struct pcilynx *lynx)
{
kref_put(&lynx->kref, lynx_release);
}
struct client {
struct pcilynx *lynx;
u32 tcode_mask;
struct packet_buffer buffer;
struct list_head link;
};
static DEFINE_MUTEX(card_mutex);
static LIST_HEAD(card_list);
static int
packet_buffer_init(struct packet_buffer *buffer, size_t capacity)
{
buffer->data = kmalloc(capacity, GFP_KERNEL);
if (buffer->data == NULL)
return -ENOMEM;
buffer->head = (struct packet *) buffer->data;
buffer->tail = (struct packet *) buffer->data;
buffer->capacity = capacity;
buffer->lost_packet_count = 0;
atomic_set(&buffer->size, 0);
init_waitqueue_head(&buffer->wait);
return 0;
}
static void
packet_buffer_destroy(struct packet_buffer *buffer)
{
kfree(buffer->data);
}
static int
packet_buffer_get(struct client *client, char __user *data, size_t user_length)
{
struct packet_buffer *buffer = &client->buffer;
size_t length;
char *end;
if (wait_event_interruptible(buffer->wait,
atomic_read(&buffer->size) > 0) ||
list_empty(&client->lynx->link))
return -ERESTARTSYS;
if (atomic_read(&buffer->size) == 0)
return -ENODEV;
length = buffer->head->length;
if (length > user_length)
return 0;
end = buffer->data + buffer->capacity;
if (&buffer->head->data[length] < end) {
if (copy_to_user(data, buffer->head->data, length))
return -EFAULT;
buffer->head = (struct packet *) &buffer->head->data[length];
} else {
size_t split = end - buffer->head->data;
if (copy_to_user(data, buffer->head->data, split))
return -EFAULT;
if (copy_to_user(data + split, buffer->data, length - split))
return -EFAULT;
buffer->head = (struct packet *) &buffer->data[length - split];
}
/*
* Decrease buffer->size as the last thing, since this is what
* keeps the interrupt from overwriting the packet we are
* retrieving from the buffer.
*/
atomic_sub(sizeof(struct packet) + length, &buffer->size);
return length;
}
static void
packet_buffer_put(struct packet_buffer *buffer, void *data, size_t length)
{
char *end;
buffer->total_packet_count++;
if (buffer->capacity <
atomic_read(&buffer->size) + sizeof(struct packet) + length) {
buffer->lost_packet_count++;
return;
}
end = buffer->data + buffer->capacity;
buffer->tail->length = length;
if (&buffer->tail->data[length] < end) {
memcpy(buffer->tail->data, data, length);
buffer->tail = (struct packet *) &buffer->tail->data[length];
} else {
size_t split = end - buffer->tail->data;
memcpy(buffer->tail->data, data, split);
memcpy(buffer->data, data + split, length - split);
buffer->tail = (struct packet *) &buffer->data[length - split];
}
/* Finally, adjust buffer size and wake up userspace reader. */
atomic_add(sizeof(struct packet) + length, &buffer->size);
wake_up_interruptible(&buffer->wait);
}
static inline void
reg_write(struct pcilynx *lynx, int offset, u32 data)
{
writel(data, lynx->registers + offset);
}
static inline u32
reg_read(struct pcilynx *lynx, int offset)
{
return readl(lynx->registers + offset);
}
static inline void
reg_set_bits(struct pcilynx *lynx, int offset, u32 mask)
{
reg_write(lynx, offset, (reg_read(lynx, offset) | mask));
}
/*
* Maybe the pcl programs could be set up to just append data instead
* of using a whole packet.
*/
static inline void
run_pcl(struct pcilynx *lynx, dma_addr_t pcl_bus,
int dmachan)
{
reg_write(lynx, DMA0_CURRENT_PCL + dmachan * 0x20, pcl_bus);
reg_write(lynx, DMA0_CHAN_CTRL + dmachan * 0x20,
DMA_CHAN_CTRL_ENABLE | DMA_CHAN_CTRL_LINK);
}
static int
set_phy_reg(struct pcilynx *lynx, int addr, int val)
{
if (addr > 15) {
dev_err(&lynx->pci_device->dev,
"PHY register address %d out of range\n", addr);
return -1;
}
if (val > 0xff) {
dev_err(&lynx->pci_device->dev,
"PHY register value %d out of range\n", val);
return -1;
}
reg_write(lynx, LINK_PHY, LINK_PHY_WRITE |
LINK_PHY_ADDR(addr) | LINK_PHY_WDATA(val));
return 0;
}
static int
nosy_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
struct client *client;
struct pcilynx *tmp, *lynx = NULL;
mutex_lock(&card_mutex);
list_for_each_entry(tmp, &card_list, link)
if (tmp->misc.minor == minor) {
lynx = lynx_get(tmp);
break;
}
mutex_unlock(&card_mutex);
if (lynx == NULL)
return -ENODEV;
client = kmalloc_obj(*client);
if (client == NULL)
goto fail;
client->tcode_mask = ~0;
client->lynx = lynx;
INIT_LIST_HEAD(&client->link);
if (packet_buffer_init(&client->buffer, 128 * 1024) < 0)
goto fail;
file->private_data = client;
return stream_open(inode, file);
fail:
kfree(client);
lynx_put(lynx);
return -ENOMEM;
}
static int
nosy_release(struct inode *inode, struct file *file)
{
struct client *client = file->private_data;
struct pcilynx *lynx = client->lynx;
spin_lock_irq(&lynx->client_list_lock);
list_del_init(&client->link);
spin_unlock_irq(&lynx->client_list_lock);
packet_buffer_destroy(&client->buffer);
kfree(client);
lynx_put(lynx);
return 0;
}
static __poll_t
nosy_poll(struct file *file, poll_table *pt)
{
struct client *client = file->private_data;
__poll_t ret = 0;
poll_wait(file, &client->buffer.wait, pt);
if (atomic_read(&client->buffer.size) > 0)
ret = EPOLLIN | EPOLLRDNORM;
if (list_empty(&client->lynx->link))
ret |= EPOLLHUP;
return ret;
}
static ssize_t
nosy_read(struct file *file, char __user *buffer, size_t count, loff_t *offset)
{
struct client *client = file->private_data;
return packet_buffer_get(client, buffer, count);
}
static long
nosy_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct client *client = file->private_data;
spinlock_t *client_list_lock = &client->lynx->client_list_lock;
struct nosy_stats stats;
int ret;
switch (cmd) {
case NOSY_IOC_GET_STATS:
spin_lock_irq(client_list_lock);
stats.total_packet_count = client->buffer.total_packet_count;
stats.lost_packet_count = client->buffer.lost_packet_count;
spin_unlock_irq(client_list_lock);
if (copy_to_user((void __user *) arg, &stats, sizeof stats))
return -EFAULT;
else
return 0;
case NOSY_IOC_START:
ret = -EBUSY;
spin_lock_irq(client_list_lock);
if (list_empty(&client->link)) {
list_add_tail(&client->link, &client->lynx->client_list);
ret = 0;
}
spin_unlock_irq(client_list_lock);
return ret;
case NOSY_IOC_STOP:
spin_lock_irq(client_list_lock);
list_del_init(&client->link);
spin_unlock_irq(client_list_lock);
return 0;
case NOSY_IOC_FILTER:
spin_lock_irq(client_list_lock);
client->tcode_mask = arg;
spin_unlock_irq(client_list_lock);
return 0;
default:
return -EINVAL;
/* Flush buffer, configure filter. */
}
}
static const struct file_operations nosy_ops = {
.owner = THIS_MODULE,
.read = nosy_read,
.unlocked_ioctl = nosy_ioctl,
.poll = nosy_poll,
.open = nosy_open,
.release = nosy_release,
};
#define PHY_PACKET_SIZE 12 /* 1 payload, 1 inverse, 1 ack = 3 quadlets */
static void
packet_irq_handler(struct pcilynx *lynx)
{
struct client *client;
u32 tcode_mask, tcode, timestamp;
size_t length;
struct timespec64 ts64;
/* FIXME: Also report rcv_speed. */
length = __le32_to_cpu(lynx->rcv_pcl->pcl_status) & 0x00001fff;
tcode = __le32_to_cpu(lynx->rcv_buffer[1]) >> 4 & 0xf;
ktime_get_real_ts64(&ts64);
timestamp = ts64.tv_nsec / NSEC_PER_USEC;
lynx->rcv_buffer[0] = (__force __le32)timestamp;
if (length == PHY_PACKET_SIZE)
tcode_mask = 1 << TCODE_PHY_PACKET;
else
tcode_mask = 1 << tcode;
spin_lock(&lynx->client_list_lock);
list_for_each_entry(client, &lynx->client_list, link)
if (client->tcode_mask & tcode_mask)
packet_buffer_put(&client->buffer,
lynx->rcv_buffer, length + 4);
spin_unlock(&lynx->client_list_lock);
}
static void
bus_reset_irq_handler(struct pcilynx *lynx)
{
struct client *client;
struct timespec64 ts64;
u32 timestamp;
ktime_get_real_ts64(&ts64);
timestamp = ts64.tv_nsec / NSEC_PER_USEC;
spin_lock(&lynx->client_list_lock);
list_for_each_entry(client, &lynx->client_list, link)
packet_buffer_put(&client->buffer, &timestamp, 4);
spin_unlock(&lynx->client_list_lock);
}
static irqreturn_t
irq_handler(int irq, void *device)
{
struct pcilynx *lynx = device;
u32 pci_int_status;
pci_int_status = reg_read(lynx, PCI_INT_STATUS);
if (pci_int_status == ~0)
/* Card was ejected. */
return IRQ_NONE;
if ((pci_int_status & PCI_INT_INT_PEND) == 0)
/* Not our interrupt, bail out quickly. */
return IRQ_NONE;
if ((pci_int_status & PCI_INT_P1394_INT) != 0) {
u32 link_int_status;
link_int_status = reg_read(lynx, LINK_INT_STATUS);
reg_write(lynx, LINK_INT_STATUS, link_int_status);
if ((link_int_status & LINK_INT_PHY_BUSRESET) > 0)
bus_reset_irq_handler(lynx);
}
/* Clear the PCI_INT_STATUS register only after clearing the
* LINK_INT_STATUS register; otherwise the PCI_INT_P1394 will
* be set again immediately. */
reg_write(lynx, PCI_INT_STATUS, pci_int_status);
if ((pci_int_status & PCI_INT_DMA0_HLT) > 0) {
packet_irq_handler(lynx);
run_pcl(lynx, lynx->rcv_start_pcl_bus, 0);
}
return IRQ_HANDLED;
}
static void
remove_card(struct pci_dev *dev)
{
struct pcilynx *lynx = pci_get_drvdata(dev);
struct client *client;
mutex_lock(&card_mutex);
list_del_init(&lynx->link);
misc_deregister(&lynx->misc);
mutex_unlock(&card_mutex);
reg_write(lynx, PCI_INT_ENABLE, 0);
free_irq(lynx->pci_device->irq, lynx);
spin_lock_irq(&lynx->client_list_lock);
list_for_each_entry(client, &lynx->client_list, link)
wake_up_interruptible(&client->buffer.wait);
spin_unlock_irq(&lynx->client_list_lock);
dma_free_coherent(&lynx->pci_device->dev, sizeof(struct pcl),
lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus);
dma_free_coherent(&lynx->pci_device->dev, sizeof(struct pcl),
lynx->rcv_pcl, lynx->rcv_pcl_bus);
dma_free_coherent(&lynx->pci_device->dev, RCV_BUFFER_SIZE,
lynx->rcv_buffer, lynx->rcv_buffer_bus);
iounmap(lynx->registers);
pci_disable_device(dev);
lynx_put(lynx);
}
static int
add_card(struct pci_dev *dev, const struct pci_device_id *unused)
{
struct pcilynx *lynx;
u32 p, end;
int ret, i;
if (dma_set_mask(&dev->dev, DMA_BIT_MASK(32))) {
dev_err(&dev->dev,
"DMA address limits not supported for PCILynx hardware\n");
return -ENXIO;
}
if (pci_enable_device(dev)) {
dev_err(&dev->dev, "Failed to enable PCILynx hardware\n");
return -ENXIO;
}
pci_set_master(dev);
lynx = kzalloc_obj(*lynx);
if (lynx == NULL) {
dev_err(&dev->dev, "Failed to allocate control structure\n");
ret = -ENOMEM;
goto fail_disable;
}
lynx->pci_device = dev;
pci_set_drvdata(dev, lynx);
spin_lock_init(&lynx->client_list_lock);
INIT_LIST_HEAD(&lynx->client_list);
kref_init(&lynx->kref);
lynx->registers = ioremap(pci_resource_start(dev, 0),
PCILYNX_MAX_REGISTER);
if (lynx->registers == NULL) {
dev_err(&dev->dev, "Failed to map registers\n");
ret = -ENOMEM;
goto fail_deallocate_lynx;
}
lynx->rcv_start_pcl = dma_alloc_coherent(&lynx->pci_device->dev,
sizeof(struct pcl),
&lynx->rcv_start_pcl_bus,
GFP_KERNEL);
lynx->rcv_pcl = dma_alloc_coherent(&lynx->pci_device->dev,
sizeof(struct pcl),
&lynx->rcv_pcl_bus, GFP_KERNEL);
lynx->rcv_buffer = dma_alloc_coherent(&lynx->pci_device->dev,
RCV_BUFFER_SIZE,
&lynx->rcv_buffer_bus, GFP_KERNEL);
if (lynx->rcv_start_pcl == NULL ||
lynx->rcv_pcl == NULL ||
lynx->rcv_buffer == NULL) {
dev_err(&dev->dev, "Failed to allocate receive buffer\n");
ret = -ENOMEM;
goto fail_deallocate_buffers;
}
lynx->rcv_start_pcl->next = cpu_to_le32(lynx->rcv_pcl_bus);
lynx->rcv_pcl->next = cpu_to_le32(PCL_NEXT_INVALID);
lynx->rcv_pcl->async_error_next = cpu_to_le32(PCL_NEXT_INVALID);
lynx->rcv_pcl->buffer[0].control =
cpu_to_le32(PCL_CMD_RCV | PCL_BIGENDIAN | 2044);
lynx->rcv_pcl->buffer[0].pointer =
cpu_to_le32(lynx->rcv_buffer_bus + 4);
p = lynx->rcv_buffer_bus + 2048;
end = lynx->rcv_buffer_bus + RCV_BUFFER_SIZE;
for (i = 1; p < end; i++, p += 2048) {
lynx->rcv_pcl->buffer[i].control =
cpu_to_le32(PCL_CMD_RCV | PCL_BIGENDIAN | 2048);
lynx->rcv_pcl->buffer[i].pointer = cpu_to_le32(p);
}
lynx->rcv_pcl->buffer[i - 1].control |= cpu_to_le32(PCL_LAST_BUFF);
reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
/* Fix buggy cards with autoboot pin not tied low: */
reg_write(lynx, DMA0_CHAN_CTRL, 0);
reg_write(lynx, DMA_GLOBAL_REGISTER, 0x00 << 24);
#if 0
/* now, looking for PHY register set */
if ((get_phy_reg(lynx, 2) & 0xe0) == 0xe0) {
lynx->phyic.reg_1394a = 1;
PRINT(KERN_INFO, lynx->id,
"found 1394a conform PHY (using extended register set)");
lynx->phyic.vendor = get_phy_vendorid(lynx);
lynx->phyic.product = get_phy_productid(lynx);
} else {
lynx->phyic.reg_1394a = 0;
PRINT(KERN_INFO, lynx->id, "found old 1394 PHY");
}
#endif
/* Setup the general receive FIFO max size. */
reg_write(lynx, FIFO_SIZES, 255);
reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_DMA_ALL);
reg_write(lynx, LINK_INT_ENABLE,
LINK_INT_PHY_TIME_OUT | LINK_INT_PHY_REG_RCVD |
LINK_INT_PHY_BUSRESET | LINK_INT_IT_STUCK |
LINK_INT_AT_STUCK | LINK_INT_SNTRJ |
LINK_INT_TC_ERR | LINK_INT_GRF_OVER_FLOW |
LINK_INT_ITF_UNDER_FLOW | LINK_INT_ATF_UNDER_FLOW);
/* Disable the L flag in self ID packets. */
set_phy_reg(lynx, 4, 0);
/* Put this baby into snoop mode */
reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_SNOOP_ENABLE);
run_pcl(lynx, lynx->rcv_start_pcl_bus, 0);
if (request_irq(dev->irq, irq_handler, IRQF_SHARED,
driver_name, lynx)) {
dev_err(&dev->dev,
"Failed to allocate shared interrupt %d\n", dev->irq);
ret = -EIO;
goto fail_deallocate_buffers;
}
lynx->misc.parent = &dev->dev;
lynx->misc.minor = MISC_DYNAMIC_MINOR;
lynx->misc.name = "nosy";
lynx->misc.fops = &nosy_ops;
mutex_lock(&card_mutex);
ret = misc_register(&lynx->misc);
if (ret) {
dev_err(&dev->dev, "Failed to register misc char device\n");
mutex_unlock(&card_mutex);
goto fail_free_irq;
}
list_add_tail(&lynx->link, &card_list);
mutex_unlock(&card_mutex);
dev_info(&dev->dev,
"Initialized PCILynx IEEE1394 card, irq=%d\n", dev->irq);
return 0;
fail_free_irq:
reg_write(lynx, PCI_INT_ENABLE, 0);
free_irq(lynx->pci_device->irq, lynx);
fail_deallocate_buffers:
if (lynx->rcv_start_pcl)
dma_free_coherent(&lynx->pci_device->dev, sizeof(struct pcl),
lynx->rcv_start_pcl,
lynx->rcv_start_pcl_bus);
if (lynx->rcv_pcl)
dma_free_coherent(&lynx->pci_device->dev, sizeof(struct pcl),
lynx->rcv_pcl, lynx->rcv_pcl_bus);
if (lynx->rcv_buffer)
dma_free_coherent(&lynx->pci_device->dev, RCV_BUFFER_SIZE,
lynx->rcv_buffer, lynx->rcv_buffer_bus);
iounmap(lynx->registers);
fail_deallocate_lynx:
kfree(lynx);
fail_disable:
pci_disable_device(dev);
return ret;
}
static struct pci_device_id pci_table[] = {
{
.vendor = PCI_VENDOR_ID_TI,
.device = PCI_DEVICE_ID_TI_PCILYNX,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(pci, pci_table);
static struct pci_driver lynx_pci_driver = {
.name = driver_name,
.id_table = pci_table,
.probe = add_card,
.remove = remove_card,
};
module_pci_driver(lynx_pci_driver);
MODULE_AUTHOR("Kristian Hoegsberg");
MODULE_DESCRIPTION("Snoop mode driver for TI pcilynx 1394 controllers");
MODULE_LICENSE("GPL");
+238
View File
@@ -0,0 +1,238 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Chip register definitions for PCILynx chipset. Based on pcilynx.h
* from the Linux 1394 drivers, but modified a bit so the names here
* match the specification exactly (even though they have weird names,
* like xxx_OVER_FLOW, or arbitrary abbreviations like SNTRJ for "sent
* reject" etc.)
*/
#define PCILYNX_MAX_REGISTER 0xfff
#define PCILYNX_MAX_MEMORY 0xffff
#define PCI_LATENCY_CACHELINE 0x0c
#define MISC_CONTROL 0x40
#define MISC_CONTROL_SWRESET (1<<0)
#define SERIAL_EEPROM_CONTROL 0x44
#define PCI_INT_STATUS 0x48
#define PCI_INT_ENABLE 0x4c
/* status and enable have identical bit numbers */
#define PCI_INT_INT_PEND (1<<31)
#define PCI_INT_FRC_INT (1<<30)
#define PCI_INT_SLV_ADR_PERR (1<<28)
#define PCI_INT_SLV_DAT_PERR (1<<27)
#define PCI_INT_MST_DAT_PERR (1<<26)
#define PCI_INT_MST_DEV_TO (1<<25)
#define PCI_INT_INT_SLV_TO (1<<23)
#define PCI_INT_AUX_TO (1<<18)
#define PCI_INT_AUX_INT (1<<17)
#define PCI_INT_P1394_INT (1<<16)
#define PCI_INT_DMA4_PCL (1<<9)
#define PCI_INT_DMA4_HLT (1<<8)
#define PCI_INT_DMA3_PCL (1<<7)
#define PCI_INT_DMA3_HLT (1<<6)
#define PCI_INT_DMA2_PCL (1<<5)
#define PCI_INT_DMA2_HLT (1<<4)
#define PCI_INT_DMA1_PCL (1<<3)
#define PCI_INT_DMA1_HLT (1<<2)
#define PCI_INT_DMA0_PCL (1<<1)
#define PCI_INT_DMA0_HLT (1<<0)
/* all DMA interrupts combined: */
#define PCI_INT_DMA_ALL 0x3ff
#define PCI_INT_DMA_HLT(chan) (1 << (chan * 2))
#define PCI_INT_DMA_PCL(chan) (1 << (chan * 2 + 1))
#define LBUS_ADDR 0xb4
#define LBUS_ADDR_SEL_RAM (0x0<<16)
#define LBUS_ADDR_SEL_ROM (0x1<<16)
#define LBUS_ADDR_SEL_AUX (0x2<<16)
#define LBUS_ADDR_SEL_ZV (0x3<<16)
#define GPIO_CTRL_A 0xb8
#define GPIO_CTRL_B 0xbc
#define GPIO_DATA_BASE 0xc0
#define DMA_BREG(base, chan) (base + chan * 0x20)
#define DMA_SREG(base, chan) (base + chan * 0x10)
#define PCL_NEXT_INVALID (1<<0)
/* transfer commands */
#define PCL_CMD_RCV (0x1<<24)
#define PCL_CMD_RCV_AND_UPDATE (0xa<<24)
#define PCL_CMD_XMT (0x2<<24)
#define PCL_CMD_UNFXMT (0xc<<24)
#define PCL_CMD_PCI_TO_LBUS (0x8<<24)
#define PCL_CMD_LBUS_TO_PCI (0x9<<24)
/* aux commands */
#define PCL_CMD_NOP (0x0<<24)
#define PCL_CMD_LOAD (0x3<<24)
#define PCL_CMD_STOREQ (0x4<<24)
#define PCL_CMD_STORED (0xb<<24)
#define PCL_CMD_STORE0 (0x5<<24)
#define PCL_CMD_STORE1 (0x6<<24)
#define PCL_CMD_COMPARE (0xe<<24)
#define PCL_CMD_SWAP_COMPARE (0xf<<24)
#define PCL_CMD_ADD (0xd<<24)
#define PCL_CMD_BRANCH (0x7<<24)
/* BRANCH condition codes */
#define PCL_COND_DMARDY_SET (0x1<<20)
#define PCL_COND_DMARDY_CLEAR (0x2<<20)
#define PCL_GEN_INTR (1<<19)
#define PCL_LAST_BUFF (1<<18)
#define PCL_LAST_CMD (PCL_LAST_BUFF)
#define PCL_WAITSTAT (1<<17)
#define PCL_BIGENDIAN (1<<16)
#define PCL_ISOMODE (1<<12)
#define DMA0_PREV_PCL 0x100
#define DMA1_PREV_PCL 0x120
#define DMA2_PREV_PCL 0x140
#define DMA3_PREV_PCL 0x160
#define DMA4_PREV_PCL 0x180
#define DMA_PREV_PCL(chan) (DMA_BREG(DMA0_PREV_PCL, chan))
#define DMA0_CURRENT_PCL 0x104
#define DMA1_CURRENT_PCL 0x124
#define DMA2_CURRENT_PCL 0x144
#define DMA3_CURRENT_PCL 0x164
#define DMA4_CURRENT_PCL 0x184
#define DMA_CURRENT_PCL(chan) (DMA_BREG(DMA0_CURRENT_PCL, chan))
#define DMA0_CHAN_STAT 0x10c
#define DMA1_CHAN_STAT 0x12c
#define DMA2_CHAN_STAT 0x14c
#define DMA3_CHAN_STAT 0x16c
#define DMA4_CHAN_STAT 0x18c
#define DMA_CHAN_STAT(chan) (DMA_BREG(DMA0_CHAN_STAT, chan))
/* CHAN_STATUS registers share bits */
#define DMA_CHAN_STAT_SELFID (1<<31)
#define DMA_CHAN_STAT_ISOPKT (1<<30)
#define DMA_CHAN_STAT_PCIERR (1<<29)
#define DMA_CHAN_STAT_PKTERR (1<<28)
#define DMA_CHAN_STAT_PKTCMPL (1<<27)
#define DMA_CHAN_STAT_SPECIALACK (1<<14)
#define DMA0_CHAN_CTRL 0x110
#define DMA1_CHAN_CTRL 0x130
#define DMA2_CHAN_CTRL 0x150
#define DMA3_CHAN_CTRL 0x170
#define DMA4_CHAN_CTRL 0x190
#define DMA_CHAN_CTRL(chan) (DMA_BREG(DMA0_CHAN_CTRL, chan))
/* CHAN_CTRL registers share bits */
#define DMA_CHAN_CTRL_ENABLE (1<<31)
#define DMA_CHAN_CTRL_BUSY (1<<30)
#define DMA_CHAN_CTRL_LINK (1<<29)
#define DMA0_READY 0x114
#define DMA1_READY 0x134
#define DMA2_READY 0x154
#define DMA3_READY 0x174
#define DMA4_READY 0x194
#define DMA_READY(chan) (DMA_BREG(DMA0_READY, chan))
#define DMA_GLOBAL_REGISTER 0x908
#define FIFO_SIZES 0xa00
#define FIFO_CONTROL 0xa10
#define FIFO_CONTROL_GRF_FLUSH (1<<4)
#define FIFO_CONTROL_ITF_FLUSH (1<<3)
#define FIFO_CONTROL_ATF_FLUSH (1<<2)
#define FIFO_XMIT_THRESHOLD 0xa14
#define DMA0_WORD0_CMP_VALUE 0xb00
#define DMA1_WORD0_CMP_VALUE 0xb10
#define DMA2_WORD0_CMP_VALUE 0xb20
#define DMA3_WORD0_CMP_VALUE 0xb30
#define DMA4_WORD0_CMP_VALUE 0xb40
#define DMA_WORD0_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD0_CMP_VALUE, chan))
#define DMA0_WORD0_CMP_ENABLE 0xb04
#define DMA1_WORD0_CMP_ENABLE 0xb14
#define DMA2_WORD0_CMP_ENABLE 0xb24
#define DMA3_WORD0_CMP_ENABLE 0xb34
#define DMA4_WORD0_CMP_ENABLE 0xb44
#define DMA_WORD0_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD0_CMP_ENABLE, chan))
#define DMA0_WORD1_CMP_VALUE 0xb08
#define DMA1_WORD1_CMP_VALUE 0xb18
#define DMA2_WORD1_CMP_VALUE 0xb28
#define DMA3_WORD1_CMP_VALUE 0xb38
#define DMA4_WORD1_CMP_VALUE 0xb48
#define DMA_WORD1_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD1_CMP_VALUE, chan))
#define DMA0_WORD1_CMP_ENABLE 0xb0c
#define DMA1_WORD1_CMP_ENABLE 0xb1c
#define DMA2_WORD1_CMP_ENABLE 0xb2c
#define DMA3_WORD1_CMP_ENABLE 0xb3c
#define DMA4_WORD1_CMP_ENABLE 0xb4c
#define DMA_WORD1_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD1_CMP_ENABLE, chan))
/* word 1 compare enable flags */
#define DMA_WORD1_CMP_MATCH_OTHERBUS (1<<15)
#define DMA_WORD1_CMP_MATCH_BROADCAST (1<<14)
#define DMA_WORD1_CMP_MATCH_BUS_BCAST (1<<13)
#define DMA_WORD1_CMP_MATCH_LOCAL_NODE (1<<12)
#define DMA_WORD1_CMP_MATCH_EXACT (1<<11)
#define DMA_WORD1_CMP_ENABLE_SELF_ID (1<<10)
#define DMA_WORD1_CMP_ENABLE_MASTER (1<<8)
#define LINK_ID 0xf00
#define LINK_ID_BUS(id) (id<<22)
#define LINK_ID_NODE(id) (id<<16)
#define LINK_CONTROL 0xf04
#define LINK_CONTROL_BUSY (1<<29)
#define LINK_CONTROL_TX_ISO_EN (1<<26)
#define LINK_CONTROL_RX_ISO_EN (1<<25)
#define LINK_CONTROL_TX_ASYNC_EN (1<<24)
#define LINK_CONTROL_RX_ASYNC_EN (1<<23)
#define LINK_CONTROL_RESET_TX (1<<21)
#define LINK_CONTROL_RESET_RX (1<<20)
#define LINK_CONTROL_CYCMASTER (1<<11)
#define LINK_CONTROL_CYCSOURCE (1<<10)
#define LINK_CONTROL_CYCTIMEREN (1<<9)
#define LINK_CONTROL_RCV_CMP_VALID (1<<7)
#define LINK_CONTROL_SNOOP_ENABLE (1<<6)
#define CYCLE_TIMER 0xf08
#define LINK_PHY 0xf0c
#define LINK_PHY_READ (1<<31)
#define LINK_PHY_WRITE (1<<30)
#define LINK_PHY_ADDR(addr) (addr<<24)
#define LINK_PHY_WDATA(data) (data<<16)
#define LINK_PHY_RADDR(addr) (addr<<8)
#define LINK_INT_STATUS 0xf14
#define LINK_INT_ENABLE 0xf18
/* status and enable have identical bit numbers */
#define LINK_INT_LINK_INT (1<<31)
#define LINK_INT_PHY_TIME_OUT (1<<30)
#define LINK_INT_PHY_REG_RCVD (1<<29)
#define LINK_INT_PHY_BUSRESET (1<<28)
#define LINK_INT_TX_RDY (1<<26)
#define LINK_INT_RX_DATA_RDY (1<<25)
#define LINK_INT_IT_STUCK (1<<20)
#define LINK_INT_AT_STUCK (1<<19)
#define LINK_INT_SNTRJ (1<<17)
#define LINK_INT_HDR_ERR (1<<16)
#define LINK_INT_TC_ERR (1<<15)
#define LINK_INT_CYC_SEC (1<<11)
#define LINK_INT_CYC_STRT (1<<10)
#define LINK_INT_CYC_DONE (1<<9)
#define LINK_INT_CYC_PEND (1<<8)
#define LINK_INT_CYC_LOST (1<<7)
#define LINK_INT_CYC_ARB_FAILED (1<<6)
#define LINK_INT_GRF_OVER_FLOW (1<<5)
#define LINK_INT_ITF_UNDER_FLOW (1<<4)
#define LINK_INT_ATF_UNDER_FLOW (1<<3)
#define LINK_INT_IARB_FAILED (1<<0)
@@ -0,0 +1,122 @@
// SPDX-License-Identifier: GPL-2.0-or-later
//
// ohci-serdes-test.c - An application of Kunit to check serialization/deserialization of data in
// buffers and registers defined in 1394 OHCI specification.
//
// Copyright (c) 2024 Takashi Sakamoto
#include <kunit/test.h>
#include "ohci.h"
static void test_self_id_count_register_deserialization(struct kunit *test)
{
const u32 expected = 0x803d0594;
bool is_error = ohci1394_self_id_count_is_error(expected);
u8 generation = ohci1394_self_id_count_get_generation(expected);
u32 size = ohci1394_self_id_count_get_size(expected);
KUNIT_EXPECT_TRUE(test, is_error);
KUNIT_EXPECT_EQ(test, 0x3d, generation);
KUNIT_EXPECT_EQ(test, 0x165, size);
}
static void test_self_id_receive_buffer_deserialization(struct kunit *test)
{
const u32 buffer[] = {
0x0006f38b,
0x807fcc56,
0x7f8033a9,
0x8145cc5e,
0x7eba33a1,
};
u8 generation = ohci1394_self_id_receive_q0_get_generation(buffer[0]);
u16 timestamp = ohci1394_self_id_receive_q0_get_timestamp(buffer[0]);
KUNIT_EXPECT_EQ(test, 0x6, generation);
KUNIT_EXPECT_EQ(test, 0xf38b, timestamp);
}
static void test_at_data_serdes(struct kunit *test)
{
static const __le32 expected[] = {
cpu_to_le32(0x00020e80),
cpu_to_le32(0xffc2ffff),
cpu_to_le32(0xe0000000),
};
__le32 quadlets[] = {0, 0, 0};
bool has_src_bus_id = ohci1394_at_data_get_src_bus_id(expected);
unsigned int speed = ohci1394_at_data_get_speed(expected);
unsigned int tlabel = ohci1394_at_data_get_tlabel(expected);
unsigned int retry = ohci1394_at_data_get_retry(expected);
unsigned int tcode = ohci1394_at_data_get_tcode(expected);
unsigned int destination_id = ohci1394_at_data_get_destination_id(expected);
u64 destination_offset = ohci1394_at_data_get_destination_offset(expected);
KUNIT_EXPECT_FALSE(test, has_src_bus_id);
KUNIT_EXPECT_EQ(test, 0x02, speed);
KUNIT_EXPECT_EQ(test, 0x03, tlabel);
KUNIT_EXPECT_EQ(test, 0x02, retry);
KUNIT_EXPECT_EQ(test, 0x08, tcode);
ohci1394_at_data_set_src_bus_id(quadlets, has_src_bus_id);
ohci1394_at_data_set_speed(quadlets, speed);
ohci1394_at_data_set_tlabel(quadlets, tlabel);
ohci1394_at_data_set_retry(quadlets, retry);
ohci1394_at_data_set_tcode(quadlets, tcode);
ohci1394_at_data_set_destination_id(quadlets, destination_id);
ohci1394_at_data_set_destination_offset(quadlets, destination_offset);
KUNIT_EXPECT_MEMEQ(test, quadlets, expected, sizeof(expected));
}
static void test_it_data_serdes(struct kunit *test)
{
static const __le32 expected[] = {
cpu_to_le32(0x000349a7),
cpu_to_le32(0x02300000),
};
__le32 quadlets[] = {0, 0};
unsigned int scode = ohci1394_it_data_get_speed(expected);
unsigned int tag = ohci1394_it_data_get_tag(expected);
unsigned int channel = ohci1394_it_data_get_channel(expected);
unsigned int tcode = ohci1394_it_data_get_tcode(expected);
unsigned int sync = ohci1394_it_data_get_sync(expected);
unsigned int data_length = ohci1394_it_data_get_data_length(expected);
KUNIT_EXPECT_EQ(test, 0x03, scode);
KUNIT_EXPECT_EQ(test, 0x01, tag);
KUNIT_EXPECT_EQ(test, 0x09, channel);
KUNIT_EXPECT_EQ(test, 0x0a, tcode);
KUNIT_EXPECT_EQ(test, 0x7, sync);
KUNIT_EXPECT_EQ(test, 0x0230, data_length);
ohci1394_it_data_set_speed(quadlets, scode);
ohci1394_it_data_set_tag(quadlets, tag);
ohci1394_it_data_set_channel(quadlets, channel);
ohci1394_it_data_set_tcode(quadlets, tcode);
ohci1394_it_data_set_sync(quadlets, sync);
ohci1394_it_data_set_data_length(quadlets, data_length);
KUNIT_EXPECT_MEMEQ(test, quadlets, expected, sizeof(expected));
}
static struct kunit_case ohci_serdes_test_cases[] = {
KUNIT_CASE(test_self_id_count_register_deserialization),
KUNIT_CASE(test_self_id_receive_buffer_deserialization),
KUNIT_CASE(test_at_data_serdes),
KUNIT_CASE(test_it_data_serdes),
{}
};
static struct kunit_suite ohci_serdes_test_suite = {
.name = "firewire-ohci-serdes",
.test_cases = ohci_serdes_test_cases,
};
kunit_test_suite(ohci_serdes_test_suite);
MODULE_DESCRIPTION("FireWire buffers and registers serialization/deserialization unit test suite");
MODULE_LICENSE("GPL");
File diff suppressed because it is too large Load Diff
+398
View File
@@ -0,0 +1,398 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _FIREWIRE_OHCI_H
#define _FIREWIRE_OHCI_H
/* OHCI register map */
#define OHCI1394_Version 0x000
#define OHCI1394_GUID_ROM 0x004
#define OHCI1394_ATRetries 0x008
#define OHCI1394_CSRData 0x00C
#define OHCI1394_CSRCompareData 0x010
#define OHCI1394_CSRControl 0x014
#define OHCI1394_ConfigROMhdr 0x018
#define OHCI1394_BusID 0x01C
#define OHCI1394_BusOptions 0x020
#define OHCI1394_GUIDHi 0x024
#define OHCI1394_GUIDLo 0x028
#define OHCI1394_ConfigROMmap 0x034
#define OHCI1394_PostedWriteAddressLo 0x038
#define OHCI1394_PostedWriteAddressHi 0x03C
#define OHCI1394_VendorID 0x040
#define OHCI1394_HCControlSet 0x050
#define OHCI1394_HCControlClear 0x054
#define OHCI1394_HCControl_BIBimageValid 0x80000000
#define OHCI1394_HCControl_noByteSwapData 0x40000000
#define OHCI1394_HCControl_programPhyEnable 0x00800000
#define OHCI1394_HCControl_aPhyEnhanceEnable 0x00400000
#define OHCI1394_HCControl_LPS 0x00080000
#define OHCI1394_HCControl_postedWriteEnable 0x00040000
#define OHCI1394_HCControl_linkEnable 0x00020000
#define OHCI1394_HCControl_softReset 0x00010000
#define OHCI1394_SelfIDBuffer 0x064
#define OHCI1394_SelfIDCount 0x068
#define OHCI1394_IRMultiChanMaskHiSet 0x070
#define OHCI1394_IRMultiChanMaskHiClear 0x074
#define OHCI1394_IRMultiChanMaskLoSet 0x078
#define OHCI1394_IRMultiChanMaskLoClear 0x07C
#define OHCI1394_IntEventSet 0x080
#define OHCI1394_IntEventClear 0x084
#define OHCI1394_IntMaskSet 0x088
#define OHCI1394_IntMaskClear 0x08C
#define OHCI1394_IsoXmitIntEventSet 0x090
#define OHCI1394_IsoXmitIntEventClear 0x094
#define OHCI1394_IsoXmitIntMaskSet 0x098
#define OHCI1394_IsoXmitIntMaskClear 0x09C
#define OHCI1394_IsoRecvIntEventSet 0x0A0
#define OHCI1394_IsoRecvIntEventClear 0x0A4
#define OHCI1394_IsoRecvIntMaskSet 0x0A8
#define OHCI1394_IsoRecvIntMaskClear 0x0AC
#define OHCI1394_InitialBandwidthAvailable 0x0B0
#define OHCI1394_InitialChannelsAvailableHi 0x0B4
#define OHCI1394_InitialChannelsAvailableLo 0x0B8
#define OHCI1394_FairnessControl 0x0DC
#define OHCI1394_LinkControlSet 0x0E0
#define OHCI1394_LinkControlClear 0x0E4
#define OHCI1394_LinkControl_rcvSelfID (1 << 9)
#define OHCI1394_LinkControl_rcvPhyPkt (1 << 10)
#define OHCI1394_LinkControl_cycleTimerEnable (1 << 20)
#define OHCI1394_LinkControl_cycleMaster (1 << 21)
#define OHCI1394_LinkControl_cycleSource (1 << 22)
#define OHCI1394_NodeID 0x0E8
#define OHCI1394_NodeID_idValid 0x80000000
#define OHCI1394_NodeID_root 0x40000000
#define OHCI1394_NodeID_nodeNumber 0x0000003f
#define OHCI1394_NodeID_busNumber 0x0000ffc0
#define OHCI1394_PhyControl 0x0EC
#define OHCI1394_PhyControl_Read(addr) (((addr) << 8) | 0x00008000)
#define OHCI1394_PhyControl_ReadDone 0x80000000
#define OHCI1394_PhyControl_ReadData(r) (((r) & 0x00ff0000) >> 16)
#define OHCI1394_PhyControl_Write(addr, data) (((addr) << 8) | (data) | 0x00004000)
#define OHCI1394_PhyControl_WritePending 0x00004000
#define OHCI1394_IsochronousCycleTimer 0x0F0
#define OHCI1394_AsReqFilterHiSet 0x100
#define OHCI1394_AsReqFilterHiClear 0x104
#define OHCI1394_AsReqFilterLoSet 0x108
#define OHCI1394_AsReqFilterLoClear 0x10C
#define OHCI1394_PhyReqFilterHiSet 0x110
#define OHCI1394_PhyReqFilterHiClear 0x114
#define OHCI1394_PhyReqFilterLoSet 0x118
#define OHCI1394_PhyReqFilterLoClear 0x11C
#define OHCI1394_PhyUpperBound 0x120
#define OHCI1394_AsReqTrContextBase 0x180
#define OHCI1394_AsReqTrContextControlSet 0x180
#define OHCI1394_AsReqTrContextControlClear 0x184
#define OHCI1394_AsReqTrCommandPtr 0x18C
#define OHCI1394_AsRspTrContextBase 0x1A0
#define OHCI1394_AsRspTrContextControlSet 0x1A0
#define OHCI1394_AsRspTrContextControlClear 0x1A4
#define OHCI1394_AsRspTrCommandPtr 0x1AC
#define OHCI1394_AsReqRcvContextBase 0x1C0
#define OHCI1394_AsReqRcvContextControlSet 0x1C0
#define OHCI1394_AsReqRcvContextControlClear 0x1C4
#define OHCI1394_AsReqRcvCommandPtr 0x1CC
#define OHCI1394_AsRspRcvContextBase 0x1E0
#define OHCI1394_AsRspRcvContextControlSet 0x1E0
#define OHCI1394_AsRspRcvContextControlClear 0x1E4
#define OHCI1394_AsRspRcvCommandPtr 0x1EC
/* Isochronous transmit registers */
#define OHCI1394_IsoXmitContextBase(n) (0x200 + 16 * (n))
#define OHCI1394_IsoXmitContextControlSet(n) (0x200 + 16 * (n))
#define OHCI1394_IsoXmitContextControlClear(n) (0x204 + 16 * (n))
#define OHCI1394_IsoXmitCommandPtr(n) (0x20C + 16 * (n))
/* Isochronous receive registers */
#define OHCI1394_IsoRcvContextBase(n) (0x400 + 32 * (n))
#define OHCI1394_IsoRcvContextControlSet(n) (0x400 + 32 * (n))
#define OHCI1394_IsoRcvContextControlClear(n) (0x404 + 32 * (n))
#define OHCI1394_IsoRcvCommandPtr(n) (0x40C + 32 * (n))
#define OHCI1394_IsoRcvContextMatch(n) (0x410 + 32 * (n))
/* Interrupts Mask/Events */
#define OHCI1394_reqTxComplete 0x00000001
#define OHCI1394_respTxComplete 0x00000002
#define OHCI1394_ARRQ 0x00000004
#define OHCI1394_ARRS 0x00000008
#define OHCI1394_RQPkt 0x00000010
#define OHCI1394_RSPkt 0x00000020
#define OHCI1394_isochTx 0x00000040
#define OHCI1394_isochRx 0x00000080
#define OHCI1394_postedWriteErr 0x00000100
#define OHCI1394_lockRespErr 0x00000200
#define OHCI1394_selfIDComplete 0x00010000
#define OHCI1394_busReset 0x00020000
#define OHCI1394_regAccessFail 0x00040000
#define OHCI1394_phy 0x00080000
#define OHCI1394_cycleSynch 0x00100000
#define OHCI1394_cycle64Seconds 0x00200000
#define OHCI1394_cycleLost 0x00400000
#define OHCI1394_cycleInconsistent 0x00800000
#define OHCI1394_unrecoverableError 0x01000000
#define OHCI1394_cycleTooLong 0x02000000
#define OHCI1394_phyRegRcvd 0x04000000
#define OHCI1394_masterIntEnable 0x80000000
#define OHCI1394_evt_no_status 0x0
#define OHCI1394_evt_long_packet 0x2
#define OHCI1394_evt_missing_ack 0x3
#define OHCI1394_evt_underrun 0x4
#define OHCI1394_evt_overrun 0x5
#define OHCI1394_evt_descriptor_read 0x6
#define OHCI1394_evt_data_read 0x7
#define OHCI1394_evt_data_write 0x8
#define OHCI1394_evt_bus_reset 0x9
#define OHCI1394_evt_timeout 0xa
#define OHCI1394_evt_tcode_err 0xb
#define OHCI1394_evt_reserved_b 0xc
#define OHCI1394_evt_reserved_c 0xd
#define OHCI1394_evt_unknown 0xe
#define OHCI1394_evt_flushed 0xf
// Asynchronous Transmit DMA.
//
// The content of first two quadlets of data for AT DMA is different from the header for IEEE 1394
// asynchronous packet.
#define OHCI1394_AT_DATA_Q0_srcBusID_MASK 0x00800000
#define OHCI1394_AT_DATA_Q0_srcBusID_SHIFT 23
#define OHCI1394_AT_DATA_Q0_spd_MASK 0x00070000
#define OHCI1394_AT_DATA_Q0_spd_SHIFT 16
#define OHCI1394_AT_DATA_Q0_tLabel_MASK 0x0000fc00
#define OHCI1394_AT_DATA_Q0_tLabel_SHIFT 10
#define OHCI1394_AT_DATA_Q0_rt_MASK 0x00000300
#define OHCI1394_AT_DATA_Q0_rt_SHIFT 8
#define OHCI1394_AT_DATA_Q0_tCode_MASK 0x000000f0
#define OHCI1394_AT_DATA_Q0_tCode_SHIFT 4
#define OHCI1394_AT_DATA_Q1_destinationId_MASK 0xffff0000
#define OHCI1394_AT_DATA_Q1_destinationId_SHIFT 16
#define OHCI1394_AT_DATA_Q1_destinationOffsetHigh_MASK 0x0000ffff
#define OHCI1394_AT_DATA_Q1_destinationOffsetHigh_SHIFT 0
#define OHCI1394_AT_DATA_Q1_rCode_MASK 0x0000f000
#define OHCI1394_AT_DATA_Q1_rCode_SHIFT 12
static inline bool ohci1394_at_data_get_src_bus_id(const __le32 *data)
{
return !!((data[0] & OHCI1394_AT_DATA_Q0_srcBusID_MASK) >> OHCI1394_AT_DATA_Q0_srcBusID_SHIFT);
}
static inline void ohci1394_at_data_set_src_bus_id(__le32 *data, bool src_bus_id)
{
data[0] &= cpu_to_le32(~OHCI1394_AT_DATA_Q0_srcBusID_MASK);
data[0] |= cpu_to_le32((src_bus_id << OHCI1394_AT_DATA_Q0_srcBusID_SHIFT) & OHCI1394_AT_DATA_Q0_srcBusID_MASK);
}
static inline unsigned int ohci1394_at_data_get_speed(const __le32 *data)
{
return (le32_to_cpu(data[0]) & OHCI1394_AT_DATA_Q0_spd_MASK) >> OHCI1394_AT_DATA_Q0_spd_SHIFT;
}
static inline void ohci1394_at_data_set_speed(__le32 *data, unsigned int scode)
{
data[0] &= cpu_to_le32(~OHCI1394_AT_DATA_Q0_spd_MASK);
data[0] |= cpu_to_le32((scode << OHCI1394_AT_DATA_Q0_spd_SHIFT) & OHCI1394_AT_DATA_Q0_spd_MASK);
}
static inline unsigned int ohci1394_at_data_get_tlabel(const __le32 *data)
{
return (le32_to_cpu(data[0]) & OHCI1394_AT_DATA_Q0_tLabel_MASK) >> OHCI1394_AT_DATA_Q0_tLabel_SHIFT;
}
static inline void ohci1394_at_data_set_tlabel(__le32 *data, unsigned int tlabel)
{
data[0] &= cpu_to_le32(~OHCI1394_AT_DATA_Q0_tLabel_MASK);
data[0] |= cpu_to_le32((tlabel << OHCI1394_AT_DATA_Q0_tLabel_SHIFT) & OHCI1394_AT_DATA_Q0_tLabel_MASK);
}
static inline unsigned int ohci1394_at_data_get_retry(const __le32 *data)
{
return (le32_to_cpu(data[0]) & OHCI1394_AT_DATA_Q0_rt_MASK) >> OHCI1394_AT_DATA_Q0_rt_SHIFT;
}
static inline void ohci1394_at_data_set_retry(__le32 *data, unsigned int retry)
{
data[0] &= cpu_to_le32(~OHCI1394_AT_DATA_Q0_rt_MASK);
data[0] |= cpu_to_le32((retry << OHCI1394_AT_DATA_Q0_rt_SHIFT) & OHCI1394_AT_DATA_Q0_rt_MASK);
}
static inline unsigned int ohci1394_at_data_get_tcode(const __le32 *data)
{
return (le32_to_cpu(data[0]) & OHCI1394_AT_DATA_Q0_tCode_MASK) >> OHCI1394_AT_DATA_Q0_tCode_SHIFT;
}
static inline void ohci1394_at_data_set_tcode(__le32 *data, unsigned int tcode)
{
data[0] &= cpu_to_le32(~OHCI1394_AT_DATA_Q0_tCode_MASK);
data[0] |= cpu_to_le32((tcode << OHCI1394_AT_DATA_Q0_tCode_SHIFT) & OHCI1394_AT_DATA_Q0_tCode_MASK);
}
static inline unsigned int ohci1394_at_data_get_destination_id(const __le32 *data)
{
return (le32_to_cpu(data[1]) & OHCI1394_AT_DATA_Q1_destinationId_MASK) >> OHCI1394_AT_DATA_Q1_destinationId_SHIFT;
}
static inline void ohci1394_at_data_set_destination_id(__le32 *data, unsigned int destination_id)
{
data[1] &= cpu_to_le32(~OHCI1394_AT_DATA_Q1_destinationId_MASK);
data[1] |= cpu_to_le32((destination_id << OHCI1394_AT_DATA_Q1_destinationId_SHIFT) & OHCI1394_AT_DATA_Q1_destinationId_MASK);
}
static inline u64 ohci1394_at_data_get_destination_offset(const __le32 *data)
{
u64 hi = (u64)((le32_to_cpu(data[1]) & OHCI1394_AT_DATA_Q1_destinationOffsetHigh_MASK) >> OHCI1394_AT_DATA_Q1_destinationOffsetHigh_SHIFT);
u64 lo = (u64)le32_to_cpu(data[2]);
return (hi << 32) | lo;
}
static inline void ohci1394_at_data_set_destination_offset(__le32 *data, u64 offset)
{
u32 hi = (u32)(offset >> 32);
u32 lo = (u32)(offset & 0x00000000ffffffff);
data[1] &= cpu_to_le32(~OHCI1394_AT_DATA_Q1_destinationOffsetHigh_MASK);
data[1] |= cpu_to_le32((hi << OHCI1394_AT_DATA_Q1_destinationOffsetHigh_SHIFT) & OHCI1394_AT_DATA_Q1_destinationOffsetHigh_MASK);
data[2] = cpu_to_le32(lo);
}
static inline unsigned int ohci1394_at_data_get_rcode(const __le32 *data)
{
return (le32_to_cpu(data[1]) & OHCI1394_AT_DATA_Q1_rCode_MASK) >> OHCI1394_AT_DATA_Q1_rCode_SHIFT;
}
static inline void ohci1394_at_data_set_rcode(__le32 *data, unsigned int rcode)
{
data[1] &= cpu_to_le32(~OHCI1394_AT_DATA_Q1_rCode_MASK);
data[1] |= cpu_to_le32((rcode << OHCI1394_AT_DATA_Q1_rCode_SHIFT) & OHCI1394_AT_DATA_Q1_rCode_MASK);
}
// Isochronous Transmit DMA.
//
// The content of first two quadlets of data for IT DMA is different from the header for IEEE 1394
// isochronous packet.
#define OHCI1394_IT_DATA_Q0_spd_MASK 0x00070000
#define OHCI1394_IT_DATA_Q0_spd_SHIFT 16
#define OHCI1394_IT_DATA_Q0_tag_MASK 0x0000c000
#define OHCI1394_IT_DATA_Q0_tag_SHIFT 14
#define OHCI1394_IT_DATA_Q0_chanNum_MASK 0x00003f00
#define OHCI1394_IT_DATA_Q0_chanNum_SHIFT 8
#define OHCI1394_IT_DATA_Q0_tcode_MASK 0x000000f0
#define OHCI1394_IT_DATA_Q0_tcode_SHIFT 4
#define OHCI1394_IT_DATA_Q0_sy_MASK 0x0000000f
#define OHCI1394_IT_DATA_Q0_sy_SHIFT 0
#define OHCI1394_IT_DATA_Q1_dataLength_MASK 0xffff0000
#define OHCI1394_IT_DATA_Q1_dataLength_SHIFT 16
static inline unsigned int ohci1394_it_data_get_speed(const __le32 *data)
{
return (le32_to_cpu(data[0]) & OHCI1394_IT_DATA_Q0_spd_MASK) >> OHCI1394_IT_DATA_Q0_spd_SHIFT;
}
static inline void ohci1394_it_data_set_speed(__le32 *data, unsigned int scode)
{
data[0] &= cpu_to_le32(~OHCI1394_IT_DATA_Q0_spd_MASK);
data[0] |= cpu_to_le32((scode << OHCI1394_IT_DATA_Q0_spd_SHIFT) & OHCI1394_IT_DATA_Q0_spd_MASK);
}
static inline unsigned int ohci1394_it_data_get_tag(const __le32 *data)
{
return (le32_to_cpu(data[0]) & OHCI1394_IT_DATA_Q0_tag_MASK) >> OHCI1394_IT_DATA_Q0_tag_SHIFT;
}
static inline void ohci1394_it_data_set_tag(__le32 *data, unsigned int tag)
{
data[0] &= cpu_to_le32(~OHCI1394_IT_DATA_Q0_tag_MASK);
data[0] |= cpu_to_le32((tag << OHCI1394_IT_DATA_Q0_tag_SHIFT) & OHCI1394_IT_DATA_Q0_tag_MASK);
}
static inline unsigned int ohci1394_it_data_get_channel(const __le32 *data)
{
return (le32_to_cpu(data[0]) & OHCI1394_IT_DATA_Q0_chanNum_MASK) >> OHCI1394_IT_DATA_Q0_chanNum_SHIFT;
}
static inline void ohci1394_it_data_set_channel(__le32 *data, unsigned int channel)
{
data[0] &= cpu_to_le32(~OHCI1394_IT_DATA_Q0_chanNum_MASK);
data[0] |= cpu_to_le32((channel << OHCI1394_IT_DATA_Q0_chanNum_SHIFT) & OHCI1394_IT_DATA_Q0_chanNum_MASK);
}
static inline unsigned int ohci1394_it_data_get_tcode(const __le32 *data)
{
return (le32_to_cpu(data[0]) & OHCI1394_IT_DATA_Q0_tcode_MASK) >> OHCI1394_IT_DATA_Q0_tcode_SHIFT;
}
static inline void ohci1394_it_data_set_tcode(__le32 *data, unsigned int tcode)
{
data[0] &= cpu_to_le32(~OHCI1394_IT_DATA_Q0_tcode_MASK);
data[0] |= cpu_to_le32((tcode << OHCI1394_IT_DATA_Q0_tcode_SHIFT) & OHCI1394_IT_DATA_Q0_tcode_MASK);
}
static inline unsigned int ohci1394_it_data_get_sync(const __le32 *data)
{
return (le32_to_cpu(data[0]) & OHCI1394_IT_DATA_Q0_sy_MASK) >> OHCI1394_IT_DATA_Q0_sy_SHIFT;
}
static inline void ohci1394_it_data_set_sync(__le32 *data, unsigned int sync)
{
data[0] &= cpu_to_le32(~OHCI1394_IT_DATA_Q0_sy_MASK);
data[0] |= cpu_to_le32((sync << OHCI1394_IT_DATA_Q0_sy_SHIFT) & OHCI1394_IT_DATA_Q0_sy_MASK);
}
static inline unsigned int ohci1394_it_data_get_data_length(const __le32 *data)
{
return (le32_to_cpu(data[1]) & OHCI1394_IT_DATA_Q1_dataLength_MASK) >> OHCI1394_IT_DATA_Q1_dataLength_SHIFT;
}
static inline void ohci1394_it_data_set_data_length(__le32 *data, unsigned int data_length)
{
data[1] &= cpu_to_le32(~OHCI1394_IT_DATA_Q1_dataLength_MASK);
data[1] |= cpu_to_le32((data_length << OHCI1394_IT_DATA_Q1_dataLength_SHIFT) & OHCI1394_IT_DATA_Q1_dataLength_MASK);
}
// Self-ID DMA.
#define OHCI1394_SelfIDCount_selfIDError_MASK 0x80000000
#define OHCI1394_SelfIDCount_selfIDError_SHIFT 31
#define OHCI1394_SelfIDCount_selfIDGeneration_MASK 0x00ff0000
#define OHCI1394_SelfIDCount_selfIDGeneration_SHIFT 16
#define OHCI1394_SelfIDCount_selfIDSize_MASK 0x000007fc
#define OHCI1394_SelfIDCount_selfIDSize_SHIFT 2
static inline bool ohci1394_self_id_count_is_error(u32 value)
{
return !!((value & OHCI1394_SelfIDCount_selfIDError_MASK) >> OHCI1394_SelfIDCount_selfIDError_SHIFT);
}
static inline u8 ohci1394_self_id_count_get_generation(u32 value)
{
return (value & OHCI1394_SelfIDCount_selfIDGeneration_MASK) >> OHCI1394_SelfIDCount_selfIDGeneration_SHIFT;
}
// In 1394 OHCI specification, the maximum size of self ID stream is 504 quadlets
// (= 63 devices * 4 self ID packets * 2 quadlets). The selfIDSize field accommodates it and its
// additional first quadlet, since the field is 9 bits (0x1ff = 511).
static inline u32 ohci1394_self_id_count_get_size(u32 value)
{
return (value & OHCI1394_SelfIDCount_selfIDSize_MASK) >> OHCI1394_SelfIDCount_selfIDSize_SHIFT;
}
#define OHCI1394_SELF_ID_RECEIVE_Q0_GENERATION_MASK 0x00ff0000
#define OHCI1394_SELF_ID_RECEIVE_Q0_GENERATION_SHIFT 16
#define OHCI1394_SELF_ID_RECEIVE_Q0_TIMESTAMP_MASK 0x0000ffff
#define OHCI1394_SELF_ID_RECEIVE_Q0_TIMESTAMP_SHIFT 0
static inline u8 ohci1394_self_id_receive_q0_get_generation(u32 quadlet0)
{
return (quadlet0 & OHCI1394_SELF_ID_RECEIVE_Q0_GENERATION_MASK) >> OHCI1394_SELF_ID_RECEIVE_Q0_GENERATION_SHIFT;
}
static inline u16 ohci1394_self_id_receive_q0_get_timestamp(u32 quadlet0)
{
return (quadlet0 & OHCI1394_SELF_ID_RECEIVE_Q0_TIMESTAMP_MASK) >> OHCI1394_SELF_ID_RECEIVE_Q0_TIMESTAMP_SHIFT;
}
#endif /* _FIREWIRE_OHCI_H */
@@ -0,0 +1,236 @@
// 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
#include <linux/types.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
@@ -0,0 +1,917 @@
// 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"
#include "phy-packet-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 serialize_phy_packet_self_id_zero(u32 *quadlet, unsigned int packet_identifier,
unsigned int phy_id, bool extended,
bool link_is_active, unsigned int gap_count,
unsigned int scode, bool is_contender,
unsigned int power_class, bool is_initiated_reset,
bool has_more_packets)
{
phy_packet_set_packet_identifier(quadlet, packet_identifier);
phy_packet_self_id_set_phy_id(quadlet, phy_id);
phy_packet_self_id_set_extended(quadlet, extended);
phy_packet_self_id_zero_set_link_active(quadlet, link_is_active);
phy_packet_self_id_zero_set_gap_count(quadlet, gap_count);
phy_packet_self_id_zero_set_scode(quadlet, scode);
phy_packet_self_id_zero_set_contender(quadlet, is_contender);
phy_packet_self_id_zero_set_power_class(quadlet, power_class);
phy_packet_self_id_zero_set_initiated_reset(quadlet, is_initiated_reset);
phy_packet_self_id_set_more_packets(quadlet, has_more_packets);
}
static void deserialize_phy_packet_self_id_zero(u32 quadlet, unsigned int *packet_identifier,
unsigned int *phy_id, bool *extended,
bool *link_is_active, unsigned int *gap_count,
unsigned int *scode, bool *is_contender,
unsigned int *power_class,
bool *is_initiated_reset, bool *has_more_packets)
{
*packet_identifier = phy_packet_get_packet_identifier(quadlet);
*phy_id = phy_packet_self_id_get_phy_id(quadlet);
*extended = phy_packet_self_id_get_extended(quadlet);
*link_is_active = phy_packet_self_id_zero_get_link_active(quadlet);
*gap_count = phy_packet_self_id_zero_get_gap_count(quadlet);
*scode = phy_packet_self_id_zero_get_scode(quadlet);
*is_contender = phy_packet_self_id_zero_get_contender(quadlet);
*power_class = phy_packet_self_id_zero_get_power_class(quadlet);
*is_initiated_reset = phy_packet_self_id_zero_get_initiated_reset(quadlet);
*has_more_packets = phy_packet_self_id_get_more_packets(quadlet);
}
static void serialize_phy_packet_self_id_extended(u32 *quadlet, unsigned int packet_identifier,
unsigned int phy_id, bool extended,
unsigned int sequence, bool has_more_packets)
{
phy_packet_set_packet_identifier(quadlet, packet_identifier);
phy_packet_self_id_set_phy_id(quadlet, phy_id);
phy_packet_self_id_set_extended(quadlet, extended);
phy_packet_self_id_extended_set_sequence(quadlet, sequence);
phy_packet_self_id_set_more_packets(quadlet, has_more_packets);
}
static void deserialize_phy_packet_self_id_extended(u32 quadlet, unsigned int *packet_identifier,
unsigned int *phy_id, bool *extended,
unsigned int *sequence, bool *has_more_packets)
{
*packet_identifier = phy_packet_get_packet_identifier(quadlet);
*phy_id = phy_packet_self_id_get_phy_id(quadlet);
*extended = phy_packet_self_id_get_extended(quadlet);
*sequence = phy_packet_self_id_extended_get_sequence(quadlet);
*has_more_packets = phy_packet_self_id_get_more_packets(quadlet);
}
static void serialize_phy_packet_phy_config(u32 *quadlet, unsigned int packet_identifier,
unsigned int root_id, bool has_force_root_node,
bool has_gap_count_optimization, unsigned int gap_count)
{
phy_packet_set_packet_identifier(quadlet, packet_identifier);
phy_packet_phy_config_set_root_id(quadlet, root_id);
phy_packet_phy_config_set_force_root_node(quadlet, has_force_root_node);
phy_packet_phy_config_set_gap_count_optimization(quadlet, has_gap_count_optimization);
phy_packet_phy_config_set_gap_count(quadlet, gap_count);
}
static void deserialize_phy_packet_phy_config(u32 quadlet, unsigned int *packet_identifier,
unsigned int *root_id, bool *has_force_root_node,
bool *has_gap_count_optimization,
unsigned int *gap_count)
{
*packet_identifier = phy_packet_get_packet_identifier(quadlet);
*root_id = phy_packet_phy_config_get_root_id(quadlet);
*has_force_root_node = phy_packet_phy_config_get_force_root_node(quadlet);
*has_gap_count_optimization = phy_packet_phy_config_get_gap_count_optimization(quadlet);
*gap_count = phy_packet_phy_config_get_gap_count(quadlet);
}
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 void test_phy_packet_self_id_zero_case0(struct kunit *test)
{
// TSB41AB1/2 with 1 port.
const u32 expected[] = {0x80458c80};
u32 quadlets[] = {0};
unsigned int packet_identifier;
unsigned int phy_id;
bool extended;
bool link_is_active;
unsigned int gap_count;
unsigned int scode;
bool is_contender;
unsigned int power_class;
enum phy_packet_self_id_port_status port_status[3];
bool is_initiated_reset;
bool has_more_packets;
unsigned int port_index;
deserialize_phy_packet_self_id_zero(expected[0], &packet_identifier, &phy_id, &extended,
&link_is_active, &gap_count, &scode, &is_contender,
&power_class, &is_initiated_reset, &has_more_packets);
KUNIT_EXPECT_EQ(test, PHY_PACKET_PACKET_IDENTIFIER_SELF_ID, packet_identifier);
KUNIT_EXPECT_EQ(test, 0, phy_id);
KUNIT_EXPECT_FALSE(test, extended);
KUNIT_EXPECT_TRUE(test, link_is_active);
KUNIT_EXPECT_EQ(test, 0x05, gap_count);
KUNIT_EXPECT_EQ(test, SCODE_400, scode);
KUNIT_EXPECT_TRUE(test, is_contender);
KUNIT_EXPECT_EQ(test, 0x4, power_class);
KUNIT_EXPECT_FALSE(test, is_initiated_reset);
KUNIT_EXPECT_FALSE(test, has_more_packets);
serialize_phy_packet_self_id_zero(quadlets, packet_identifier, phy_id, extended,
link_is_active, gap_count, scode, is_contender,
power_class, is_initiated_reset, has_more_packets);
for (port_index = 0; port_index < ARRAY_SIZE(port_status); ++port_index) {
port_status[port_index] =
self_id_sequence_get_port_status(expected, ARRAY_SIZE(expected), port_index);
}
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_PARENT, port_status[0]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NONE, port_status[1]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NONE, port_status[2]);
for (port_index = 0; port_index < ARRAY_SIZE(port_status); ++port_index) {
self_id_sequence_set_port_status(quadlets, ARRAY_SIZE(quadlets), port_index,
port_status[port_index]);
}
KUNIT_EXPECT_MEMEQ(test, quadlets, expected, sizeof(expected));
}
static void test_phy_packet_self_id_zero_case1(struct kunit *test)
{
// XIO2213 and TSB81BA3E with 3 ports.
const u32 expected[] = {0x817fcc5e};
u32 quadlets[] = {0};
unsigned int packet_identifier;
unsigned int phy_id;
bool extended;
bool link_is_active;
unsigned int gap_count;
unsigned int scode;
bool is_contender;
unsigned int power_class;
enum phy_packet_self_id_port_status port_status[3];
bool is_initiated_reset;
bool has_more_packets;
unsigned int port_index;
deserialize_phy_packet_self_id_zero(expected[0], &packet_identifier, &phy_id, &extended,
&link_is_active, &gap_count, &scode, &is_contender,
&power_class, &is_initiated_reset, &has_more_packets);
KUNIT_EXPECT_EQ(test, PHY_PACKET_PACKET_IDENTIFIER_SELF_ID, packet_identifier);
KUNIT_EXPECT_EQ(test, 1, phy_id);
KUNIT_EXPECT_FALSE(test, extended);
KUNIT_EXPECT_TRUE(test, link_is_active);
KUNIT_EXPECT_EQ(test, 0x3f, gap_count);
KUNIT_EXPECT_EQ(test, SCODE_800, scode);
KUNIT_EXPECT_TRUE(test, is_contender);
KUNIT_EXPECT_EQ(test, 0x4, power_class);
KUNIT_EXPECT_TRUE(test, is_initiated_reset);
KUNIT_EXPECT_FALSE(test, has_more_packets);
serialize_phy_packet_self_id_zero(quadlets, packet_identifier, phy_id, extended,
link_is_active, gap_count, scode, is_contender,
power_class, is_initiated_reset, has_more_packets);
for (port_index = 0; port_index < ARRAY_SIZE(port_status); ++port_index) {
port_status[port_index] =
self_id_sequence_get_port_status(expected, ARRAY_SIZE(expected), port_index);
}
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[0]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[1]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_CHILD, port_status[2]);
for (port_index = 0; port_index < ARRAY_SIZE(port_status); ++port_index) {
self_id_sequence_set_port_status(quadlets, ARRAY_SIZE(quadlets), port_index,
port_status[port_index]);
}
KUNIT_EXPECT_MEMEQ(test, quadlets, expected, sizeof(expected));
}
static void test_phy_packet_self_id_zero_and_one(struct kunit *test)
{
// TSB41LV06A with 6 ports.
const u32 expected[] = {
0x803f8459,
0x80815000,
};
u32 quadlets[] = {0, 0};
unsigned int packet_identifier;
unsigned int phy_id;
bool extended;
bool link_is_active;
unsigned int gap_count;
unsigned int scode;
bool is_contender;
unsigned int power_class;
enum phy_packet_self_id_port_status port_status[11];
bool is_initiated_reset;
bool has_more_packets;
unsigned int sequence;
unsigned int port_index;
deserialize_phy_packet_self_id_zero(expected[0], &packet_identifier, &phy_id, &extended,
&link_is_active, &gap_count, &scode, &is_contender,
&power_class, &is_initiated_reset, &has_more_packets);
KUNIT_EXPECT_EQ(test, PHY_PACKET_PACKET_IDENTIFIER_SELF_ID, packet_identifier);
KUNIT_EXPECT_EQ(test, 0, phy_id);
KUNIT_EXPECT_FALSE(test, extended);
KUNIT_EXPECT_FALSE(test, link_is_active);
KUNIT_EXPECT_EQ(test, 0x3f, gap_count);
KUNIT_EXPECT_EQ(test, SCODE_400, scode);
KUNIT_EXPECT_FALSE(test, is_contender);
KUNIT_EXPECT_EQ(test, 0x4, power_class);
KUNIT_EXPECT_FALSE(test, is_initiated_reset);
KUNIT_EXPECT_TRUE(test, has_more_packets);
serialize_phy_packet_self_id_zero(quadlets, packet_identifier, phy_id, extended,
link_is_active, gap_count, scode, is_contender,
power_class, is_initiated_reset, has_more_packets);
deserialize_phy_packet_self_id_extended(expected[1], &packet_identifier, &phy_id, &extended,
&sequence, &has_more_packets);
KUNIT_EXPECT_EQ(test, PHY_PACKET_PACKET_IDENTIFIER_SELF_ID, packet_identifier);
KUNIT_EXPECT_EQ(test, 0, phy_id);
KUNIT_EXPECT_TRUE(test, extended);
KUNIT_EXPECT_EQ(test, 0, sequence);
KUNIT_EXPECT_FALSE(test, has_more_packets);
serialize_phy_packet_self_id_extended(&quadlets[1], packet_identifier, phy_id, extended,
sequence, has_more_packets);
for (port_index = 0; port_index < ARRAY_SIZE(port_status); ++port_index) {
port_status[port_index] =
self_id_sequence_get_port_status(expected, ARRAY_SIZE(expected), port_index);
}
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[0]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[1]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_PARENT, port_status[2]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[3]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[4]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[5]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NONE, port_status[6]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NONE, port_status[7]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NONE, port_status[8]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NONE, port_status[9]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NONE, port_status[10]);
for (port_index = 0; port_index < ARRAY_SIZE(port_status); ++port_index) {
self_id_sequence_set_port_status(quadlets, ARRAY_SIZE(quadlets), port_index,
port_status[port_index]);
}
KUNIT_EXPECT_MEMEQ(test, quadlets, expected, sizeof(expected));
}
static void test_phy_packet_phy_config_force_root_node(struct kunit *test)
{
const u32 expected = 0x02800000;
u32 quadlet = 0;
unsigned int packet_identifier;
unsigned int root_id;
bool has_force_root_node;
bool has_gap_count_optimization;
unsigned int gap_count;
deserialize_phy_packet_phy_config(expected, &packet_identifier, &root_id,
&has_force_root_node, &has_gap_count_optimization,
&gap_count);
KUNIT_EXPECT_EQ(test, PHY_PACKET_PACKET_IDENTIFIER_PHY_CONFIG, packet_identifier);
KUNIT_EXPECT_EQ(test, 0x02, root_id);
KUNIT_EXPECT_TRUE(test, has_force_root_node);
KUNIT_EXPECT_FALSE(test, has_gap_count_optimization);
KUNIT_EXPECT_EQ(test, 0, gap_count);
serialize_phy_packet_phy_config(&quadlet, packet_identifier, root_id, has_force_root_node,
has_gap_count_optimization, gap_count);
KUNIT_EXPECT_EQ(test, quadlet, expected);
}
static void test_phy_packet_phy_config_gap_count_optimization(struct kunit *test)
{
const u32 expected = 0x034f0000;
u32 quadlet = 0;
unsigned int packet_identifier;
unsigned int root_id;
bool has_force_root_node;
bool has_gap_count_optimization;
unsigned int gap_count;
deserialize_phy_packet_phy_config(expected, &packet_identifier, &root_id,
&has_force_root_node, &has_gap_count_optimization,
&gap_count);
KUNIT_EXPECT_EQ(test, PHY_PACKET_PACKET_IDENTIFIER_PHY_CONFIG, packet_identifier);
KUNIT_EXPECT_EQ(test, 0x03, root_id);
KUNIT_EXPECT_FALSE(test, has_force_root_node);
KUNIT_EXPECT_TRUE(test, has_gap_count_optimization);
KUNIT_EXPECT_EQ(test, 0x0f, gap_count);
serialize_phy_packet_phy_config(&quadlet, packet_identifier, root_id, has_force_root_node,
has_gap_count_optimization, gap_count);
KUNIT_EXPECT_EQ(test, quadlet, 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),
KUNIT_CASE(test_phy_packet_self_id_zero_case0),
KUNIT_CASE(test_phy_packet_self_id_zero_case1),
KUNIT_CASE(test_phy_packet_self_id_zero_and_one),
KUNIT_CASE(test_phy_packet_phy_config_force_root_node),
KUNIT_CASE(test_phy_packet_phy_config_gap_count_optimization),
{}
};
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_DESCRIPTION("FireWire packet serialization/deserialization unit test suite");
MODULE_LICENSE("GPL");
@@ -0,0 +1,302 @@
// SPDX-License-Identifier: GPL-2.0-or-later
//
// phy-packet-definitions.h - The definitions of phy packet for IEEE 1394.
//
// Copyright (c) 2024 Takashi Sakamoto
#ifndef _FIREWIRE_PHY_PACKET_DEFINITIONS_H
#define _FIREWIRE_PHY_PACKET_DEFINITIONS_H
#define PACKET_IDENTIFIER_MASK 0xc0000000
#define PACKET_IDENTIFIER_SHIFT 30
static inline unsigned int phy_packet_get_packet_identifier(u32 quadlet)
{
return (quadlet & PACKET_IDENTIFIER_MASK) >> PACKET_IDENTIFIER_SHIFT;
}
static inline void phy_packet_set_packet_identifier(u32 *quadlet, unsigned int packet_identifier)
{
*quadlet &= ~PACKET_IDENTIFIER_MASK;
*quadlet |= (packet_identifier << PACKET_IDENTIFIER_SHIFT) & PACKET_IDENTIFIER_MASK;
}
#define PHY_PACKET_PACKET_IDENTIFIER_PHY_CONFIG 0
#define PHY_CONFIG_ROOT_ID_MASK 0x3f000000
#define PHY_CONFIG_ROOT_ID_SHIFT 24
#define PHY_CONFIG_FORCE_ROOT_NODE_MASK 0x00800000
#define PHY_CONFIG_FORCE_ROOT_NODE_SHIFT 23
#define PHY_CONFIG_GAP_COUNT_OPTIMIZATION_MASK 0x00400000
#define PHY_CONFIG_GAP_COUNT_OPTIMIZATION_SHIFT 22
#define PHY_CONFIG_GAP_COUNT_MASK 0x003f0000
#define PHY_CONFIG_GAP_COUNT_SHIFT 16
static inline unsigned int phy_packet_phy_config_get_root_id(u32 quadlet)
{
return (quadlet & PHY_CONFIG_ROOT_ID_MASK) >> PHY_CONFIG_ROOT_ID_SHIFT;
}
static inline void phy_packet_phy_config_set_root_id(u32 *quadlet, unsigned int root_id)
{
*quadlet &= ~PHY_CONFIG_ROOT_ID_MASK;
*quadlet |= (root_id << PHY_CONFIG_ROOT_ID_SHIFT) & PHY_CONFIG_ROOT_ID_MASK;
}
static inline bool phy_packet_phy_config_get_force_root_node(u32 quadlet)
{
return (quadlet & PHY_CONFIG_FORCE_ROOT_NODE_MASK) >> PHY_CONFIG_FORCE_ROOT_NODE_SHIFT;
}
static inline void phy_packet_phy_config_set_force_root_node(u32 *quadlet, bool has_force_root_node)
{
*quadlet &= ~PHY_CONFIG_FORCE_ROOT_NODE_MASK;
*quadlet |= (has_force_root_node << PHY_CONFIG_FORCE_ROOT_NODE_SHIFT) & PHY_CONFIG_FORCE_ROOT_NODE_MASK;
}
static inline bool phy_packet_phy_config_get_gap_count_optimization(u32 quadlet)
{
return (quadlet & PHY_CONFIG_GAP_COUNT_OPTIMIZATION_MASK) >> PHY_CONFIG_GAP_COUNT_OPTIMIZATION_SHIFT;
}
static inline void phy_packet_phy_config_set_gap_count_optimization(u32 *quadlet, bool has_gap_count_optimization)
{
*quadlet &= ~PHY_CONFIG_GAP_COUNT_OPTIMIZATION_MASK;
*quadlet |= (has_gap_count_optimization << PHY_CONFIG_GAP_COUNT_OPTIMIZATION_SHIFT) & PHY_CONFIG_GAP_COUNT_OPTIMIZATION_MASK;
}
static inline unsigned int phy_packet_phy_config_get_gap_count(u32 quadlet)
{
return (quadlet & PHY_CONFIG_GAP_COUNT_MASK) >> PHY_CONFIG_GAP_COUNT_SHIFT;
}
static inline void phy_packet_phy_config_set_gap_count(u32 *quadlet, unsigned int gap_count)
{
*quadlet &= ~PHY_CONFIG_GAP_COUNT_MASK;
*quadlet |= (gap_count << PHY_CONFIG_GAP_COUNT_SHIFT) & PHY_CONFIG_GAP_COUNT_MASK;
}
#define PHY_PACKET_PACKET_IDENTIFIER_SELF_ID 2
#define SELF_ID_PHY_ID_MASK 0x3f000000
#define SELF_ID_PHY_ID_SHIFT 24
#define SELF_ID_EXTENDED_MASK 0x00800000
#define SELF_ID_EXTENDED_SHIFT 23
#define SELF_ID_MORE_PACKETS_MASK 0x00000001
#define SELF_ID_MORE_PACKETS_SHIFT 0
#define SELF_ID_ZERO_LINK_ACTIVE_MASK 0x00400000
#define SELF_ID_ZERO_LINK_ACTIVE_SHIFT 22
#define SELF_ID_ZERO_GAP_COUNT_MASK 0x003f0000
#define SELF_ID_ZERO_GAP_COUNT_SHIFT 16
#define SELF_ID_ZERO_SCODE_MASK 0x0000c000
#define SELF_ID_ZERO_SCODE_SHIFT 14
#define SELF_ID_ZERO_CONTENDER_MASK 0x00000800
#define SELF_ID_ZERO_CONTENDER_SHIFT 11
#define SELF_ID_ZERO_POWER_CLASS_MASK 0x00000700
#define SELF_ID_ZERO_POWER_CLASS_SHIFT 8
#define SELF_ID_ZERO_INITIATED_RESET_MASK 0x00000002
#define SELF_ID_ZERO_INITIATED_RESET_SHIFT 1
#define SELF_ID_EXTENDED_SEQUENCE_MASK 0x00700000
#define SELF_ID_EXTENDED_SEQUENCE_SHIFT 20
#define SELF_ID_PORT_STATUS_MASK 0x3
#define SELF_ID_SEQUENCE_MAXIMUM_QUADLET_COUNT 4
static inline unsigned int phy_packet_self_id_get_phy_id(u32 quadlet)
{
return (quadlet & SELF_ID_PHY_ID_MASK) >> SELF_ID_PHY_ID_SHIFT;
}
static inline void phy_packet_self_id_set_phy_id(u32 *quadlet, unsigned int phy_id)
{
*quadlet &= ~SELF_ID_PHY_ID_MASK;
*quadlet |= (phy_id << SELF_ID_PHY_ID_SHIFT) & SELF_ID_PHY_ID_MASK;
}
static inline bool phy_packet_self_id_get_extended(u32 quadlet)
{
return (quadlet & SELF_ID_EXTENDED_MASK) >> SELF_ID_EXTENDED_SHIFT;
}
static inline void phy_packet_self_id_set_extended(u32 *quadlet, bool extended)
{
*quadlet &= ~SELF_ID_EXTENDED_MASK;
*quadlet |= (extended << SELF_ID_EXTENDED_SHIFT) & SELF_ID_EXTENDED_MASK;
}
static inline bool phy_packet_self_id_zero_get_link_active(u32 quadlet)
{
return (quadlet & SELF_ID_ZERO_LINK_ACTIVE_MASK) >> SELF_ID_ZERO_LINK_ACTIVE_SHIFT;
}
static inline void phy_packet_self_id_zero_set_link_active(u32 *quadlet, bool is_active)
{
*quadlet &= ~SELF_ID_ZERO_LINK_ACTIVE_MASK;
*quadlet |= (is_active << SELF_ID_ZERO_LINK_ACTIVE_SHIFT) & SELF_ID_ZERO_LINK_ACTIVE_MASK;
}
static inline unsigned int phy_packet_self_id_zero_get_gap_count(u32 quadlet)
{
return (quadlet & SELF_ID_ZERO_GAP_COUNT_MASK) >> SELF_ID_ZERO_GAP_COUNT_SHIFT;
}
static inline void phy_packet_self_id_zero_set_gap_count(u32 *quadlet, unsigned int gap_count)
{
*quadlet &= ~SELF_ID_ZERO_GAP_COUNT_MASK;
*quadlet |= (gap_count << SELF_ID_ZERO_GAP_COUNT_SHIFT) & SELF_ID_ZERO_GAP_COUNT_MASK;
}
static inline unsigned int phy_packet_self_id_zero_get_scode(u32 quadlet)
{
return (quadlet & SELF_ID_ZERO_SCODE_MASK) >> SELF_ID_ZERO_SCODE_SHIFT;
}
static inline void phy_packet_self_id_zero_set_scode(u32 *quadlet, unsigned int speed)
{
*quadlet &= ~SELF_ID_ZERO_SCODE_MASK;
*quadlet |= (speed << SELF_ID_ZERO_SCODE_SHIFT) & SELF_ID_ZERO_SCODE_MASK;
}
static inline bool phy_packet_self_id_zero_get_contender(u32 quadlet)
{
return (quadlet & SELF_ID_ZERO_CONTENDER_MASK) >> SELF_ID_ZERO_CONTENDER_SHIFT;
}
static inline void phy_packet_self_id_zero_set_contender(u32 *quadlet, bool is_contender)
{
*quadlet &= ~SELF_ID_ZERO_CONTENDER_MASK;
*quadlet |= (is_contender << SELF_ID_ZERO_CONTENDER_SHIFT) & SELF_ID_ZERO_CONTENDER_MASK;
}
static inline unsigned int phy_packet_self_id_zero_get_power_class(u32 quadlet)
{
return (quadlet & SELF_ID_ZERO_POWER_CLASS_MASK) >> SELF_ID_ZERO_POWER_CLASS_SHIFT;
}
static inline void phy_packet_self_id_zero_set_power_class(u32 *quadlet, unsigned int power_class)
{
*quadlet &= ~SELF_ID_ZERO_POWER_CLASS_MASK;
*quadlet |= (power_class << SELF_ID_ZERO_POWER_CLASS_SHIFT) & SELF_ID_ZERO_POWER_CLASS_MASK;
}
static inline bool phy_packet_self_id_zero_get_initiated_reset(u32 quadlet)
{
return (quadlet & SELF_ID_ZERO_INITIATED_RESET_MASK) >> SELF_ID_ZERO_INITIATED_RESET_SHIFT;
}
static inline void phy_packet_self_id_zero_set_initiated_reset(u32 *quadlet, bool is_initiated_reset)
{
*quadlet &= ~SELF_ID_ZERO_INITIATED_RESET_MASK;
*quadlet |= (is_initiated_reset << SELF_ID_ZERO_INITIATED_RESET_SHIFT) & SELF_ID_ZERO_INITIATED_RESET_MASK;
}
static inline bool phy_packet_self_id_get_more_packets(u32 quadlet)
{
return (quadlet & SELF_ID_MORE_PACKETS_MASK) >> SELF_ID_MORE_PACKETS_SHIFT;
}
static inline void phy_packet_self_id_set_more_packets(u32 *quadlet, bool is_more_packets)
{
*quadlet &= ~SELF_ID_MORE_PACKETS_MASK;
*quadlet |= (is_more_packets << SELF_ID_MORE_PACKETS_SHIFT) & SELF_ID_MORE_PACKETS_MASK;
}
static inline unsigned int phy_packet_self_id_extended_get_sequence(u32 quadlet)
{
return (quadlet & SELF_ID_EXTENDED_SEQUENCE_MASK) >> SELF_ID_EXTENDED_SEQUENCE_SHIFT;
}
static inline void phy_packet_self_id_extended_set_sequence(u32 *quadlet, unsigned int sequence)
{
*quadlet &= ~SELF_ID_EXTENDED_SEQUENCE_MASK;
*quadlet |= (sequence << SELF_ID_EXTENDED_SHIFT) & SELF_ID_EXTENDED_SEQUENCE_MASK;
}
struct self_id_sequence_enumerator {
const u32 *cursor;
unsigned int quadlet_count;
};
static inline const u32 *self_id_sequence_enumerator_next(
struct self_id_sequence_enumerator *enumerator, unsigned int *quadlet_count)
{
const u32 *self_id_sequence, *cursor;
u32 quadlet;
unsigned int count;
unsigned int sequence;
if (enumerator->cursor == NULL || enumerator->quadlet_count == 0)
return ERR_PTR(-ENODATA);
cursor = enumerator->cursor;
count = 1;
quadlet = *cursor;
sequence = 0;
while (phy_packet_self_id_get_more_packets(quadlet)) {
if (count >= enumerator->quadlet_count ||
count >= SELF_ID_SEQUENCE_MAXIMUM_QUADLET_COUNT)
return ERR_PTR(-EPROTO);
++cursor;
++count;
quadlet = *cursor;
if (!phy_packet_self_id_get_extended(quadlet) ||
sequence != phy_packet_self_id_extended_get_sequence(quadlet))
return ERR_PTR(-EPROTO);
++sequence;
}
*quadlet_count = count;
self_id_sequence = enumerator->cursor;
enumerator->cursor += count;
enumerator->quadlet_count -= count;
return self_id_sequence;
}
enum phy_packet_self_id_port_status {
PHY_PACKET_SELF_ID_PORT_STATUS_NONE = 0,
PHY_PACKET_SELF_ID_PORT_STATUS_NCONN = 1,
PHY_PACKET_SELF_ID_PORT_STATUS_PARENT = 2,
PHY_PACKET_SELF_ID_PORT_STATUS_CHILD = 3,
};
static inline unsigned int self_id_sequence_get_port_capacity(unsigned int quadlet_count)
{
return quadlet_count * 8 - 5;
}
static inline enum phy_packet_self_id_port_status self_id_sequence_get_port_status(
const u32 *self_id_sequence, unsigned int quadlet_count, unsigned int port_index)
{
unsigned int index, shift;
index = (port_index + 5) / 8;
shift = 16 - ((port_index + 5) % 8) * 2;
if (index < quadlet_count && index < SELF_ID_SEQUENCE_MAXIMUM_QUADLET_COUNT)
return (self_id_sequence[index] >> shift) & SELF_ID_PORT_STATUS_MASK;
return PHY_PACKET_SELF_ID_PORT_STATUS_NONE;
}
static inline void self_id_sequence_set_port_status(u32 *self_id_sequence, unsigned int quadlet_count,
unsigned int port_index,
enum phy_packet_self_id_port_status status)
{
unsigned int index, shift;
index = (port_index + 5) / 8;
shift = 16 - ((port_index + 5) % 8) * 2;
if (index < quadlet_count) {
self_id_sequence[index] &= ~(SELF_ID_PORT_STATUS_MASK << shift);
self_id_sequence[index] |= status << shift;
}
}
#endif // _FIREWIRE_PHY_PACKET_DEFINITIONS_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,152 @@
// SPDX-License-Identifier: GPL-2.0-or-later
//
// self-id-sequence-helper-test.c - An application of Kunit to test helpers of self ID sequence.
//
// Copyright (c) 2024 Takashi Sakamoto
#include <kunit/test.h>
#include "phy-packet-definitions.h"
static void test_self_id_sequence_enumerator_valid(struct kunit *test)
{
static const u32 valid_sequences[] = {
0x00000000,
0x00000001, 0x00800000,
0x00000001, 0x00800001, 0x00900000,
0x00000000,
};
struct self_id_sequence_enumerator enumerator;
const u32 *entry;
unsigned int quadlet_count;
enumerator.cursor = valid_sequences;
enumerator.quadlet_count = ARRAY_SIZE(valid_sequences);
entry = self_id_sequence_enumerator_next(&enumerator, &quadlet_count);
KUNIT_EXPECT_PTR_EQ(test, entry, &valid_sequences[0]);
KUNIT_EXPECT_EQ(test, quadlet_count, 1);
KUNIT_EXPECT_EQ(test, enumerator.quadlet_count, 6);
entry = self_id_sequence_enumerator_next(&enumerator, &quadlet_count);
KUNIT_EXPECT_PTR_EQ(test, entry, &valid_sequences[1]);
KUNIT_EXPECT_EQ(test, quadlet_count, 2);
KUNIT_EXPECT_EQ(test, enumerator.quadlet_count, 4);
entry = self_id_sequence_enumerator_next(&enumerator, &quadlet_count);
KUNIT_EXPECT_PTR_EQ(test, entry, &valid_sequences[3]);
KUNIT_EXPECT_EQ(test, quadlet_count, 3);
KUNIT_EXPECT_EQ(test, enumerator.quadlet_count, 1);
entry = self_id_sequence_enumerator_next(&enumerator, &quadlet_count);
KUNIT_EXPECT_PTR_EQ(test, entry, &valid_sequences[6]);
KUNIT_EXPECT_EQ(test, quadlet_count, 1);
KUNIT_EXPECT_EQ(test, enumerator.quadlet_count, 0);
entry = self_id_sequence_enumerator_next(&enumerator, &quadlet_count);
KUNIT_EXPECT_EQ(test, PTR_ERR(entry), -ENODATA);
}
static void test_self_id_sequence_enumerator_invalid(struct kunit *test)
{
static const u32 invalid_sequences[] = {
0x00000001,
};
struct self_id_sequence_enumerator enumerator;
const u32 *entry;
unsigned int count;
enumerator.cursor = invalid_sequences;
enumerator.quadlet_count = ARRAY_SIZE(invalid_sequences);
entry = self_id_sequence_enumerator_next(&enumerator, &count);
KUNIT_EXPECT_EQ(test, PTR_ERR(entry), -EPROTO);
}
static void test_self_id_sequence_get_port_status(struct kunit *test)
{
static const u32 expected[] = {
0x000000e5,
0x00839e79,
0x0091e79d,
0x00a279e4,
};
u32 quadlets [] = {
0x00000001,
0x00800001,
0x00900001,
0x00a00000,
};
enum phy_packet_self_id_port_status port_status[28];
unsigned int port_capacity;
unsigned int port_index;
KUNIT_ASSERT_EQ(test, ARRAY_SIZE(expected), ARRAY_SIZE(quadlets));
// With an extra port.
port_capacity = self_id_sequence_get_port_capacity(ARRAY_SIZE(expected)) + 1;
KUNIT_ASSERT_EQ(test, port_capacity, ARRAY_SIZE(port_status));
for (port_index = 0; port_index < port_capacity; ++port_index) {
port_status[port_index] =
self_id_sequence_get_port_status(expected, ARRAY_SIZE(expected), port_index);
self_id_sequence_set_port_status(quadlets, ARRAY_SIZE(quadlets), port_index,
port_status[port_index]);
}
// Self ID zero.
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_CHILD, port_status[0]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_PARENT, port_status[1]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[2]);
// Self ID one.
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_CHILD, port_status[3]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_PARENT, port_status[4]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[5]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_CHILD, port_status[6]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_PARENT, port_status[7]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[8]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_CHILD, port_status[9]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_PARENT, port_status[10]);
// Self ID two.
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[11]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_CHILD, port_status[12]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_PARENT, port_status[13]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[14]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_CHILD, port_status[15]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_PARENT, port_status[16]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[17]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_CHILD, port_status[18]);
// Self ID three.
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_PARENT, port_status[19]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[20]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_CHILD, port_status[21]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_PARENT, port_status[22]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[23]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_CHILD, port_status[24]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_PARENT, port_status[25]);
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NCONN, port_status[26]);
// Our of order.
KUNIT_EXPECT_EQ(test, PHY_PACKET_SELF_ID_PORT_STATUS_NONE, port_status[27]);
KUNIT_EXPECT_MEMEQ(test, quadlets, expected, sizeof(expected));
}
static struct kunit_case self_id_sequence_helper_test_cases[] = {
KUNIT_CASE(test_self_id_sequence_enumerator_valid),
KUNIT_CASE(test_self_id_sequence_enumerator_invalid),
KUNIT_CASE(test_self_id_sequence_get_port_status),
{}
};
static struct kunit_suite self_id_sequence_helper_test_suite = {
.name = "self-id-sequence-helper",
.test_cases = self_id_sequence_helper_test_cases,
};
kunit_test_suite(self_id_sequence_helper_test_suite);
MODULE_DESCRIPTION("Unit test suite for helpers of self ID sequence");
MODULE_LICENSE("GPL");
+90
View File
@@ -0,0 +1,90 @@
// 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_DESCRIPTION("FireWire UAPI unit test suite");
MODULE_LICENSE("GPL");