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

tascam-transaction.c (10462B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * tascam-transaction.c - a part of driver for TASCAM FireWire series
      4 *
      5 * Copyright (c) 2015 Takashi Sakamoto
      6 */
      7
      8#include "tascam.h"
      9
     10/*
     11 * When return minus value, given argument is not MIDI status.
     12 * When return 0, given argument is a beginning of system exclusive.
     13 * When return the others, given argument is MIDI data.
     14 */
     15static inline int calculate_message_bytes(u8 status)
     16{
     17	switch (status) {
     18	case 0xf6:	/* Tune request. */
     19	case 0xf8:	/* Timing clock. */
     20	case 0xfa:	/* Start. */
     21	case 0xfb:	/* Continue. */
     22	case 0xfc:	/* Stop. */
     23	case 0xfe:	/* Active sensing. */
     24	case 0xff:	/* System reset. */
     25		return 1;
     26	case 0xf1:	/* MIDI time code quarter frame. */
     27	case 0xf3:	/* Song select. */
     28		return 2;
     29	case 0xf2:	/* Song position pointer. */
     30		return 3;
     31	case 0xf0:	/* Exclusive. */
     32		return 0;
     33	case 0xf7:	/* End of exclusive. */
     34		break;
     35	case 0xf4:	/* Undefined. */
     36	case 0xf5:	/* Undefined. */
     37	case 0xf9:	/* Undefined. */
     38	case 0xfd:	/* Undefined. */
     39		break;
     40	default:
     41		switch (status & 0xf0) {
     42		case 0x80:	/* Note on. */
     43		case 0x90:	/* Note off. */
     44		case 0xa0:	/* Polyphonic key pressure. */
     45		case 0xb0:	/* Control change and Mode change. */
     46		case 0xe0:	/* Pitch bend change. */
     47			return 3;
     48		case 0xc0:	/* Program change. */
     49		case 0xd0:	/* Channel pressure. */
     50			return 2;
     51		default:
     52		break;
     53		}
     54	break;
     55	}
     56
     57	return -EINVAL;
     58}
     59
     60static int fill_message(struct snd_fw_async_midi_port *port,
     61			struct snd_rawmidi_substream *substream)
     62{
     63	int i, len, consume;
     64	u8 *label, *msg;
     65	u8 status;
     66
     67	/* The first byte is used for label, the rest for MIDI bytes. */
     68	label = port->buf;
     69	msg = port->buf + 1;
     70
     71	consume = snd_rawmidi_transmit_peek(substream, msg, 3);
     72	if (consume == 0)
     73		return 0;
     74
     75	/* On exclusive message. */
     76	if (port->on_sysex) {
     77		/* Seek the end of exclusives. */
     78		for (i = 0; i < consume; ++i) {
     79			if (msg[i] == 0xf7) {
     80				port->on_sysex = false;
     81				break;
     82			}
     83		}
     84
     85		/* At the end of exclusive message, use label 0x07. */
     86		if (!port->on_sysex) {
     87			consume = i + 1;
     88			*label = (substream->number << 4) | 0x07;
     89		/* During exclusive message, use label 0x04. */
     90		} else if (consume == 3) {
     91			*label = (substream->number << 4) | 0x04;
     92		/* We need to fill whole 3 bytes. Go to next change. */
     93		} else {
     94			return 0;
     95		}
     96
     97		len = consume;
     98	} else {
     99		/* The beginning of exclusives. */
    100		if (msg[0] == 0xf0) {
    101			/* Transfer it in next chance in another condition. */
    102			port->on_sysex = true;
    103			return 0;
    104		} else {
    105			/* On running-status. */
    106			if ((msg[0] & 0x80) != 0x80)
    107				status = port->running_status;
    108			else
    109				status = msg[0];
    110
    111			/* Calculate consume bytes. */
    112			len = calculate_message_bytes(status);
    113			if (len <= 0)
    114				return 0;
    115
    116			/* On running-status. */
    117			if ((msg[0] & 0x80) != 0x80) {
    118				/* Enough MIDI bytes were not retrieved. */
    119				if (consume < len - 1)
    120					return 0;
    121				consume = len - 1;
    122
    123				msg[2] = msg[1];
    124				msg[1] = msg[0];
    125				msg[0] = port->running_status;
    126			} else {
    127				/* Enough MIDI bytes were not retrieved. */
    128				if (consume < len)
    129					return 0;
    130				consume = len;
    131
    132				port->running_status = msg[0];
    133			}
    134		}
    135
    136		*label = (substream->number << 4) | (msg[0] >> 4);
    137	}
    138
    139	if (len > 0 && len < 3)
    140		memset(msg + len, 0, 3 - len);
    141
    142	return consume;
    143}
    144
    145static void async_midi_port_callback(struct fw_card *card, int rcode,
    146				     void *data, size_t length,
    147				     void *callback_data)
    148{
    149	struct snd_fw_async_midi_port *port = callback_data;
    150	struct snd_rawmidi_substream *substream = READ_ONCE(port->substream);
    151
    152	/* This port is closed. */
    153	if (substream == NULL)
    154		return;
    155
    156	if (rcode == RCODE_COMPLETE)
    157		snd_rawmidi_transmit_ack(substream, port->consume_bytes);
    158	else if (!rcode_is_permanent_error(rcode))
    159		/* To start next transaction immediately for recovery. */
    160		port->next_ktime = 0;
    161	else
    162		/* Don't continue processing. */
    163		port->error = true;
    164
    165	port->idling = true;
    166
    167	if (!snd_rawmidi_transmit_empty(substream))
    168		schedule_work(&port->work);
    169}
    170
    171static void midi_port_work(struct work_struct *work)
    172{
    173	struct snd_fw_async_midi_port *port =
    174			container_of(work, struct snd_fw_async_midi_port, work);
    175	struct snd_rawmidi_substream *substream = READ_ONCE(port->substream);
    176	int generation;
    177
    178	/* Under transacting or error state. */
    179	if (!port->idling || port->error)
    180		return;
    181
    182	/* Nothing to do. */
    183	if (substream == NULL || snd_rawmidi_transmit_empty(substream))
    184		return;
    185
    186	/* Do it in next chance. */
    187	if (ktime_after(port->next_ktime, ktime_get())) {
    188		schedule_work(&port->work);
    189		return;
    190	}
    191
    192	/*
    193	 * Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
    194	 * Later, snd_rawmidi_transmit_ack() is called.
    195	 */
    196	memset(port->buf, 0, 4);
    197	port->consume_bytes = fill_message(port, substream);
    198	if (port->consume_bytes <= 0) {
    199		/* Do it in next chance, immediately. */
    200		if (port->consume_bytes == 0) {
    201			port->next_ktime = 0;
    202			schedule_work(&port->work);
    203		} else {
    204			/* Fatal error. */
    205			port->error = true;
    206		}
    207		return;
    208	}
    209
    210	/* Set interval to next transaction. */
    211	port->next_ktime = ktime_add_ns(ktime_get(),
    212			port->consume_bytes * 8 * (NSEC_PER_SEC / 31250));
    213
    214	/* Start this transaction. */
    215	port->idling = false;
    216
    217	/*
    218	 * In Linux FireWire core, when generation is updated with memory
    219	 * barrier, node id has already been updated. In this module, After
    220	 * this smp_rmb(), load/store instructions to memory are completed.
    221	 * Thus, both of generation and node id are available with recent
    222	 * values. This is a light-serialization solution to handle bus reset
    223	 * events on IEEE 1394 bus.
    224	 */
    225	generation = port->parent->generation;
    226	smp_rmb();
    227
    228	fw_send_request(port->parent->card, &port->transaction,
    229			TCODE_WRITE_QUADLET_REQUEST,
    230			port->parent->node_id, generation,
    231			port->parent->max_speed,
    232			TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD,
    233			port->buf, 4, async_midi_port_callback,
    234			port);
    235}
    236
    237void snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port)
    238{
    239	port->idling = true;
    240	port->error = false;
    241	port->running_status = 0;
    242	port->on_sysex = false;
    243}
    244
    245static void handle_midi_tx(struct fw_card *card, struct fw_request *request,
    246			   int tcode, int destination, int source,
    247			   int generation, unsigned long long offset,
    248			   void *data, size_t length, void *callback_data)
    249{
    250	struct snd_tscm *tscm = callback_data;
    251	u32 *buf = (u32 *)data;
    252	unsigned int messages;
    253	unsigned int i;
    254	unsigned int port;
    255	struct snd_rawmidi_substream *substream;
    256	u8 *b;
    257	int bytes;
    258
    259	if (offset != tscm->async_handler.offset)
    260		goto end;
    261
    262	messages = length / 8;
    263	for (i = 0; i < messages; i++) {
    264		b = (u8 *)(buf + i * 2);
    265
    266		port = b[0] >> 4;
    267		/* TODO: support virtual MIDI ports. */
    268		if (port >= tscm->spec->midi_capture_ports)
    269			goto end;
    270
    271		/* Assume the message length. */
    272		bytes = calculate_message_bytes(b[1]);
    273		/* On MIDI data or exclusives. */
    274		if (bytes <= 0) {
    275			/* Seek the end of exclusives. */
    276			for (bytes = 1; bytes < 4; bytes++) {
    277				if (b[bytes] == 0xf7)
    278					break;
    279			}
    280			if (bytes == 4)
    281				bytes = 3;
    282		}
    283
    284		substream = READ_ONCE(tscm->tx_midi_substreams[port]);
    285		if (substream != NULL)
    286			snd_rawmidi_receive(substream, b + 1, bytes);
    287	}
    288end:
    289	fw_send_response(card, request, RCODE_COMPLETE);
    290}
    291
    292int snd_tscm_transaction_register(struct snd_tscm *tscm)
    293{
    294	static const struct fw_address_region resp_register_region = {
    295		.start	= 0xffffe0000000ull,
    296		.end	= 0xffffe000ffffull,
    297	};
    298	unsigned int i;
    299	int err;
    300
    301	/*
    302	 * Usually, two quadlets are transferred by one transaction. The first
    303	 * quadlet has MIDI messages, the rest includes timestamp.
    304	 * Sometimes, 8 set of the data is transferred by a block transaction.
    305	 */
    306	tscm->async_handler.length = 8 * 8;
    307	tscm->async_handler.address_callback = handle_midi_tx;
    308	tscm->async_handler.callback_data = tscm;
    309
    310	err = fw_core_add_address_handler(&tscm->async_handler,
    311					  &resp_register_region);
    312	if (err < 0)
    313		return err;
    314
    315	err = snd_tscm_transaction_reregister(tscm);
    316	if (err < 0)
    317		goto error;
    318
    319	for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) {
    320		tscm->out_ports[i].parent = fw_parent_device(tscm->unit);
    321		tscm->out_ports[i].next_ktime = 0;
    322		INIT_WORK(&tscm->out_ports[i].work, midi_port_work);
    323	}
    324
    325	return err;
    326error:
    327	fw_core_remove_address_handler(&tscm->async_handler);
    328	tscm->async_handler.callback_data = NULL;
    329	return err;
    330}
    331
    332/* At bus reset, these registers are cleared. */
    333int snd_tscm_transaction_reregister(struct snd_tscm *tscm)
    334{
    335	struct fw_device *device = fw_parent_device(tscm->unit);
    336	__be32 reg;
    337	int err;
    338
    339	/* Register messaging address. Block transaction is not allowed. */
    340	reg = cpu_to_be32((device->card->node_id << 16) |
    341			  (tscm->async_handler.offset >> 32));
    342	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
    343				 TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
    344				 &reg, sizeof(reg), 0);
    345	if (err < 0)
    346		return err;
    347
    348	reg = cpu_to_be32(tscm->async_handler.offset);
    349	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
    350				 TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
    351				 &reg, sizeof(reg), 0);
    352	if (err < 0)
    353		return err;
    354
    355	/* Turn on messaging. */
    356	reg = cpu_to_be32(0x00000001);
    357	err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
    358				  TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
    359				  &reg, sizeof(reg), 0);
    360	if (err < 0)
    361		return err;
    362
    363	/* Turn on FireWire LED. */
    364	reg = cpu_to_be32(0x0001008e);
    365	return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
    366				  TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
    367				  &reg, sizeof(reg), 0);
    368}
    369
    370void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
    371{
    372	__be32 reg;
    373
    374	if (tscm->async_handler.callback_data == NULL)
    375		return;
    376
    377	/* Turn off FireWire LED. */
    378	reg = cpu_to_be32(0x0000008e);
    379	snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
    380			   TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
    381			   &reg, sizeof(reg), 0);
    382
    383	/* Turn off messaging. */
    384	reg = cpu_to_be32(0x00000000);
    385	snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
    386			   TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
    387			   &reg, sizeof(reg), 0);
    388
    389	/* Unregister the address. */
    390	snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
    391			   TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
    392			   &reg, sizeof(reg), 0);
    393	snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
    394			   TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
    395			   &reg, sizeof(reg), 0);
    396
    397	fw_core_remove_address_handler(&tscm->async_handler);
    398	tscm->async_handler.callback_data = NULL;
    399}