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

oxfw-scs1x.c (10545B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * oxfw-scs1x.c - a part of driver for OXFW970/971 based devices
      4 *
      5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
      6 * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
      7 */
      8
      9#include "oxfw.h"
     10
     11#define HSS1394_ADDRESS			0xc007dedadadaULL
     12#define HSS1394_MAX_PACKET_SIZE		64
     13#define HSS1394_TAG_USER_DATA		0x00
     14#define HSS1394_TAG_CHANGE_ADDRESS	0xf1
     15
     16struct fw_scs1x {
     17	struct fw_address_handler hss_handler;
     18	u8 input_escape_count;
     19	struct snd_rawmidi_substream *input;
     20
     21	/* For MIDI playback. */
     22	struct snd_rawmidi_substream *output;
     23	bool output_idle;
     24	u8 output_status;
     25	u8 output_bytes;
     26	bool output_escaped;
     27	bool output_escape_high_nibble;
     28	struct work_struct work;
     29	wait_queue_head_t idle_wait;
     30	u8 buffer[HSS1394_MAX_PACKET_SIZE];
     31	bool transaction_running;
     32	struct fw_transaction transaction;
     33	unsigned int transaction_bytes;
     34	bool error;
     35	struct fw_device *fw_dev;
     36};
     37
     38static const u8 sysex_escape_prefix[] = {
     39	0xf0,			/* SysEx begin */
     40	0x00, 0x01, 0x60,	/* Stanton DJ */
     41	0x48, 0x53, 0x53,	/* "HSS" */
     42};
     43
     44static void midi_input_escaped_byte(struct snd_rawmidi_substream *stream,
     45				    u8 byte)
     46{
     47	u8 nibbles[2];
     48
     49	nibbles[0] = byte >> 4;
     50	nibbles[1] = byte & 0x0f;
     51	snd_rawmidi_receive(stream, nibbles, 2);
     52}
     53
     54static void midi_input_byte(struct fw_scs1x *scs,
     55			    struct snd_rawmidi_substream *stream, u8 byte)
     56{
     57	const u8 eox = 0xf7;
     58
     59	if (scs->input_escape_count > 0) {
     60		midi_input_escaped_byte(stream, byte);
     61		scs->input_escape_count--;
     62		if (scs->input_escape_count == 0)
     63			snd_rawmidi_receive(stream, &eox, sizeof(eox));
     64	} else if (byte == 0xf9) {
     65		snd_rawmidi_receive(stream, sysex_escape_prefix,
     66				    ARRAY_SIZE(sysex_escape_prefix));
     67		midi_input_escaped_byte(stream, 0x00);
     68		midi_input_escaped_byte(stream, 0xf9);
     69		scs->input_escape_count = 3;
     70	} else {
     71		snd_rawmidi_receive(stream, &byte, 1);
     72	}
     73}
     74
     75static void midi_input_packet(struct fw_scs1x *scs,
     76			      struct snd_rawmidi_substream *stream,
     77			      const u8 *data, unsigned int bytes)
     78{
     79	unsigned int i;
     80	const u8 eox = 0xf7;
     81
     82	if (data[0] == HSS1394_TAG_USER_DATA) {
     83		for (i = 1; i < bytes; ++i)
     84			midi_input_byte(scs, stream, data[i]);
     85	} else {
     86		snd_rawmidi_receive(stream, sysex_escape_prefix,
     87				    ARRAY_SIZE(sysex_escape_prefix));
     88		for (i = 0; i < bytes; ++i)
     89			midi_input_escaped_byte(stream, data[i]);
     90		snd_rawmidi_receive(stream, &eox, sizeof(eox));
     91	}
     92}
     93
     94static void handle_hss(struct fw_card *card, struct fw_request *request,
     95		       int tcode, int destination, int source, int generation,
     96		       unsigned long long offset, void *data, size_t length,
     97		       void *callback_data)
     98{
     99	struct fw_scs1x *scs = callback_data;
    100	struct snd_rawmidi_substream *stream;
    101	int rcode;
    102
    103	if (offset != scs->hss_handler.offset) {
    104		rcode = RCODE_ADDRESS_ERROR;
    105		goto end;
    106	}
    107	if (tcode != TCODE_WRITE_QUADLET_REQUEST &&
    108	    tcode != TCODE_WRITE_BLOCK_REQUEST) {
    109		rcode = RCODE_TYPE_ERROR;
    110		goto end;
    111	}
    112
    113	if (length >= 1) {
    114		stream = READ_ONCE(scs->input);
    115		if (stream)
    116			midi_input_packet(scs, stream, data, length);
    117	}
    118
    119	rcode = RCODE_COMPLETE;
    120end:
    121	fw_send_response(card, request, rcode);
    122}
    123
    124static void scs_write_callback(struct fw_card *card, int rcode,
    125			       void *data, size_t length, void *callback_data)
    126{
    127	struct fw_scs1x *scs = callback_data;
    128
    129	if (!rcode_is_permanent_error(rcode)) {
    130		/* Don't retry for this data. */
    131		if (rcode == RCODE_COMPLETE)
    132			scs->transaction_bytes = 0;
    133	} else {
    134		scs->error = true;
    135	}
    136
    137	scs->transaction_running = false;
    138	schedule_work(&scs->work);
    139}
    140
    141static bool is_valid_running_status(u8 status)
    142{
    143	return status >= 0x80 && status <= 0xef;
    144}
    145
    146static bool is_one_byte_cmd(u8 status)
    147{
    148	return status == 0xf6 ||
    149	       status >= 0xf8;
    150}
    151
    152static bool is_two_bytes_cmd(u8 status)
    153{
    154	return (status >= 0xc0 && status <= 0xdf) ||
    155	       status == 0xf1 ||
    156	       status == 0xf3;
    157}
    158
    159static bool is_three_bytes_cmd(u8 status)
    160{
    161	return (status >= 0x80 && status <= 0xbf) ||
    162	       (status >= 0xe0 && status <= 0xef) ||
    163	       status == 0xf2;
    164}
    165
    166static bool is_invalid_cmd(u8 status)
    167{
    168	return status == 0xf4 ||
    169	       status == 0xf5 ||
    170	       status == 0xf9 ||
    171	       status == 0xfd;
    172}
    173
    174static void scs_output_work(struct work_struct *work)
    175{
    176	struct fw_scs1x *scs = container_of(work, struct fw_scs1x, work);
    177	struct snd_rawmidi_substream *stream;
    178	unsigned int i;
    179	u8 byte;
    180	int generation;
    181
    182	if (scs->transaction_running)
    183		return;
    184
    185	stream = READ_ONCE(scs->output);
    186	if (!stream || scs->error) {
    187		scs->output_idle = true;
    188		wake_up(&scs->idle_wait);
    189		return;
    190	}
    191
    192	if (scs->transaction_bytes > 0)
    193		goto retry;
    194
    195	i = scs->output_bytes;
    196	for (;;) {
    197		if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
    198			scs->output_bytes = i;
    199			scs->output_idle = true;
    200			wake_up(&scs->idle_wait);
    201			return;
    202		}
    203		/*
    204		 * Convert from real MIDI to what I think the device expects (no
    205		 * running status, one command per packet, unescaped SysExs).
    206		 */
    207		if (scs->output_escaped && byte < 0x80) {
    208			if (scs->output_escape_high_nibble) {
    209				if (i < HSS1394_MAX_PACKET_SIZE) {
    210					scs->buffer[i] = byte << 4;
    211					scs->output_escape_high_nibble = false;
    212				}
    213			} else {
    214				scs->buffer[i++] |= byte & 0x0f;
    215				scs->output_escape_high_nibble = true;
    216			}
    217		} else if (byte < 0x80) {
    218			if (i == 1) {
    219				if (!is_valid_running_status(
    220							scs->output_status))
    221					continue;
    222				scs->buffer[0] = HSS1394_TAG_USER_DATA;
    223				scs->buffer[i++] = scs->output_status;
    224			}
    225			scs->buffer[i++] = byte;
    226			if ((i == 3 && is_two_bytes_cmd(scs->output_status)) ||
    227			    (i == 4 && is_three_bytes_cmd(scs->output_status)))
    228				break;
    229			if (i == 1 + ARRAY_SIZE(sysex_escape_prefix) &&
    230			    !memcmp(scs->buffer + 1, sysex_escape_prefix,
    231				    ARRAY_SIZE(sysex_escape_prefix))) {
    232				scs->output_escaped = true;
    233				scs->output_escape_high_nibble = true;
    234				i = 0;
    235			}
    236			if (i >= HSS1394_MAX_PACKET_SIZE)
    237				i = 1;
    238		} else if (byte == 0xf7) {
    239			if (scs->output_escaped) {
    240				if (i >= 1 && scs->output_escape_high_nibble &&
    241				    scs->buffer[0] !=
    242						HSS1394_TAG_CHANGE_ADDRESS)
    243					break;
    244			} else {
    245				if (i > 1 && scs->output_status == 0xf0) {
    246					scs->buffer[i++] = 0xf7;
    247					break;
    248				}
    249			}
    250			i = 1;
    251			scs->output_escaped = false;
    252		} else if (!is_invalid_cmd(byte) && byte < 0xf8) {
    253			i = 1;
    254			scs->buffer[0] = HSS1394_TAG_USER_DATA;
    255			scs->buffer[i++] = byte;
    256			scs->output_status = byte;
    257			scs->output_escaped = false;
    258			if (is_one_byte_cmd(byte))
    259				break;
    260		}
    261	}
    262	scs->output_bytes = 1;
    263	scs->output_escaped = false;
    264
    265	scs->transaction_bytes = i;
    266retry:
    267	scs->transaction_running = true;
    268	generation = scs->fw_dev->generation;
    269	smp_rmb(); /* node_id vs. generation */
    270	fw_send_request(scs->fw_dev->card, &scs->transaction,
    271			TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id,
    272			generation, scs->fw_dev->max_speed, HSS1394_ADDRESS,
    273			scs->buffer, scs->transaction_bytes,
    274			scs_write_callback, scs);
    275}
    276
    277static int midi_capture_open(struct snd_rawmidi_substream *stream)
    278{
    279	return 0;
    280}
    281
    282static int midi_capture_close(struct snd_rawmidi_substream *stream)
    283{
    284	return 0;
    285}
    286
    287static void midi_capture_trigger(struct snd_rawmidi_substream *stream, int up)
    288{
    289	struct fw_scs1x *scs = stream->rmidi->private_data;
    290
    291	if (up) {
    292		scs->input_escape_count = 0;
    293		WRITE_ONCE(scs->input, stream);
    294	} else {
    295		WRITE_ONCE(scs->input, NULL);
    296	}
    297}
    298
    299static int midi_playback_open(struct snd_rawmidi_substream *stream)
    300{
    301	return 0;
    302}
    303
    304static int midi_playback_close(struct snd_rawmidi_substream *stream)
    305{
    306	return 0;
    307}
    308
    309static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up)
    310{
    311	struct fw_scs1x *scs = stream->rmidi->private_data;
    312
    313	if (up) {
    314		scs->output_status = 0;
    315		scs->output_bytes = 1;
    316		scs->output_escaped = false;
    317		scs->output_idle = false;
    318		scs->transaction_bytes = 0;
    319		scs->error = false;
    320
    321		WRITE_ONCE(scs->output, stream);
    322		schedule_work(&scs->work);
    323	} else {
    324		WRITE_ONCE(scs->output, NULL);
    325	}
    326}
    327static void midi_playback_drain(struct snd_rawmidi_substream *stream)
    328{
    329	struct fw_scs1x *scs = stream->rmidi->private_data;
    330
    331	wait_event(scs->idle_wait, scs->output_idle);
    332}
    333
    334static int register_address(struct snd_oxfw *oxfw)
    335{
    336	struct fw_scs1x *scs = oxfw->spec;
    337	__be64 data;
    338
    339	data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
    340			    scs->hss_handler.offset);
    341	return snd_fw_transaction(oxfw->unit, TCODE_WRITE_BLOCK_REQUEST,
    342				  HSS1394_ADDRESS, &data, sizeof(data), 0);
    343}
    344
    345static void remove_scs1x(struct snd_rawmidi *rmidi)
    346{
    347	struct fw_scs1x *scs = rmidi->private_data;
    348
    349	fw_core_remove_address_handler(&scs->hss_handler);
    350}
    351
    352void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw)
    353{
    354	register_address(oxfw);
    355}
    356
    357int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw)
    358{
    359	static const struct snd_rawmidi_ops midi_capture_ops = {
    360		.open    = midi_capture_open,
    361		.close   = midi_capture_close,
    362		.trigger = midi_capture_trigger,
    363	};
    364	static const struct snd_rawmidi_ops midi_playback_ops = {
    365		.open    = midi_playback_open,
    366		.close   = midi_playback_close,
    367		.trigger = midi_playback_trigger,
    368		.drain   = midi_playback_drain,
    369	};
    370	struct snd_rawmidi *rmidi;
    371	struct fw_scs1x *scs;
    372	int err;
    373
    374	scs = devm_kzalloc(&oxfw->card->card_dev, sizeof(struct fw_scs1x),
    375			   GFP_KERNEL);
    376	if (!scs)
    377		return -ENOMEM;
    378	scs->fw_dev = fw_parent_device(oxfw->unit);
    379	oxfw->spec = scs;
    380
    381	/* Allocate own handler for imcoming asynchronous transaction. */
    382	scs->hss_handler.length = HSS1394_MAX_PACKET_SIZE;
    383	scs->hss_handler.address_callback = handle_hss;
    384	scs->hss_handler.callback_data = scs;
    385	err = fw_core_add_address_handler(&scs->hss_handler,
    386					  &fw_high_memory_region);
    387	if (err < 0)
    388		return err;
    389
    390	err = register_address(oxfw);
    391	if (err < 0)
    392		goto err_allocated;
    393
    394	/* Use unique name for backward compatibility to scs1x module. */
    395	err = snd_rawmidi_new(oxfw->card, "SCS.1x", 0, 1, 1, &rmidi);
    396	if (err < 0)
    397		goto err_allocated;
    398	rmidi->private_data = scs;
    399	rmidi->private_free = remove_scs1x;
    400
    401	snprintf(rmidi->name, sizeof(rmidi->name),
    402		 "%s MIDI", oxfw->card->shortname);
    403
    404	rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
    405			    SNDRV_RAWMIDI_INFO_OUTPUT |
    406			    SNDRV_RAWMIDI_INFO_DUPLEX;
    407	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
    408			    &midi_capture_ops);
    409	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
    410			    &midi_playback_ops);
    411
    412	INIT_WORK(&scs->work, scs_output_work);
    413	init_waitqueue_head(&scs->idle_wait);
    414	scs->output_idle = true;
    415
    416	return 0;
    417err_allocated:
    418	fw_core_remove_address_handler(&scs->hss_handler);
    419	return err;
    420}