Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firewire: core: handle device quirk of MOTU Audio Express

A commit 3a93d082bacf ("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>

+85
+53
drivers/firewire/core-device.c
··· 557 557 return quirks; 558 558 } 559 559 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 + 560 608 static int read_rom(struct fw_device *device, 561 609 int generation, int index, u32 *data) 562 610 { ··· 784 736 if (length < i) 785 737 length = i; 786 738 } 739 + 740 + quirks |= detect_quirks_by_root_directory(rom + ROOT_DIR_OFFSET, length - ROOT_DIR_OFFSET); 741 + 742 + // Just prevent from torn writing/reading. 743 + WRITE_ONCE(device->quirks, quirks); 787 744 788 745 old_rom = device->config_rom; 789 746 new_rom = kmemdup(rom, length * 4, GFP_KERNEL);
+29
drivers/firewire/ohci.c
··· 1319 1319 enable_work(&ctx->work); 1320 1320 } 1321 1321 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 + 1322 1330 static int handle_at_packet(struct context *context, 1323 1331 struct descriptor *d, 1324 1332 struct descriptor *last) ··· 1398 1390 fallthrough; 1399 1391 1400 1392 default: 1393 + if (unlikely(evt == 0x10)) { 1394 + u32 params[2] = { 1395 + packet->generation, 1396 + async_header_get_destination(packet->header), 1397 + }; 1398 + struct device *dev; 1399 + 1400 + fw_card_get(&ohci->card); 1401 + dev = device_find_child(ohci->card.device, (const void *)params, find_fw_device); 1402 + fw_card_put(&ohci->card); 1403 + if (dev) { 1404 + struct fw_device *device = fw_device(dev); 1405 + int quirks = READ_ONCE(device->quirks); 1406 + 1407 + put_device(dev); 1408 + if (quirks & FW_DEVICE_QUIRK_ACK_PACKET_WITH_INVALID_PENDING_CODE) { 1409 + packet->ack = ACK_PENDING; 1410 + break; 1411 + } 1412 + } 1413 + } 1401 1414 packet->ack = RCODE_SEND_ERROR; 1402 1415 break; 1403 1416 }
+3
include/linux/firewire.h
··· 176 176 177 177 // See a509e43ff338 ("firewire: core: fix unstable I/O with Canon camcorder"). 178 178 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), 179 182 }; 180 183 181 184 enum fw_device_state {