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

midi.c (6908B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Line 6 Linux USB driver
      4 *
      5 * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
      6 */
      7
      8#include <linux/slab.h>
      9#include <linux/usb.h>
     10#include <linux/export.h>
     11#include <sound/core.h>
     12#include <sound/rawmidi.h>
     13
     14#include "driver.h"
     15#include "midi.h"
     16
     17#define line6_rawmidi_substream_midi(substream) \
     18	((struct snd_line6_midi *)((substream)->rmidi->private_data))
     19
     20static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
     21			   int length);
     22
     23/*
     24	Pass data received via USB to MIDI.
     25*/
     26void line6_midi_receive(struct usb_line6 *line6, unsigned char *data,
     27			int length)
     28{
     29	if (line6->line6midi->substream_receive)
     30		snd_rawmidi_receive(line6->line6midi->substream_receive,
     31				    data, length);
     32}
     33
     34/*
     35	Read data from MIDI buffer and transmit them via USB.
     36*/
     37static void line6_midi_transmit(struct snd_rawmidi_substream *substream)
     38{
     39	struct usb_line6 *line6 =
     40	    line6_rawmidi_substream_midi(substream)->line6;
     41	struct snd_line6_midi *line6midi = line6->line6midi;
     42	struct midi_buffer *mb = &line6midi->midibuf_out;
     43	unsigned char chunk[LINE6_FALLBACK_MAXPACKETSIZE];
     44	int req, done;
     45
     46	for (;;) {
     47		req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size);
     48		done = snd_rawmidi_transmit_peek(substream, chunk, req);
     49
     50		if (done == 0)
     51			break;
     52
     53		line6_midibuf_write(mb, chunk, done);
     54		snd_rawmidi_transmit_ack(substream, done);
     55	}
     56
     57	for (;;) {
     58		done = line6_midibuf_read(mb, chunk,
     59					  LINE6_FALLBACK_MAXPACKETSIZE);
     60
     61		if (done == 0)
     62			break;
     63
     64		send_midi_async(line6, chunk, done);
     65	}
     66}
     67
     68/*
     69	Notification of completion of MIDI transmission.
     70*/
     71static void midi_sent(struct urb *urb)
     72{
     73	unsigned long flags;
     74	int status;
     75	int num;
     76	struct usb_line6 *line6 = (struct usb_line6 *)urb->context;
     77
     78	status = urb->status;
     79	kfree(urb->transfer_buffer);
     80	usb_free_urb(urb);
     81
     82	if (status == -ESHUTDOWN)
     83		return;
     84
     85	spin_lock_irqsave(&line6->line6midi->lock, flags);
     86	num = --line6->line6midi->num_active_send_urbs;
     87
     88	if (num == 0) {
     89		line6_midi_transmit(line6->line6midi->substream_transmit);
     90		num = line6->line6midi->num_active_send_urbs;
     91	}
     92
     93	if (num == 0)
     94		wake_up(&line6->line6midi->send_wait);
     95
     96	spin_unlock_irqrestore(&line6->line6midi->lock, flags);
     97}
     98
     99/*
    100	Send an asynchronous MIDI message.
    101	Assumes that line6->line6midi->lock is held
    102	(i.e., this function is serialized).
    103*/
    104static int send_midi_async(struct usb_line6 *line6, unsigned char *data,
    105			   int length)
    106{
    107	struct urb *urb;
    108	int retval;
    109	unsigned char *transfer_buffer;
    110
    111	urb = usb_alloc_urb(0, GFP_ATOMIC);
    112
    113	if (urb == NULL)
    114		return -ENOMEM;
    115
    116	transfer_buffer = kmemdup(data, length, GFP_ATOMIC);
    117
    118	if (transfer_buffer == NULL) {
    119		usb_free_urb(urb);
    120		return -ENOMEM;
    121	}
    122
    123	usb_fill_int_urb(urb, line6->usbdev,
    124			 usb_sndintpipe(line6->usbdev,
    125					 line6->properties->ep_ctrl_w),
    126			 transfer_buffer, length, midi_sent, line6,
    127			 line6->interval);
    128	urb->actual_length = 0;
    129	retval = usb_urb_ep_type_check(urb);
    130	if (retval < 0)
    131		goto error;
    132
    133	retval = usb_submit_urb(urb, GFP_ATOMIC);
    134	if (retval < 0)
    135		goto error;
    136
    137	++line6->line6midi->num_active_send_urbs;
    138	return 0;
    139
    140 error:
    141	dev_err(line6->ifcdev, "usb_submit_urb failed\n");
    142	usb_free_urb(urb);
    143	return retval;
    144}
    145
    146static int line6_midi_output_open(struct snd_rawmidi_substream *substream)
    147{
    148	return 0;
    149}
    150
    151static int line6_midi_output_close(struct snd_rawmidi_substream *substream)
    152{
    153	return 0;
    154}
    155
    156static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream,
    157				      int up)
    158{
    159	unsigned long flags;
    160	struct usb_line6 *line6 =
    161	    line6_rawmidi_substream_midi(substream)->line6;
    162
    163	line6->line6midi->substream_transmit = substream;
    164	spin_lock_irqsave(&line6->line6midi->lock, flags);
    165
    166	if (line6->line6midi->num_active_send_urbs == 0)
    167		line6_midi_transmit(substream);
    168
    169	spin_unlock_irqrestore(&line6->line6midi->lock, flags);
    170}
    171
    172static void line6_midi_output_drain(struct snd_rawmidi_substream *substream)
    173{
    174	struct usb_line6 *line6 =
    175	    line6_rawmidi_substream_midi(substream)->line6;
    176	struct snd_line6_midi *midi = line6->line6midi;
    177
    178	wait_event_interruptible(midi->send_wait,
    179				 midi->num_active_send_urbs == 0);
    180}
    181
    182static int line6_midi_input_open(struct snd_rawmidi_substream *substream)
    183{
    184	return 0;
    185}
    186
    187static int line6_midi_input_close(struct snd_rawmidi_substream *substream)
    188{
    189	return 0;
    190}
    191
    192static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream,
    193				     int up)
    194{
    195	struct usb_line6 *line6 =
    196	    line6_rawmidi_substream_midi(substream)->line6;
    197
    198	if (up)
    199		line6->line6midi->substream_receive = substream;
    200	else
    201		line6->line6midi->substream_receive = NULL;
    202}
    203
    204static const struct snd_rawmidi_ops line6_midi_output_ops = {
    205	.open = line6_midi_output_open,
    206	.close = line6_midi_output_close,
    207	.trigger = line6_midi_output_trigger,
    208	.drain = line6_midi_output_drain,
    209};
    210
    211static const struct snd_rawmidi_ops line6_midi_input_ops = {
    212	.open = line6_midi_input_open,
    213	.close = line6_midi_input_close,
    214	.trigger = line6_midi_input_trigger,
    215};
    216
    217/* Create a MIDI device */
    218static int snd_line6_new_midi(struct usb_line6 *line6,
    219			      struct snd_rawmidi **rmidi_ret)
    220{
    221	struct snd_rawmidi *rmidi;
    222	int err;
    223
    224	err = snd_rawmidi_new(line6->card, "Line 6 MIDI", 0, 1, 1, rmidi_ret);
    225	if (err < 0)
    226		return err;
    227
    228	rmidi = *rmidi_ret;
    229	strcpy(rmidi->id, line6->properties->id);
    230	strcpy(rmidi->name, line6->properties->name);
    231
    232	rmidi->info_flags =
    233	    SNDRV_RAWMIDI_INFO_OUTPUT |
    234	    SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
    235
    236	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
    237			    &line6_midi_output_ops);
    238	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
    239			    &line6_midi_input_ops);
    240	return 0;
    241}
    242
    243/* MIDI device destructor */
    244static void snd_line6_midi_free(struct snd_rawmidi *rmidi)
    245{
    246	struct snd_line6_midi *line6midi = rmidi->private_data;
    247
    248	line6_midibuf_destroy(&line6midi->midibuf_in);
    249	line6_midibuf_destroy(&line6midi->midibuf_out);
    250	kfree(line6midi);
    251}
    252
    253/*
    254	Initialize the Line 6 MIDI subsystem.
    255*/
    256int line6_init_midi(struct usb_line6 *line6)
    257{
    258	int err;
    259	struct snd_rawmidi *rmidi;
    260	struct snd_line6_midi *line6midi;
    261
    262	if (!(line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)) {
    263		/* skip MIDI initialization and report success */
    264		return 0;
    265	}
    266
    267	err = snd_line6_new_midi(line6, &rmidi);
    268	if (err < 0)
    269		return err;
    270
    271	line6midi = kzalloc(sizeof(struct snd_line6_midi), GFP_KERNEL);
    272	if (!line6midi)
    273		return -ENOMEM;
    274
    275	rmidi->private_data = line6midi;
    276	rmidi->private_free = snd_line6_midi_free;
    277
    278	init_waitqueue_head(&line6midi->send_wait);
    279	spin_lock_init(&line6midi->lock);
    280	line6midi->line6 = line6;
    281
    282	err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0);
    283	if (err < 0)
    284		return err;
    285
    286	err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1);
    287	if (err < 0)
    288		return err;
    289
    290	line6->line6midi = line6midi;
    291	return 0;
    292}
    293EXPORT_SYMBOL_GPL(line6_init_midi);