// SPDX-License-Identifier: GPL-2.0-only // // motu-command-dsp-message-parser.c - a part of driver for MOTU FireWire series // // Copyright (c) 2021 Takashi Sakamoto // Below models allow software to configure their DSP function by command transferred in // asynchronous transaction: // * 828 mk3 (FireWire only and Hybrid) // * 896 mk3 (FireWire only and Hybrid) // * Ultralite mk3 (FireWire only and Hybrid) // * Traveler mk3 // * Track 16 // // Isochronous packets from the above models includes messages to report state of hardware meter. #include "motu.h" enum msg_parser_state { INITIALIZED, FRAGMENT_DETECTED, AVAILABLE, }; struct msg_parser { spinlock_t lock; enum msg_parser_state state; unsigned int interval; unsigned int message_count; unsigned int fragment_pos; unsigned int value_index; u64 value; struct snd_firewire_motu_command_dsp_meter meter; }; int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu) { struct msg_parser *parser; parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL); if (!parser) return -ENOMEM; spin_lock_init(&parser->lock); motu->message_parser = parser; return 0; } int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc) { struct msg_parser *parser = motu->message_parser; parser->state = INITIALIZED; // All of data blocks don't have messages with meaningful information. switch (sfc) { case CIP_SFC_176400: case CIP_SFC_192000: parser->interval = 4; break; case CIP_SFC_88200: case CIP_SFC_96000: parser->interval = 2; break; case CIP_SFC_32000: case CIP_SFC_44100: case CIP_SFC_48000: default: parser->interval = 1; break; } return 0; } #define FRAGMENT_POS 6 #define MIDI_BYTE_POS 7 #define MIDI_FLAG_POS 8 // One value of hardware meter consists of 4 messages. #define FRAGMENTS_PER_VALUE 4 #define VALUES_AT_IMAGE_END 0xffffffffffffffff void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs, unsigned int desc_count, unsigned int data_block_quadlets) { struct msg_parser *parser = motu->message_parser; unsigned int interval = parser->interval; unsigned long flags; int i; spin_lock_irqsave(&parser->lock, flags); for (i = 0; i < desc_count; ++i) { const struct pkt_desc *desc = descs + i; __be32 *buffer = desc->ctx_payload; unsigned int data_blocks = desc->data_blocks; int j; for (j = 0; j < data_blocks; ++j) { u8 *b = (u8 *)buffer; buffer += data_block_quadlets; switch (parser->state) { case INITIALIZED: { u8 fragment = b[FRAGMENT_POS]; if (fragment > 0) { parser->value = fragment; parser->message_count = 1; parser->state = FRAGMENT_DETECTED; } break; } case FRAGMENT_DETECTED: { if (parser->message_count % interval == 0) { u8 fragment = b[FRAGMENT_POS]; parser->value >>= 8; parser->value |= (u64)fragment << 56; if (parser->value == VALUES_AT_IMAGE_END) { parser->state = AVAILABLE; parser->fragment_pos = 0; parser->value_index = 0; parser->message_count = 0; } } ++parser->message_count; break; } case AVAILABLE: default: { if (parser->message_count % interval == 0) { u8 fragment = b[FRAGMENT_POS]; parser->value >>= 8; parser->value |= (u64)fragment << 56; ++parser->fragment_pos; if (parser->fragment_pos == 4) { // Skip the last two quadlets since they could be // invalid value (0xffffffff) as floating point // number. if (parser->value_index < SNDRV_FIREWIRE_MOTU_COMMAND_DSP_METER_COUNT - 2) { u32 val = (u32)(parser->value >> 32); parser->meter.data[parser->value_index] = val; } ++parser->value_index; parser->fragment_pos = 0; } if (parser->value == VALUES_AT_IMAGE_END) { parser->value_index = 0; parser->fragment_pos = 0; parser->message_count = 0; } } ++parser->message_count; break; } } } } spin_unlock_irqrestore(&parser->lock, flags); } void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu, struct snd_firewire_motu_command_dsp_meter *meter) { struct msg_parser *parser = motu->message_parser; unsigned long flags; spin_lock_irqsave(&parser->lock, flags); memcpy(meter, &parser->meter, sizeof(*meter)); spin_unlock_irqrestore(&parser->lock, flags); }