cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ff-transaction.c (6486B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * ff-transaction.c - a part of driver for RME Fireface series
      4 *
      5 * Copyright (c) 2015-2017 Takashi Sakamoto
      6 */
      7
      8#include "ff.h"
      9
     10static void finish_transmit_midi_msg(struct snd_ff *ff, unsigned int port,
     11				     int rcode)
     12{
     13	struct snd_rawmidi_substream *substream =
     14				READ_ONCE(ff->rx_midi_substreams[port]);
     15
     16	if (rcode_is_permanent_error(rcode)) {
     17		ff->rx_midi_error[port] = true;
     18		return;
     19	}
     20
     21	if (rcode != RCODE_COMPLETE) {
     22		/* Transfer the message again, immediately. */
     23		ff->next_ktime[port] = 0;
     24		schedule_work(&ff->rx_midi_work[port]);
     25		return;
     26	}
     27
     28	snd_rawmidi_transmit_ack(substream, ff->rx_bytes[port]);
     29	ff->rx_bytes[port] = 0;
     30
     31	if (!snd_rawmidi_transmit_empty(substream))
     32		schedule_work(&ff->rx_midi_work[port]);
     33}
     34
     35static void finish_transmit_midi0_msg(struct fw_card *card, int rcode,
     36				      void *data, size_t length,
     37				      void *callback_data)
     38{
     39	struct snd_ff *ff =
     40		container_of(callback_data, struct snd_ff, transactions[0]);
     41	finish_transmit_midi_msg(ff, 0, rcode);
     42}
     43
     44static void finish_transmit_midi1_msg(struct fw_card *card, int rcode,
     45				      void *data, size_t length,
     46				      void *callback_data)
     47{
     48	struct snd_ff *ff =
     49		container_of(callback_data, struct snd_ff, transactions[1]);
     50	finish_transmit_midi_msg(ff, 1, rcode);
     51}
     52
     53static void transmit_midi_msg(struct snd_ff *ff, unsigned int port)
     54{
     55	struct snd_rawmidi_substream *substream =
     56			READ_ONCE(ff->rx_midi_substreams[port]);
     57	int quad_count;
     58
     59	struct fw_device *fw_dev = fw_parent_device(ff->unit);
     60	unsigned long long addr;
     61	int generation;
     62	fw_transaction_callback_t callback;
     63	int tcode;
     64
     65	if (substream == NULL || snd_rawmidi_transmit_empty(substream))
     66		return;
     67
     68	if (ff->rx_bytes[port] > 0 || ff->rx_midi_error[port])
     69		return;
     70
     71	/* Do it in next chance. */
     72	if (ktime_after(ff->next_ktime[port], ktime_get())) {
     73		schedule_work(&ff->rx_midi_work[port]);
     74		return;
     75	}
     76
     77	quad_count = ff->spec->protocol->fill_midi_msg(ff, substream, port);
     78	if (quad_count <= 0)
     79		return;
     80
     81	if (port == 0) {
     82		addr = ff->spec->midi_rx_addrs[0];
     83		callback = finish_transmit_midi0_msg;
     84	} else {
     85		addr = ff->spec->midi_rx_addrs[1];
     86		callback = finish_transmit_midi1_msg;
     87	}
     88
     89	/* Set interval to next transaction. */
     90	ff->next_ktime[port] = ktime_add_ns(ktime_get(),
     91			ff->rx_bytes[port] * 8 * (NSEC_PER_SEC / 31250));
     92
     93	if (quad_count == 1)
     94		tcode = TCODE_WRITE_QUADLET_REQUEST;
     95	else
     96		tcode = TCODE_WRITE_BLOCK_REQUEST;
     97
     98	/*
     99	 * In Linux FireWire core, when generation is updated with memory
    100	 * barrier, node id has already been updated. In this module, After
    101	 * this smp_rmb(), load/store instructions to memory are completed.
    102	 * Thus, both of generation and node id are available with recent
    103	 * values. This is a light-serialization solution to handle bus reset
    104	 * events on IEEE 1394 bus.
    105	 */
    106	generation = fw_dev->generation;
    107	smp_rmb();
    108	fw_send_request(fw_dev->card, &ff->transactions[port], tcode,
    109			fw_dev->node_id, generation, fw_dev->max_speed,
    110			addr, &ff->msg_buf[port], quad_count * 4,
    111			callback, &ff->transactions[port]);
    112}
    113
    114static void transmit_midi0_msg(struct work_struct *work)
    115{
    116	struct snd_ff *ff = container_of(work, struct snd_ff, rx_midi_work[0]);
    117
    118	transmit_midi_msg(ff, 0);
    119}
    120
    121static void transmit_midi1_msg(struct work_struct *work)
    122{
    123	struct snd_ff *ff = container_of(work, struct snd_ff, rx_midi_work[1]);
    124
    125	transmit_midi_msg(ff, 1);
    126}
    127
    128static void handle_midi_msg(struct fw_card *card, struct fw_request *request,
    129			    int tcode, int destination, int source,
    130			    int generation, unsigned long long offset,
    131			    void *data, size_t length, void *callback_data)
    132{
    133	struct snd_ff *ff = callback_data;
    134	__le32 *buf = data;
    135
    136	fw_send_response(card, request, RCODE_COMPLETE);
    137
    138	offset -= ff->async_handler.offset;
    139	ff->spec->protocol->handle_midi_msg(ff, (unsigned int)offset, buf,
    140					    length);
    141}
    142
    143static int allocate_own_address(struct snd_ff *ff, int i)
    144{
    145	struct fw_address_region midi_msg_region;
    146	int err;
    147
    148	ff->async_handler.length = ff->spec->midi_addr_range;
    149	ff->async_handler.address_callback = handle_midi_msg;
    150	ff->async_handler.callback_data = ff;
    151
    152	midi_msg_region.start = 0x000100000000ull * i;
    153	midi_msg_region.end = midi_msg_region.start + ff->async_handler.length;
    154
    155	err = fw_core_add_address_handler(&ff->async_handler, &midi_msg_region);
    156	if (err >= 0) {
    157		/* Controllers are allowed to register this region. */
    158		if (ff->async_handler.offset & 0x0000ffffffff) {
    159			fw_core_remove_address_handler(&ff->async_handler);
    160			err = -EAGAIN;
    161		}
    162	}
    163
    164	return err;
    165}
    166
    167// Controllers are allowed to register higher 4 bytes of destination address to
    168// receive asynchronous transactions for MIDI messages, while the way to
    169// register lower 4 bytes of address is different depending on protocols. For
    170// details, please refer to comments in protocol implementations.
    171//
    172// This driver expects userspace applications to configure registers for the
    173// lower address because in most cases such registers has the other settings.
    174int snd_ff_transaction_reregister(struct snd_ff *ff)
    175{
    176	struct fw_card *fw_card = fw_parent_device(ff->unit)->card;
    177	u32 addr;
    178	__le32 reg;
    179
    180	/*
    181	 * Controllers are allowed to register its node ID and upper 2 byte of
    182	 * local address to listen asynchronous transactions.
    183	 */
    184	addr = (fw_card->node_id << 16) | (ff->async_handler.offset >> 32);
    185	reg = cpu_to_le32(addr);
    186	return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
    187				  ff->spec->midi_high_addr,
    188				  &reg, sizeof(reg), 0);
    189}
    190
    191int snd_ff_transaction_register(struct snd_ff *ff)
    192{
    193	int i, err;
    194
    195	/*
    196	 * Allocate in Memory Space of IEC 13213, but lower 4 byte in LSB should
    197	 * be zero due to device specification.
    198	 */
    199	for (i = 0; i < 0xffff; i++) {
    200		err = allocate_own_address(ff, i);
    201		if (err != -EBUSY && err != -EAGAIN)
    202			break;
    203	}
    204	if (err < 0)
    205		return err;
    206
    207	err = snd_ff_transaction_reregister(ff);
    208	if (err < 0)
    209		return err;
    210
    211	INIT_WORK(&ff->rx_midi_work[0], transmit_midi0_msg);
    212	INIT_WORK(&ff->rx_midi_work[1], transmit_midi1_msg);
    213
    214	return 0;
    215}
    216
    217void snd_ff_transaction_unregister(struct snd_ff *ff)
    218{
    219	__le32 reg;
    220
    221	if (ff->async_handler.callback_data == NULL)
    222		return;
    223	ff->async_handler.callback_data = NULL;
    224
    225	/* Release higher 4 bytes of address. */
    226	reg = cpu_to_le32(0x00000000);
    227	snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
    228			   ff->spec->midi_high_addr,
    229			   &reg, sizeof(reg), 0);
    230
    231	fw_core_remove_address_handler(&ff->async_handler);
    232}