sound/core/seq/seq_virmidi.c

Source file repositories/reference/linux-study-clean/sound/core/seq/seq_virmidi.c

File Facts

System
Linux kernel
Corpus path
sound/core/seq/seq_virmidi.c
Extension
.c
Size
13493 bytes
Lines
526
Domain
Driver Families
Bucket
sound/core
Inferred role
Driver Families: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
			if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
				continue;
			snd_seq_dump_var_event(ev, dump_to_rawmidi, vmidi->substream);
			snd_midi_event_reset_decode(vmidi->parser);
		} else {
			len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev);
			if (len > 0)
				snd_rawmidi_receive(vmidi->substream, msg, len);
		}
	}
	if (atomic)
		read_unlock(&rdev->filelist_lock);
	else
		up_read(&rdev->filelist_sem);

	return 0;
}

/*
 * event handler of virmidi port
 */
static int snd_virmidi_event_input(struct snd_seq_event *ev, int direct,
				   void *private_data, int atomic, int hop)
{
	struct snd_virmidi_dev *rdev;

	rdev = private_data;
	if (!(rdev->flags & SNDRV_VIRMIDI_USE))
		return 0; /* ignored */
	return snd_virmidi_dev_receive_event(rdev, ev, atomic);
}

/*
 * trigger rawmidi stream for input
 */
static void snd_virmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
	struct snd_virmidi *vmidi = substream->runtime->private_data;

	WRITE_ONCE(vmidi->trigger, !!up);
}

/* process rawmidi bytes and send events;
 * we need no lock here for vmidi->event since it's handled only in this work
 */
static void snd_vmidi_output_work(struct work_struct *work)
{
	struct snd_virmidi *vmidi;
	struct snd_rawmidi_substream *substream;
	unsigned char input;
	int ret;

	vmidi = container_of(work, struct snd_virmidi, output_work);
	substream = vmidi->substream;

	/* discard the outputs in dispatch mode unless subscribed */
	if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
	    !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
		snd_rawmidi_proceed(substream);
		return;
	}

	while (READ_ONCE(vmidi->trigger)) {
		if (snd_rawmidi_transmit(substream, &input, 1) != 1)
			break;
		if (!snd_midi_event_encode_byte(vmidi->parser, input,
						&vmidi->event))
			continue;
		if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
			ret = snd_seq_kernel_client_dispatch(vmidi->client,
							     &vmidi->event,
							     false, 0);
			vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
			if (ret < 0)
				break;
		}
		/* rawmidi input might be huge, allow to have a break */
		cond_resched();
	}
}

/*
 * trigger rawmidi stream for output
 */
static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
	struct snd_virmidi *vmidi = substream->runtime->private_data;

	WRITE_ONCE(vmidi->trigger, !!up);

Annotation

Implementation Notes