Skip to content

Commit 15f9610

Browse files
committed
firewire: core: handle device quirk of MOTU Audio Express
A commit 3a93d08 ("ALSA: firewire-motu: add support for MOTU Audio Express") describes a quirk of MOTU Audio Express. The device returns acknowledge packet with 0x10 as the pending state of any types of asynchronous request transaction. It is completely out of specification. This commit implements handling for that device-specific quirk. The quirk is detected after reading the root directory of configuration ROM. When processing the acknowledge code in 1394 OHCI AT context event handler, firewire-ohci module seeks the device instance of destination node by traversing device hierarchy. If the device has the quirk, the acknowledge code is replaced with the standard code. The 1394 OHCI AT context events occur for outgoing asynchronous request packets. The device traversal is safe since no new request initiators exist after the fw_card_instance has been invalidated. Link: https://lore.kernel.org/r/20251013140311.97159-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
1 parent 5a43dc9 commit 15f9610

3 files changed

Lines changed: 85 additions & 0 deletions

File tree

drivers/firewire/core-device.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,54 @@ static int detect_quirks_by_bus_information_block(const u32 *bus_information_blo
557557
return quirks;
558558
}
559559

560+
struct entry_match {
561+
unsigned int index;
562+
u32 value;
563+
};
564+
565+
static const struct entry_match motu_audio_express_matches[] = {
566+
{ 1, 0x030001f2 },
567+
{ 3, 0xd1000002 },
568+
{ 4, 0x8d000005 },
569+
{ 6, 0x120001f2 },
570+
{ 7, 0x13000033 },
571+
{ 8, 0x17104800 },
572+
};
573+
574+
static int detect_quirks_by_root_directory(const u32 *root_directory, unsigned int length)
575+
{
576+
static const struct {
577+
enum fw_device_quirk quirk;
578+
const struct entry_match *matches;
579+
unsigned int match_count;
580+
} *entry, entries[] = {
581+
{
582+
.quirk = FW_DEVICE_QUIRK_ACK_PACKET_WITH_INVALID_PENDING_CODE,
583+
.matches = motu_audio_express_matches,
584+
.match_count = ARRAY_SIZE(motu_audio_express_matches),
585+
},
586+
};
587+
int quirks = 0;
588+
int i;
589+
590+
for (i = 0; i < ARRAY_SIZE(entries); ++i) {
591+
int j;
592+
593+
entry = entries + i;
594+
for (j = 0; j < entry->match_count; ++j) {
595+
unsigned int index = entry->matches[j].index;
596+
unsigned int value = entry->matches[j].value;
597+
598+
if ((length < index) || (root_directory[index] != value))
599+
break;
600+
}
601+
if (j == entry->match_count)
602+
quirks |= entry->quirk;
603+
}
604+
605+
return quirks;
606+
}
607+
560608
static int read_rom(struct fw_device *device,
561609
int generation, int index, u32 *data)
562610
{
@@ -737,6 +785,11 @@ static int read_config_rom(struct fw_device *device, int generation)
737785
length = i;
738786
}
739787

788+
quirks |= detect_quirks_by_root_directory(rom + ROOT_DIR_OFFSET, length - ROOT_DIR_OFFSET);
789+
790+
// Just prevent from torn writing/reading.
791+
WRITE_ONCE(device->quirks, quirks);
792+
740793
old_rom = device->config_rom;
741794
new_rom = kmemdup(rom, length * 4, GFP_KERNEL);
742795
if (new_rom == NULL) {

drivers/firewire/ohci.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,6 +1319,14 @@ static void at_context_flush(struct at_context *ctx)
13191319
enable_work(&ctx->work);
13201320
}
13211321

1322+
static int find_fw_device(struct device *dev, const void *data)
1323+
{
1324+
struct fw_device *device = fw_device(dev);
1325+
const u32 *params = data;
1326+
1327+
return (device->generation == params[0]) && (device->node_id == params[1]);
1328+
}
1329+
13221330
static int handle_at_packet(struct context *context,
13231331
struct descriptor *d,
13241332
struct descriptor *last)
@@ -1390,6 +1398,27 @@ static int handle_at_packet(struct context *context,
13901398
fallthrough;
13911399

13921400
default:
1401+
if (unlikely(evt == 0x10)) {
1402+
u32 params[2] = {
1403+
packet->generation,
1404+
async_header_get_destination(packet->header),
1405+
};
1406+
struct device *dev;
1407+
1408+
fw_card_get(&ohci->card);
1409+
dev = device_find_child(ohci->card.device, (const void *)params, find_fw_device);
1410+
fw_card_put(&ohci->card);
1411+
if (dev) {
1412+
struct fw_device *device = fw_device(dev);
1413+
int quirks = READ_ONCE(device->quirks);
1414+
1415+
put_device(dev);
1416+
if (quirks & FW_DEVICE_QUIRK_ACK_PACKET_WITH_INVALID_PENDING_CODE) {
1417+
packet->ack = ACK_PENDING;
1418+
break;
1419+
}
1420+
}
1421+
}
13931422
packet->ack = RCODE_SEND_ERROR;
13941423
break;
13951424
}

include/linux/firewire.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ enum fw_device_quirk {
176176

177177
// See a509e43ff338 ("firewire: core: fix unstable I/O with Canon camcorder").
178178
FW_DEVICE_QUIRK_IRM_IGNORES_BUS_MANAGER = BIT(1),
179+
180+
// MOTU Audio Express transfers acknowledge packet with 0x10 for pending state.
181+
FW_DEVICE_QUIRK_ACK_PACKET_WITH_INVALID_PENDING_CODE = BIT(2),
179182
};
180183

181184
enum fw_device_state {

0 commit comments

Comments
 (0)