Path: blob/master/sound/firewire/fireface/ff-transaction.c
26451 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* ff-transaction.c - a part of driver for RME Fireface series3*4* Copyright (c) 2015-2017 Takashi Sakamoto5*/67#include "ff.h"89static void finish_transmit_midi_msg(struct snd_ff *ff, unsigned int port,10int rcode)11{12struct snd_rawmidi_substream *substream =13READ_ONCE(ff->rx_midi_substreams[port]);1415if (rcode_is_permanent_error(rcode)) {16ff->rx_midi_error[port] = true;17return;18}1920if (rcode != RCODE_COMPLETE) {21/* Transfer the message again, immediately. */22ff->next_ktime[port] = 0;23schedule_work(&ff->rx_midi_work[port]);24return;25}2627snd_rawmidi_transmit_ack(substream, ff->rx_bytes[port]);28ff->rx_bytes[port] = 0;2930if (!snd_rawmidi_transmit_empty(substream))31schedule_work(&ff->rx_midi_work[port]);32}3334static void finish_transmit_midi0_msg(struct fw_card *card, int rcode,35void *data, size_t length,36void *callback_data)37{38struct snd_ff *ff =39container_of(callback_data, struct snd_ff, transactions[0]);40finish_transmit_midi_msg(ff, 0, rcode);41}4243static void finish_transmit_midi1_msg(struct fw_card *card, int rcode,44void *data, size_t length,45void *callback_data)46{47struct snd_ff *ff =48container_of(callback_data, struct snd_ff, transactions[1]);49finish_transmit_midi_msg(ff, 1, rcode);50}5152static void transmit_midi_msg(struct snd_ff *ff, unsigned int port)53{54struct snd_rawmidi_substream *substream =55READ_ONCE(ff->rx_midi_substreams[port]);56int quad_count;5758struct fw_device *fw_dev = fw_parent_device(ff->unit);59unsigned long long addr;60int generation;61fw_transaction_callback_t callback;62int tcode;6364if (substream == NULL || snd_rawmidi_transmit_empty(substream))65return;6667if (ff->rx_bytes[port] > 0 || ff->rx_midi_error[port])68return;6970/* Do it in next chance. */71if (ktime_after(ff->next_ktime[port], ktime_get())) {72schedule_work(&ff->rx_midi_work[port]);73return;74}7576quad_count = ff->spec->protocol->fill_midi_msg(ff, substream, port);77if (quad_count <= 0)78return;7980if (port == 0) {81addr = ff->spec->midi_rx_addrs[0];82callback = finish_transmit_midi0_msg;83} else {84addr = ff->spec->midi_rx_addrs[1];85callback = finish_transmit_midi1_msg;86}8788/* Set interval to next transaction. */89ff->next_ktime[port] = ktime_add_ns(ktime_get(),90ff->rx_bytes[port] * 8 * (NSEC_PER_SEC / 31250));9192if (quad_count == 1)93tcode = TCODE_WRITE_QUADLET_REQUEST;94else95tcode = TCODE_WRITE_BLOCK_REQUEST;9697/*98* In Linux FireWire core, when generation is updated with memory99* barrier, node id has already been updated. In this module, After100* this smp_rmb(), load/store instructions to memory are completed.101* Thus, both of generation and node id are available with recent102* values. This is a light-serialization solution to handle bus reset103* events on IEEE 1394 bus.104*/105generation = fw_dev->generation;106smp_rmb();107fw_send_request(fw_dev->card, &ff->transactions[port], tcode,108fw_dev->node_id, generation, fw_dev->max_speed,109addr, &ff->msg_buf[port], quad_count * 4,110callback, &ff->transactions[port]);111}112113static void transmit_midi0_msg(struct work_struct *work)114{115struct snd_ff *ff = container_of(work, struct snd_ff, rx_midi_work[0]);116117transmit_midi_msg(ff, 0);118}119120static void transmit_midi1_msg(struct work_struct *work)121{122struct snd_ff *ff = container_of(work, struct snd_ff, rx_midi_work[1]);123124transmit_midi_msg(ff, 1);125}126127static void handle_msg(struct fw_card *card, struct fw_request *request, int tcode,128int destination, int source, int generation, unsigned long long offset,129void *data, size_t length, void *callback_data)130{131struct snd_ff *ff = callback_data;132__le32 *buf = data;133u32 tstamp = fw_request_get_timestamp(request);134unsigned long flag;135136fw_send_response(card, request, RCODE_COMPLETE);137138offset -= ff->async_handler.offset;139140spin_lock_irqsave(&ff->lock, flag);141ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length, tstamp);142spin_unlock_irqrestore(&ff->lock, flag);143}144145static int allocate_own_address(struct snd_ff *ff, int i)146{147struct fw_address_region midi_msg_region;148int err;149150ff->async_handler.length = ff->spec->midi_addr_range;151ff->async_handler.address_callback = handle_msg;152ff->async_handler.callback_data = ff;153154midi_msg_region.start = 0x000100000000ull * i;155midi_msg_region.end = midi_msg_region.start + ff->async_handler.length;156157err = fw_core_add_address_handler(&ff->async_handler, &midi_msg_region);158if (err >= 0) {159/* Controllers are allowed to register this region. */160if (ff->async_handler.offset & 0x0000ffffffff) {161fw_core_remove_address_handler(&ff->async_handler);162err = -EAGAIN;163}164}165166return err;167}168169// Controllers are allowed to register higher 4 bytes of destination address to170// receive asynchronous transactions for MIDI messages, while the way to171// register lower 4 bytes of address is different depending on protocols. For172// details, please refer to comments in protocol implementations.173//174// This driver expects userspace applications to configure registers for the175// lower address because in most cases such registers has the other settings.176int snd_ff_transaction_reregister(struct snd_ff *ff)177{178struct fw_card *fw_card = fw_parent_device(ff->unit)->card;179u32 addr;180__le32 reg;181182/*183* Controllers are allowed to register its node ID and upper 2 byte of184* local address to listen asynchronous transactions.185*/186addr = (fw_card->node_id << 16) | (ff->async_handler.offset >> 32);187reg = cpu_to_le32(addr);188return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,189ff->spec->midi_high_addr,190®, sizeof(reg), 0);191}192193int snd_ff_transaction_register(struct snd_ff *ff)194{195int i, err;196197/*198* Allocate in Memory Space of IEC 13213, but lower 4 byte in LSB should199* be zero due to device specification.200*/201for (i = 0; i < 0xffff; i++) {202err = allocate_own_address(ff, i);203if (err != -EBUSY && err != -EAGAIN)204break;205}206if (err < 0)207return err;208209err = snd_ff_transaction_reregister(ff);210if (err < 0)211return err;212213INIT_WORK(&ff->rx_midi_work[0], transmit_midi0_msg);214INIT_WORK(&ff->rx_midi_work[1], transmit_midi1_msg);215216return 0;217}218219void snd_ff_transaction_unregister(struct snd_ff *ff)220{221__le32 reg;222223if (ff->async_handler.callback_data == NULL)224return;225ff->async_handler.callback_data = NULL;226227/* Release higher 4 bytes of address. */228reg = cpu_to_le32(0x00000000);229snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,230ff->spec->midi_high_addr,231®, sizeof(reg), 0);232233fw_core_remove_address_handler(&ff->async_handler);234}235236237