Path: blob/master/sound/firewire/fireface/ff-transaction.c
29266 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);134135fw_send_response(card, request, RCODE_COMPLETE);136137offset -= ff->async_handler.offset;138139guard(spinlock_irqsave)(&ff->lock);140ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length, tstamp);141}142143static int allocate_own_address(struct snd_ff *ff, int i)144{145struct fw_address_region midi_msg_region;146int err;147148ff->async_handler.length = ff->spec->midi_addr_range;149ff->async_handler.address_callback = handle_msg;150ff->async_handler.callback_data = ff;151152midi_msg_region.start = 0x000100000000ull * i;153midi_msg_region.end = midi_msg_region.start + ff->async_handler.length;154155err = fw_core_add_address_handler(&ff->async_handler, &midi_msg_region);156if (err >= 0) {157/* Controllers are allowed to register this region. */158if (ff->async_handler.offset & 0x0000ffffffff) {159fw_core_remove_address_handler(&ff->async_handler);160err = -EAGAIN;161}162}163164return err;165}166167// Controllers are allowed to register higher 4 bytes of destination address to168// receive asynchronous transactions for MIDI messages, while the way to169// register lower 4 bytes of address is different depending on protocols. For170// details, please refer to comments in protocol implementations.171//172// This driver expects userspace applications to configure registers for the173// lower address because in most cases such registers has the other settings.174int snd_ff_transaction_reregister(struct snd_ff *ff)175{176struct fw_card *fw_card = fw_parent_device(ff->unit)->card;177u32 addr;178__le32 reg;179180/*181* Controllers are allowed to register its node ID and upper 2 byte of182* local address to listen asynchronous transactions.183*/184addr = (fw_card->node_id << 16) | (ff->async_handler.offset >> 32);185reg = cpu_to_le32(addr);186return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,187ff->spec->midi_high_addr,188®, sizeof(reg), 0);189}190191int snd_ff_transaction_register(struct snd_ff *ff)192{193int i, err;194195/*196* Allocate in Memory Space of IEC 13213, but lower 4 byte in LSB should197* be zero due to device specification.198*/199for (i = 0; i < 0xffff; i++) {200err = allocate_own_address(ff, i);201if (err != -EBUSY && err != -EAGAIN)202break;203}204if (err < 0)205return err;206207err = snd_ff_transaction_reregister(ff);208if (err < 0)209return err;210211INIT_WORK(&ff->rx_midi_work[0], transmit_midi0_msg);212INIT_WORK(&ff->rx_midi_work[1], transmit_midi1_msg);213214return 0;215}216217void snd_ff_transaction_unregister(struct snd_ff *ff)218{219__le32 reg;220221if (ff->async_handler.callback_data == NULL)222return;223ff->async_handler.callback_data = NULL;224225/* Release higher 4 bytes of address. */226reg = cpu_to_le32(0x00000000);227snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,228ff->spec->midi_high_addr,229®, sizeof(reg), 0);230231fw_core_remove_address_handler(&ff->async_handler);232}233234235