2019-05-27 06:55:05 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Abstract layer for MIDI v1.0 stream
|
2007-10-15 07:50:19 +00:00
|
|
|
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sound/core.h>
|
|
|
|
#include <linux/major.h>
|
|
|
|
#include <linux/init.h>
|
2017-02-02 18:15:33 +00:00
|
|
|
#include <linux/sched/signal.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/wait.h>
|
2006-01-16 15:29:08 +00:00
|
|
|
#include <linux/mutex.h>
|
2011-07-15 17:13:37 +00:00
|
|
|
#include <linux/module.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/delay.h>
|
2018-07-17 21:12:33 +00:00
|
|
|
#include <linux/mm.h>
|
2019-03-20 21:15:24 +00:00
|
|
|
#include <linux/nospec.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <sound/rawmidi.h>
|
|
|
|
#include <sound/info.h>
|
|
|
|
#include <sound/control.h>
|
|
|
|
#include <sound/minors.h>
|
|
|
|
#include <sound/initval.h>
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
#include <sound/ump.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-10-15 07:50:19 +00:00
|
|
|
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
|
2005-04-16 22:20:36 +00:00
|
|
|
MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA.");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
|
|
#ifdef CONFIG_SND_OSSEMUL
|
2006-05-17 15:14:51 +00:00
|
|
|
static int midi_map[SNDRV_CARDS];
|
2005-04-16 22:20:36 +00:00
|
|
|
static int amidi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
|
|
|
|
module_param_array(midi_map, int, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(midi_map, "Raw MIDI device number assigned to 1st OSS device.");
|
|
|
|
module_param_array(amidi_map, int, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(amidi_map, "Raw MIDI device number assigned to 2nd OSS device.");
|
|
|
|
#endif /* CONFIG_SND_OSSEMUL */
|
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static int snd_rawmidi_dev_free(struct snd_device *device);
|
|
|
|
static int snd_rawmidi_dev_register(struct snd_device *device);
|
|
|
|
static int snd_rawmidi_dev_disconnect(struct snd_device *device);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-20 13:06:59 +00:00
|
|
|
static LIST_HEAD(snd_rawmidi_devices);
|
2006-01-16 15:29:08 +00:00
|
|
|
static DEFINE_MUTEX(register_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2014-02-04 17:21:39 +00:00
|
|
|
#define rmidi_err(rmidi, fmt, args...) \
|
2023-08-16 16:02:48 +00:00
|
|
|
dev_err((rmidi)->dev, fmt, ##args)
|
2014-02-04 17:21:39 +00:00
|
|
|
#define rmidi_warn(rmidi, fmt, args...) \
|
2023-08-16 16:02:48 +00:00
|
|
|
dev_warn((rmidi)->dev, fmt, ##args)
|
2014-02-04 17:21:39 +00:00
|
|
|
#define rmidi_dbg(rmidi, fmt, args...) \
|
2023-08-16 16:02:48 +00:00
|
|
|
dev_dbg((rmidi)->dev, fmt, ##args)
|
2014-02-04 17:21:39 +00:00
|
|
|
|
2018-04-24 12:06:12 +00:00
|
|
|
struct snd_rawmidi_status32 {
|
|
|
|
s32 stream;
|
|
|
|
s32 tstamp_sec; /* Timestamp */
|
|
|
|
s32 tstamp_nsec;
|
|
|
|
u32 avail; /* available bytes */
|
|
|
|
u32 xruns; /* count of overruns since last status (in bytes) */
|
|
|
|
unsigned char reserved[16]; /* reserved for future use */
|
|
|
|
};
|
|
|
|
|
|
|
|
#define SNDRV_RAWMIDI_IOCTL_STATUS32 _IOWR('W', 0x20, struct snd_rawmidi_status32)
|
|
|
|
|
|
|
|
struct snd_rawmidi_status64 {
|
|
|
|
int stream;
|
|
|
|
u8 rsvd[4]; /* alignment */
|
|
|
|
s64 tstamp_sec; /* Timestamp */
|
|
|
|
s64 tstamp_nsec;
|
|
|
|
size_t avail; /* available bytes */
|
|
|
|
size_t xruns; /* count of overruns since last status (in bytes) */
|
|
|
|
unsigned char reserved[16]; /* reserved for future use */
|
|
|
|
};
|
|
|
|
|
|
|
|
#define SNDRV_RAWMIDI_IOCTL_STATUS64 _IOWR('W', 0x20, struct snd_rawmidi_status64)
|
|
|
|
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
#define rawmidi_is_ump(rmidi) \
|
|
|
|
(IS_ENABLED(CONFIG_SND_UMP) && ((rmidi)->info_flags & SNDRV_RAWMIDI_INFO_UMP))
|
|
|
|
|
2005-11-20 13:06:59 +00:00
|
|
|
static struct snd_rawmidi *snd_rawmidi_search(struct snd_card *card, int device)
|
|
|
|
{
|
|
|
|
struct snd_rawmidi *rawmidi;
|
|
|
|
|
2006-10-05 14:02:22 +00:00
|
|
|
list_for_each_entry(rawmidi, &snd_rawmidi_devices, list)
|
2005-11-20 13:06:59 +00:00
|
|
|
if (rawmidi->card == card && rawmidi->device == device)
|
|
|
|
return rawmidi;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static inline unsigned short snd_rawmidi_file_flags(struct file *file)
|
|
|
|
{
|
|
|
|
switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) {
|
|
|
|
case FMODE_WRITE:
|
|
|
|
return SNDRV_RAWMIDI_LFLG_OUTPUT;
|
|
|
|
case FMODE_READ:
|
|
|
|
return SNDRV_RAWMIDI_LFLG_INPUT;
|
|
|
|
default:
|
|
|
|
return SNDRV_RAWMIDI_LFLG_OPEN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-06 08:35:27 +00:00
|
|
|
static inline bool __snd_rawmidi_ready(struct snd_rawmidi_runtime *runtime)
|
|
|
|
{
|
|
|
|
return runtime->avail >= runtime->avail_min;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool snd_rawmidi_ready(struct snd_rawmidi_substream *substream)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(spinlock_irqsave)(&substream->lock);
|
|
|
|
return __snd_rawmidi_ready(substream->runtime);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substream,
|
|
|
|
size_t count)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
return runtime->avail >= runtime->avail_min &&
|
|
|
|
(!substream->append || runtime->avail >= count);
|
|
|
|
}
|
|
|
|
|
2011-06-14 12:37:06 +00:00
|
|
|
static void snd_rawmidi_input_event_work(struct work_struct *work)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2011-06-14 12:37:06 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime =
|
|
|
|
container_of(work, struct snd_rawmidi_runtime, event_work);
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2011-06-14 12:37:06 +00:00
|
|
|
if (runtime->event)
|
|
|
|
runtime->event(runtime->substream);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2022-06-17 14:40:48 +00:00
|
|
|
/* buffer refcount management: call with substream->lock held */
|
2020-05-07 11:44:56 +00:00
|
|
|
static inline void snd_rawmidi_buffer_ref(struct snd_rawmidi_runtime *runtime)
|
|
|
|
{
|
|
|
|
runtime->buffer_ref++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void snd_rawmidi_buffer_unref(struct snd_rawmidi_runtime *runtime)
|
|
|
|
{
|
|
|
|
runtime->buffer_ref--;
|
|
|
|
}
|
|
|
|
|
2022-06-17 14:40:51 +00:00
|
|
|
static void snd_rawmidi_buffer_ref_sync(struct snd_rawmidi_substream *substream)
|
|
|
|
{
|
|
|
|
int loop = HZ;
|
|
|
|
|
|
|
|
spin_lock_irq(&substream->lock);
|
|
|
|
while (substream->runtime->buffer_ref) {
|
|
|
|
spin_unlock_irq(&substream->lock);
|
|
|
|
if (!--loop) {
|
|
|
|
rmidi_err(substream->rmidi, "Buffer ref sync timeout\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
schedule_timeout_uninterruptible(1);
|
|
|
|
spin_lock_irq(&substream->lock);
|
|
|
|
}
|
|
|
|
spin_unlock_irq(&substream->lock);
|
|
|
|
}
|
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2018-07-17 20:32:52 +00:00
|
|
|
runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
|
|
|
|
if (!runtime)
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENOMEM;
|
2011-06-14 12:37:06 +00:00
|
|
|
runtime->substream = substream;
|
2005-04-16 22:20:36 +00:00
|
|
|
init_waitqueue_head(&runtime->sleep);
|
2011-06-14 12:37:06 +00:00
|
|
|
INIT_WORK(&runtime->event_work, snd_rawmidi_input_event_work);
|
2005-04-16 22:20:36 +00:00
|
|
|
runtime->event = NULL;
|
|
|
|
runtime->buffer_size = PAGE_SIZE;
|
|
|
|
runtime->avail_min = 1;
|
|
|
|
if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
|
|
|
|
runtime->avail = 0;
|
|
|
|
else
|
|
|
|
runtime->avail = runtime->buffer_size;
|
2018-09-03 13:16:43 +00:00
|
|
|
runtime->buffer = kvzalloc(runtime->buffer_size, GFP_KERNEL);
|
2018-07-17 20:32:52 +00:00
|
|
|
if (!runtime->buffer) {
|
2005-04-16 22:20:36 +00:00
|
|
|
kfree(runtime);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
runtime->appl_ptr = runtime->hw_ptr = 0;
|
|
|
|
substream->runtime = runtime;
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
if (rawmidi_is_ump(substream->rmidi))
|
|
|
|
runtime->align = 3;
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
/* get the current alignment (either 0 or 3) */
|
|
|
|
static inline int get_align(struct snd_rawmidi_runtime *runtime)
|
|
|
|
{
|
|
|
|
if (IS_ENABLED(CONFIG_SND_UMP))
|
|
|
|
return runtime->align;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get the trimmed size with the current alignment */
|
|
|
|
#define get_aligned_size(runtime, size) ((size) & ~get_align(runtime))
|
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static int snd_rawmidi_runtime_free(struct snd_rawmidi_substream *substream)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2018-07-17 21:12:33 +00:00
|
|
|
kvfree(runtime->buffer);
|
2005-04-16 22:20:36 +00:00
|
|
|
kfree(runtime);
|
|
|
|
substream->runtime = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-07-17 20:32:52 +00:00
|
|
|
static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-03 07:17:05 +00:00
|
|
|
if (!substream->opened)
|
|
|
|
return;
|
2011-06-14 12:37:06 +00:00
|
|
|
substream->ops->trigger(substream, up);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-03 07:17:05 +00:00
|
|
|
if (!substream->opened)
|
|
|
|
return;
|
2005-04-16 22:20:36 +00:00
|
|
|
substream->ops->trigger(substream, up);
|
2011-06-14 12:37:06 +00:00
|
|
|
if (!up)
|
|
|
|
cancel_work_sync(&substream->runtime->event_work);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2018-07-17 21:07:29 +00:00
|
|
|
static void __reset_runtime_ptrs(struct snd_rawmidi_runtime *runtime,
|
|
|
|
bool is_input)
|
|
|
|
{
|
|
|
|
runtime->drain = 0;
|
|
|
|
runtime->appl_ptr = runtime->hw_ptr = 0;
|
|
|
|
runtime->avail = is_input ? 0 : runtime->buffer_size;
|
|
|
|
}
|
|
|
|
|
2022-06-17 14:40:48 +00:00
|
|
|
static void reset_runtime_ptrs(struct snd_rawmidi_substream *substream,
|
2018-07-17 21:07:29 +00:00
|
|
|
bool is_input)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(spinlock_irqsave)(&substream->lock);
|
2022-06-17 14:40:50 +00:00
|
|
|
if (substream->opened && substream->runtime)
|
|
|
|
__reset_runtime_ptrs(substream->runtime, is_input);
|
2018-07-17 21:07:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream)
|
|
|
|
{
|
|
|
|
snd_rawmidi_output_trigger(substream, 0);
|
2022-06-17 14:40:48 +00:00
|
|
|
reset_runtime_ptrs(substream, false);
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_drop_output);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2022-06-17 14:40:51 +00:00
|
|
|
int err = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
long timeout;
|
2022-06-17 14:40:51 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime;
|
|
|
|
|
2024-02-27 08:52:51 +00:00
|
|
|
scoped_guard(spinlock_irq, &substream->lock) {
|
|
|
|
runtime = substream->runtime;
|
|
|
|
if (!substream->opened || !runtime || !runtime->buffer)
|
|
|
|
return -EINVAL;
|
2022-06-17 14:40:51 +00:00
|
|
|
snd_rawmidi_buffer_ref(runtime);
|
|
|
|
runtime->drain = 1;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
timeout = wait_event_interruptible_timeout(runtime->sleep,
|
|
|
|
(runtime->avail >= runtime->buffer_size),
|
|
|
|
10*HZ);
|
2022-06-17 14:40:51 +00:00
|
|
|
|
2024-02-27 08:52:51 +00:00
|
|
|
scoped_guard(spinlock_irq, &substream->lock) {
|
|
|
|
if (signal_pending(current))
|
|
|
|
err = -ERESTARTSYS;
|
|
|
|
if (runtime->avail < runtime->buffer_size && !timeout) {
|
|
|
|
rmidi_warn(substream->rmidi,
|
|
|
|
"rawmidi drain error (avail = %li, buffer_size = %li)\n",
|
|
|
|
(long)runtime->avail, (long)runtime->buffer_size);
|
|
|
|
err = -EIO;
|
|
|
|
}
|
|
|
|
runtime->drain = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2022-06-17 14:40:51 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (err != -ERESTARTSYS) {
|
|
|
|
/* we need wait a while to make sure that Tx FIFOs are empty */
|
|
|
|
if (substream->ops->drain)
|
|
|
|
substream->ops->drain(substream);
|
|
|
|
else
|
|
|
|
msleep(50);
|
|
|
|
snd_rawmidi_drop_output(substream);
|
|
|
|
}
|
2022-06-17 14:40:51 +00:00
|
|
|
|
2024-02-27 08:52:51 +00:00
|
|
|
scoped_guard(spinlock_irq, &substream->lock)
|
|
|
|
snd_rawmidi_buffer_unref(runtime);
|
2022-06-17 14:40:51 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
return err;
|
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_drain_output);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
snd_rawmidi_input_trigger(substream, 0);
|
2022-06-17 14:40:48 +00:00
|
|
|
reset_runtime_ptrs(substream, true);
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_drain_input);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-02-11 16:03:49 +00:00
|
|
|
/* look for an available substream for the given stream direction;
|
|
|
|
* if a specific subdevice is given, try to assign it
|
|
|
|
*/
|
|
|
|
static int assign_substream(struct snd_rawmidi *rmidi, int subdevice,
|
|
|
|
int stream, int mode,
|
|
|
|
struct snd_rawmidi_substream **sub_ret)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2009-02-11 16:03:49 +00:00
|
|
|
struct snd_rawmidi_substream *substream;
|
|
|
|
struct snd_rawmidi_str *s = &rmidi->streams[stream];
|
2020-01-05 14:47:56 +00:00
|
|
|
static const unsigned int info_flags[2] = {
|
2009-02-11 16:03:49 +00:00
|
|
|
[SNDRV_RAWMIDI_STREAM_OUTPUT] = SNDRV_RAWMIDI_INFO_OUTPUT,
|
|
|
|
[SNDRV_RAWMIDI_STREAM_INPUT] = SNDRV_RAWMIDI_INFO_INPUT,
|
|
|
|
};
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-02-11 16:03:49 +00:00
|
|
|
if (!(rmidi->info_flags & info_flags[stream]))
|
2009-02-11 13:55:59 +00:00
|
|
|
return -ENXIO;
|
2009-02-11 16:03:49 +00:00
|
|
|
if (subdevice >= 0 && subdevice >= s->substream_count)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
list_for_each_entry(substream, &s->substreams, list) {
|
|
|
|
if (substream->opened) {
|
|
|
|
if (stream == SNDRV_RAWMIDI_STREAM_INPUT ||
|
2009-10-21 07:10:16 +00:00
|
|
|
!(mode & SNDRV_RAWMIDI_LFLG_APPEND) ||
|
|
|
|
!substream->append)
|
2009-02-11 16:03:49 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (subdevice < 0 || subdevice == substream->number) {
|
|
|
|
*sub_ret = substream;
|
|
|
|
return 0;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2009-02-11 16:03:49 +00:00
|
|
|
return -EAGAIN;
|
|
|
|
}
|
2009-02-11 13:55:59 +00:00
|
|
|
|
2009-02-11 16:03:49 +00:00
|
|
|
/* open and do ref-counting for the given substream */
|
|
|
|
static int open_substream(struct snd_rawmidi *rmidi,
|
|
|
|
struct snd_rawmidi_substream *substream,
|
|
|
|
int mode)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2009-10-21 07:09:38 +00:00
|
|
|
if (substream->use_count == 0) {
|
|
|
|
err = snd_rawmidi_runtime_create(substream);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
err = substream->ops->open(substream);
|
2009-10-21 07:11:43 +00:00
|
|
|
if (err < 0) {
|
|
|
|
snd_rawmidi_runtime_free(substream);
|
2009-10-21 07:09:38 +00:00
|
|
|
return err;
|
2009-10-21 07:11:43 +00:00
|
|
|
}
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(spinlock_irq)(&substream->lock);
|
2009-10-21 07:09:38 +00:00
|
|
|
substream->opened = 1;
|
2009-07-13 11:52:46 +00:00
|
|
|
substream->active_sensing = 0;
|
2009-10-21 07:09:38 +00:00
|
|
|
if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
|
|
|
|
substream->append = 1;
|
2009-11-10 09:14:04 +00:00
|
|
|
substream->pid = get_pid(task_pid(current));
|
2009-10-21 07:12:26 +00:00
|
|
|
rmidi->streams[substream->stream].substream_opened++;
|
2009-10-21 07:09:38 +00:00
|
|
|
}
|
|
|
|
substream->use_count++;
|
2009-02-11 16:03:49 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void close_substream(struct snd_rawmidi *rmidi,
|
|
|
|
struct snd_rawmidi_substream *substream,
|
|
|
|
int cleanup);
|
|
|
|
|
|
|
|
static int rawmidi_open_priv(struct snd_rawmidi *rmidi, int subdevice, int mode,
|
|
|
|
struct snd_rawmidi_file *rfile)
|
|
|
|
{
|
|
|
|
struct snd_rawmidi_substream *sinput = NULL, *soutput = NULL;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
rfile->input = rfile->output = NULL;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
|
2009-02-11 16:03:49 +00:00
|
|
|
err = assign_substream(rmidi, subdevice,
|
|
|
|
SNDRV_RAWMIDI_STREAM_INPUT,
|
|
|
|
mode, &sinput);
|
|
|
|
if (err < 0)
|
2009-10-21 07:11:43 +00:00
|
|
|
return err;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
|
2009-02-11 16:03:49 +00:00
|
|
|
err = assign_substream(rmidi, subdevice,
|
|
|
|
SNDRV_RAWMIDI_STREAM_OUTPUT,
|
|
|
|
mode, &soutput);
|
|
|
|
if (err < 0)
|
2009-10-21 07:11:43 +00:00
|
|
|
return err;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2009-02-11 16:03:49 +00:00
|
|
|
|
|
|
|
if (sinput) {
|
|
|
|
err = open_substream(rmidi, sinput, mode);
|
|
|
|
if (err < 0)
|
2009-10-21 07:11:43 +00:00
|
|
|
return err;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2009-02-11 16:03:49 +00:00
|
|
|
if (soutput) {
|
|
|
|
err = open_substream(rmidi, soutput, mode);
|
|
|
|
if (err < 0) {
|
|
|
|
if (sinput)
|
|
|
|
close_substream(rmidi, sinput, 0);
|
2009-10-21 07:11:43 +00:00
|
|
|
return err;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
2009-02-11 16:03:49 +00:00
|
|
|
|
|
|
|
rfile->rmidi = rmidi;
|
|
|
|
rfile->input = sinput;
|
|
|
|
rfile->output = soutput;
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
2009-02-11 16:03:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* called from sound/core/seq/seq_midi.c */
|
2023-05-23 07:53:22 +00:00
|
|
|
int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, int subdevice,
|
2018-07-17 20:32:52 +00:00
|
|
|
int mode, struct snd_rawmidi_file *rfile)
|
2009-02-11 16:03:49 +00:00
|
|
|
{
|
2023-05-23 07:53:22 +00:00
|
|
|
int err;
|
2009-02-11 16:03:49 +00:00
|
|
|
|
|
|
|
if (snd_BUG_ON(!rfile))
|
|
|
|
return -EINVAL;
|
2023-05-23 07:53:22 +00:00
|
|
|
if (!try_module_get(rmidi->card->module))
|
|
|
|
return -ENXIO;
|
2009-02-11 16:03:49 +00:00
|
|
|
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(mutex)(&rmidi->open_mutex);
|
2009-02-11 16:03:49 +00:00
|
|
|
err = rawmidi_open_priv(rmidi, subdevice, mode, rfile);
|
|
|
|
if (err < 0)
|
|
|
|
module_put(rmidi->card->module);
|
2005-04-16 22:20:36 +00:00
|
|
|
return err;
|
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_kernel_open);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
static int snd_rawmidi_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
int maj = imajor(inode);
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_card *card;
|
2005-11-20 13:06:59 +00:00
|
|
|
int subdevice;
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned short fflags;
|
|
|
|
int err;
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi *rmidi;
|
2009-02-11 16:03:49 +00:00
|
|
|
struct snd_rawmidi_file *rawmidi_file = NULL;
|
2017-06-20 10:06:13 +00:00
|
|
|
wait_queue_entry_t wait;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2018-07-17 20:32:52 +00:00
|
|
|
if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))
|
2009-02-11 16:03:49 +00:00
|
|
|
return -EINVAL; /* invalid combination */
|
|
|
|
|
2019-03-26 20:51:19 +00:00
|
|
|
err = stream_open(inode, file);
|
2010-04-13 09:49:04 +00:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2005-10-24 15:05:03 +00:00
|
|
|
if (maj == snd_major) {
|
2005-11-20 13:06:59 +00:00
|
|
|
rmidi = snd_lookup_minor_data(iminor(inode),
|
|
|
|
SNDRV_DEVICE_TYPE_RAWMIDI);
|
2005-04-16 22:20:36 +00:00
|
|
|
#ifdef CONFIG_SND_OSSEMUL
|
2005-10-24 15:05:03 +00:00
|
|
|
} else if (maj == SOUND_MAJOR) {
|
2005-11-20 13:06:59 +00:00
|
|
|
rmidi = snd_lookup_oss_minor_data(iminor(inode),
|
|
|
|
SNDRV_OSS_DEVICE_TYPE_MIDI);
|
2005-04-16 22:20:36 +00:00
|
|
|
#endif
|
2005-10-24 15:05:03 +00:00
|
|
|
} else
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENXIO;
|
|
|
|
|
|
|
|
if (rmidi == NULL)
|
|
|
|
return -ENODEV;
|
2009-02-11 16:03:49 +00:00
|
|
|
|
2012-10-16 11:05:59 +00:00
|
|
|
if (!try_module_get(rmidi->card->module)) {
|
|
|
|
snd_card_unref(rmidi->card);
|
2009-02-11 16:03:49 +00:00
|
|
|
return -ENXIO;
|
2012-10-16 11:05:59 +00:00
|
|
|
}
|
2009-02-11 16:03:49 +00:00
|
|
|
|
|
|
|
mutex_lock(&rmidi->open_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
card = rmidi->card;
|
|
|
|
err = snd_card_file_add(card, file);
|
|
|
|
if (err < 0)
|
2009-02-11 16:03:49 +00:00
|
|
|
goto __error_card;
|
2005-04-16 22:20:36 +00:00
|
|
|
fflags = snd_rawmidi_file_flags(file);
|
2005-10-24 15:05:03 +00:00
|
|
|
if ((file->f_flags & O_APPEND) || maj == SOUND_MAJOR) /* OSS emul? */
|
2005-04-16 22:20:36 +00:00
|
|
|
fflags |= SNDRV_RAWMIDI_LFLG_APPEND;
|
|
|
|
rawmidi_file = kmalloc(sizeof(*rawmidi_file), GFP_KERNEL);
|
|
|
|
if (rawmidi_file == NULL) {
|
2009-02-11 16:03:49 +00:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto __error;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2021-12-18 12:39:25 +00:00
|
|
|
rawmidi_file->user_pversion = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
init_waitqueue_entry(&wait, current);
|
|
|
|
add_wait_queue(&rmidi->open_wait, &wait);
|
|
|
|
while (1) {
|
2014-02-19 13:30:29 +00:00
|
|
|
subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_RAWMIDI);
|
2009-02-11 16:03:49 +00:00
|
|
|
err = rawmidi_open_priv(rmidi, subdevice, fflags, rawmidi_file);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (err >= 0)
|
|
|
|
break;
|
|
|
|
if (err == -EAGAIN) {
|
|
|
|
if (file->f_flags & O_NONBLOCK) {
|
|
|
|
err = -EBUSY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
2006-01-16 15:29:08 +00:00
|
|
|
mutex_unlock(&rmidi->open_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
schedule();
|
2006-01-16 15:29:08 +00:00
|
|
|
mutex_lock(&rmidi->open_mutex);
|
2012-10-16 14:43:39 +00:00
|
|
|
if (rmidi->card->shutdown) {
|
|
|
|
err = -ENODEV;
|
|
|
|
break;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
if (signal_pending(current)) {
|
|
|
|
err = -ERESTARTSYS;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-02-11 16:03:49 +00:00
|
|
|
remove_wait_queue(&rmidi->open_wait, &wait);
|
|
|
|
if (err < 0) {
|
|
|
|
kfree(rawmidi_file);
|
|
|
|
goto __error;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
#ifdef CONFIG_SND_OSSEMUL
|
|
|
|
if (rawmidi_file->input && rawmidi_file->input->runtime)
|
|
|
|
rawmidi_file->input->runtime->oss = (maj == SOUND_MAJOR);
|
|
|
|
if (rawmidi_file->output && rawmidi_file->output->runtime)
|
|
|
|
rawmidi_file->output->runtime->oss = (maj == SOUND_MAJOR);
|
|
|
|
#endif
|
2009-02-11 16:03:49 +00:00
|
|
|
file->private_data = rawmidi_file;
|
|
|
|
mutex_unlock(&rmidi->open_mutex);
|
2012-10-16 11:05:59 +00:00
|
|
|
snd_card_unref(rmidi->card);
|
2009-02-11 16:03:49 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
__error:
|
|
|
|
snd_card_file_remove(card, file);
|
|
|
|
__error_card:
|
2006-01-16 15:29:08 +00:00
|
|
|
mutex_unlock(&rmidi->open_mutex);
|
2009-02-11 16:03:49 +00:00
|
|
|
module_put(rmidi->card->module);
|
2012-10-16 11:05:59 +00:00
|
|
|
snd_card_unref(rmidi->card);
|
2005-04-16 22:20:36 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-02-11 16:03:49 +00:00
|
|
|
static void close_substream(struct snd_rawmidi *rmidi,
|
|
|
|
struct snd_rawmidi_substream *substream,
|
|
|
|
int cleanup)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2009-02-11 16:03:49 +00:00
|
|
|
if (--substream->use_count)
|
|
|
|
return;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-02-11 16:03:49 +00:00
|
|
|
if (cleanup) {
|
|
|
|
if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
|
|
|
|
snd_rawmidi_input_trigger(substream, 0);
|
|
|
|
else {
|
2005-04-16 22:20:36 +00:00
|
|
|
if (substream->active_sensing) {
|
|
|
|
unsigned char buf = 0xfe;
|
2009-02-11 16:03:49 +00:00
|
|
|
/* sending single active sensing message
|
|
|
|
* to shut the device up
|
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
snd_rawmidi_kernel_write(substream, &buf, 1);
|
|
|
|
}
|
|
|
|
if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS)
|
|
|
|
snd_rawmidi_output_trigger(substream, 0);
|
|
|
|
}
|
2022-06-17 14:40:51 +00:00
|
|
|
snd_rawmidi_buffer_ref_sync(substream);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2024-02-27 08:52:51 +00:00
|
|
|
scoped_guard(spinlock_irq, &substream->lock) {
|
|
|
|
substream->opened = 0;
|
|
|
|
substream->append = 0;
|
|
|
|
}
|
2009-02-11 16:03:49 +00:00
|
|
|
substream->ops->close(substream);
|
|
|
|
if (substream->runtime->private_free)
|
|
|
|
substream->runtime->private_free(substream);
|
|
|
|
snd_rawmidi_runtime_free(substream);
|
2009-11-10 09:14:04 +00:00
|
|
|
put_pid(substream->pid);
|
|
|
|
substream->pid = NULL;
|
2009-10-21 07:12:26 +00:00
|
|
|
rmidi->streams[substream->stream].substream_opened--;
|
2009-02-11 16:03:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
|
|
|
|
{
|
|
|
|
struct snd_rawmidi *rmidi;
|
|
|
|
|
|
|
|
rmidi = rfile->rmidi;
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(mutex)(&rmidi->open_mutex);
|
2009-02-11 16:03:49 +00:00
|
|
|
if (rfile->input) {
|
|
|
|
close_substream(rmidi, rfile->input, 1);
|
|
|
|
rfile->input = NULL;
|
|
|
|
}
|
|
|
|
if (rfile->output) {
|
|
|
|
close_substream(rmidi, rfile->output, 1);
|
|
|
|
rfile->output = NULL;
|
|
|
|
}
|
|
|
|
rfile->rmidi = NULL;
|
|
|
|
wake_up(&rmidi->open_wait);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* called from sound/core/seq/seq_midi.c */
|
|
|
|
int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile)
|
|
|
|
{
|
|
|
|
struct snd_rawmidi *rmidi;
|
|
|
|
|
|
|
|
if (snd_BUG_ON(!rfile))
|
|
|
|
return -ENXIO;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2009-02-11 16:03:49 +00:00
|
|
|
rmidi = rfile->rmidi;
|
|
|
|
rawmidi_release_priv(rfile);
|
2005-04-16 22:20:36 +00:00
|
|
|
module_put(rmidi->card->module);
|
|
|
|
return 0;
|
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_kernel_release);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
static int snd_rawmidi_release(struct inode *inode, struct file *file)
|
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_file *rfile;
|
|
|
|
struct snd_rawmidi *rmidi;
|
2010-10-15 10:06:18 +00:00
|
|
|
struct module *module;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
rfile = file->private_data;
|
|
|
|
rmidi = rfile->rmidi;
|
2009-02-11 16:03:49 +00:00
|
|
|
rawmidi_release_priv(rfile);
|
2005-04-16 22:20:36 +00:00
|
|
|
kfree(rfile);
|
2010-10-15 10:06:18 +00:00
|
|
|
module = rmidi->card->module;
|
2005-04-16 22:20:36 +00:00
|
|
|
snd_card_file_remove(rmidi->card, file);
|
2010-10-15 10:06:18 +00:00
|
|
|
module_put(module);
|
2009-02-11 16:03:49 +00:00
|
|
|
return 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2005-11-23 12:14:50 +00:00
|
|
|
static int snd_rawmidi_info(struct snd_rawmidi_substream *substream,
|
|
|
|
struct snd_rawmidi_info *info)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi *rmidi;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (substream == NULL)
|
|
|
|
return -ENODEV;
|
|
|
|
rmidi = substream->rmidi;
|
|
|
|
memset(info, 0, sizeof(*info));
|
|
|
|
info->card = rmidi->card->number;
|
|
|
|
info->device = rmidi->device;
|
|
|
|
info->subdevice = substream->number;
|
|
|
|
info->stream = substream->stream;
|
|
|
|
info->flags = rmidi->info_flags;
|
|
|
|
strcpy(info->id, rmidi->id);
|
|
|
|
strcpy(info->name, rmidi->name);
|
|
|
|
strcpy(info->subname, substream->name);
|
|
|
|
info->subdevices_count = substream->pstr->substream_count;
|
|
|
|
info->subdevices_avail = (substream->pstr->substream_count -
|
|
|
|
substream->pstr->substream_opened);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static int snd_rawmidi_info_user(struct snd_rawmidi_substream *substream,
|
2018-07-17 20:32:52 +00:00
|
|
|
struct snd_rawmidi_info __user *_info)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_info info;
|
2005-04-16 22:20:36 +00:00
|
|
|
int err;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
|
|
|
err = snd_rawmidi_info(substream, &info);
|
|
|
|
if (err < 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
return err;
|
2005-11-17 12:56:51 +00:00
|
|
|
if (copy_to_user(_info, &info, sizeof(struct snd_rawmidi_info)))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-14 15:44:12 +00:00
|
|
|
static int __snd_rawmidi_info_select(struct snd_card *card,
|
|
|
|
struct snd_rawmidi_info *info)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi *rmidi;
|
|
|
|
struct snd_rawmidi_str *pstr;
|
|
|
|
struct snd_rawmidi_substream *substream;
|
2005-11-20 13:06:59 +00:00
|
|
|
|
|
|
|
rmidi = snd_rawmidi_search(card, info->device);
|
2005-11-20 12:59:56 +00:00
|
|
|
if (!rmidi)
|
|
|
|
return -ENXIO;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (info->stream < 0 || info->stream > 1)
|
|
|
|
return -EINVAL;
|
2019-03-20 21:15:24 +00:00
|
|
|
info->stream = array_index_nospec(info->stream, 2);
|
2005-04-16 22:20:36 +00:00
|
|
|
pstr = &rmidi->streams[info->stream];
|
|
|
|
if (pstr->substream_count == 0)
|
|
|
|
return -ENOENT;
|
|
|
|
if (info->subdevice >= pstr->substream_count)
|
|
|
|
return -ENXIO;
|
2006-10-05 14:02:22 +00:00
|
|
|
list_for_each_entry(substream, &pstr->substreams, list) {
|
2005-04-16 22:20:36 +00:00
|
|
|
if ((unsigned int)substream->number == info->subdevice)
|
|
|
|
return snd_rawmidi_info(substream, info);
|
|
|
|
}
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
2017-12-14 15:44:12 +00:00
|
|
|
|
|
|
|
int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info)
|
|
|
|
{
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(mutex)(®ister_mutex);
|
|
|
|
return __snd_rawmidi_info_select(card, info);
|
2017-12-14 15:44:12 +00:00
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_info_select);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static int snd_rawmidi_info_select_user(struct snd_card *card,
|
|
|
|
struct snd_rawmidi_info __user *_info)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int err;
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_info info;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (get_user(info.device, &_info->device))
|
|
|
|
return -EFAULT;
|
|
|
|
if (get_user(info.stream, &_info->stream))
|
|
|
|
return -EFAULT;
|
|
|
|
if (get_user(info.subdevice, &_info->subdevice))
|
|
|
|
return -EFAULT;
|
2018-07-17 20:32:52 +00:00
|
|
|
err = snd_rawmidi_info_select(card, &info);
|
|
|
|
if (err < 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
return err;
|
2005-11-17 12:56:51 +00:00
|
|
|
if (copy_to_user(_info, &info, sizeof(struct snd_rawmidi_info)))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-06-17 14:40:48 +00:00
|
|
|
static int resize_runtime_buffer(struct snd_rawmidi_substream *substream,
|
2018-07-17 21:07:29 +00:00
|
|
|
struct snd_rawmidi_params *params,
|
|
|
|
bool is_input)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2022-06-17 14:40:48 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
2018-07-17 15:26:43 +00:00
|
|
|
char *newbuf, *oldbuf;
|
2021-05-15 07:15:33 +00:00
|
|
|
unsigned int framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
|
|
|
if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L)
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
2021-05-15 07:15:33 +00:00
|
|
|
if (framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP && (params->buffer_size & 0x1f) != 0)
|
|
|
|
return -EINVAL;
|
2018-07-17 20:32:52 +00:00
|
|
|
if (params->avail_min < 1 || params->avail_min > params->buffer_size)
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
if (params->buffer_size & get_align(runtime))
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (params->buffer_size != runtime->buffer_size) {
|
2018-09-03 13:16:43 +00:00
|
|
|
newbuf = kvzalloc(params->buffer_size, GFP_KERNEL);
|
2006-03-28 09:56:54 +00:00
|
|
|
if (!newbuf)
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENOMEM;
|
2024-11-25 14:20:25 +00:00
|
|
|
spin_lock_irq(&substream->lock);
|
2020-05-07 11:44:56 +00:00
|
|
|
if (runtime->buffer_ref) {
|
2024-11-25 14:20:25 +00:00
|
|
|
spin_unlock_irq(&substream->lock);
|
2020-05-07 11:44:56 +00:00
|
|
|
kvfree(newbuf);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
2018-07-17 15:26:43 +00:00
|
|
|
oldbuf = runtime->buffer;
|
2005-04-16 22:20:36 +00:00
|
|
|
runtime->buffer = newbuf;
|
|
|
|
runtime->buffer_size = params->buffer_size;
|
2018-07-17 21:07:29 +00:00
|
|
|
__reset_runtime_ptrs(runtime, is_input);
|
2024-11-25 14:20:25 +00:00
|
|
|
spin_unlock_irq(&substream->lock);
|
2018-07-17 21:12:33 +00:00
|
|
|
kvfree(oldbuf);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
runtime->avail_min = params->avail_min;
|
|
|
|
return 0;
|
|
|
|
}
|
2018-07-17 21:07:29 +00:00
|
|
|
|
|
|
|
int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream,
|
|
|
|
struct snd_rawmidi_params *params)
|
|
|
|
{
|
2022-06-17 14:40:49 +00:00
|
|
|
int err;
|
|
|
|
|
2018-07-17 21:07:29 +00:00
|
|
|
snd_rawmidi_drain_output(substream);
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(mutex)(&substream->rmidi->open_mutex);
|
2022-06-17 14:40:49 +00:00
|
|
|
if (substream->append && substream->use_count > 1)
|
2024-02-27 08:52:51 +00:00
|
|
|
return -EBUSY;
|
|
|
|
err = resize_runtime_buffer(substream, params, false);
|
2022-06-17 14:40:49 +00:00
|
|
|
if (!err)
|
|
|
|
substream->active_sensing = !params->no_active_sensing;
|
|
|
|
return err;
|
2018-07-17 21:07:29 +00:00
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_output_params);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
|
2018-07-17 20:32:52 +00:00
|
|
|
struct snd_rawmidi_params *params)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2021-05-15 07:15:33 +00:00
|
|
|
unsigned int framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK;
|
|
|
|
unsigned int clock_type = params->mode & SNDRV_RAWMIDI_MODE_CLOCK_MASK;
|
|
|
|
int err;
|
|
|
|
|
2022-06-17 14:40:49 +00:00
|
|
|
snd_rawmidi_drain_input(substream);
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(mutex)(&substream->rmidi->open_mutex);
|
2021-05-15 07:15:33 +00:00
|
|
|
if (framing == SNDRV_RAWMIDI_MODE_FRAMING_NONE && clock_type != SNDRV_RAWMIDI_MODE_CLOCK_NONE)
|
2022-06-17 14:40:49 +00:00
|
|
|
err = -EINVAL;
|
2021-05-15 07:15:33 +00:00
|
|
|
else if (clock_type > SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW)
|
2022-06-17 14:40:49 +00:00
|
|
|
err = -EINVAL;
|
|
|
|
else if (framing > SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP)
|
|
|
|
err = -EINVAL;
|
|
|
|
else
|
|
|
|
err = resize_runtime_buffer(substream, params, true);
|
2021-05-15 07:15:33 +00:00
|
|
|
|
2022-06-17 14:40:49 +00:00
|
|
|
if (!err) {
|
|
|
|
substream->framing = framing;
|
|
|
|
substream->clock_type = clock_type;
|
|
|
|
}
|
2021-05-15 07:15:33 +00:00
|
|
|
return 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_input_params);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static int snd_rawmidi_output_status(struct snd_rawmidi_substream *substream,
|
2018-04-24 12:06:12 +00:00
|
|
|
struct snd_rawmidi_status64 *status)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
memset(status, 0, sizeof(*status));
|
|
|
|
status->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(spinlock_irq)(&substream->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
status->avail = runtime->avail;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static int snd_rawmidi_input_status(struct snd_rawmidi_substream *substream,
|
2018-04-24 12:06:12 +00:00
|
|
|
struct snd_rawmidi_status64 *status)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
memset(status, 0, sizeof(*status));
|
|
|
|
status->stream = SNDRV_RAWMIDI_STREAM_INPUT;
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(spinlock_irq)(&substream->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
status->avail = runtime->avail;
|
|
|
|
status->xruns = runtime->xruns;
|
|
|
|
runtime->xruns = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-04-24 12:06:12 +00:00
|
|
|
static int snd_rawmidi_ioctl_status32(struct snd_rawmidi_file *rfile,
|
|
|
|
struct snd_rawmidi_status32 __user *argp)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
struct snd_rawmidi_status32 __user *status = argp;
|
|
|
|
struct snd_rawmidi_status32 status32;
|
|
|
|
struct snd_rawmidi_status64 status64;
|
|
|
|
|
|
|
|
if (copy_from_user(&status32, argp,
|
|
|
|
sizeof(struct snd_rawmidi_status32)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
switch (status32.stream) {
|
|
|
|
case SNDRV_RAWMIDI_STREAM_OUTPUT:
|
|
|
|
if (rfile->output == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
err = snd_rawmidi_output_status(rfile->output, &status64);
|
|
|
|
break;
|
|
|
|
case SNDRV_RAWMIDI_STREAM_INPUT:
|
|
|
|
if (rfile->input == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
err = snd_rawmidi_input_status(rfile->input, &status64);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
status32 = (struct snd_rawmidi_status32) {
|
|
|
|
.stream = status64.stream,
|
|
|
|
.tstamp_sec = status64.tstamp_sec,
|
|
|
|
.tstamp_nsec = status64.tstamp_nsec,
|
|
|
|
.avail = status64.avail,
|
|
|
|
.xruns = status64.xruns,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (copy_to_user(status, &status32, sizeof(*status)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int snd_rawmidi_ioctl_status64(struct snd_rawmidi_file *rfile,
|
|
|
|
struct snd_rawmidi_status64 __user *argp)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
struct snd_rawmidi_status64 status;
|
|
|
|
|
|
|
|
if (copy_from_user(&status, argp, sizeof(struct snd_rawmidi_status64)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
switch (status.stream) {
|
|
|
|
case SNDRV_RAWMIDI_STREAM_OUTPUT:
|
|
|
|
if (rfile->output == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
err = snd_rawmidi_output_status(rfile->output, &status);
|
|
|
|
break;
|
|
|
|
case SNDRV_RAWMIDI_STREAM_INPUT:
|
|
|
|
if (rfile->input == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
err = snd_rawmidi_input_status(rfile->input, &status);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
if (copy_to_user(argp, &status,
|
|
|
|
sizeof(struct snd_rawmidi_status64)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_file *rfile;
|
2023-05-23 07:53:23 +00:00
|
|
|
struct snd_rawmidi *rmidi;
|
2005-04-16 22:20:36 +00:00
|
|
|
void __user *argp = (void __user *)arg;
|
|
|
|
|
|
|
|
rfile = file->private_data;
|
|
|
|
if (((cmd >> 8) & 0xff) != 'W')
|
|
|
|
return -ENOTTY;
|
|
|
|
switch (cmd) {
|
|
|
|
case SNDRV_RAWMIDI_IOCTL_PVERSION:
|
|
|
|
return put_user(SNDRV_RAWMIDI_VERSION, (int __user *)argp) ? -EFAULT : 0;
|
|
|
|
case SNDRV_RAWMIDI_IOCTL_INFO:
|
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
int stream;
|
|
|
|
struct snd_rawmidi_info __user *info = argp;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (get_user(stream, &info->stream))
|
|
|
|
return -EFAULT;
|
|
|
|
switch (stream) {
|
|
|
|
case SNDRV_RAWMIDI_STREAM_INPUT:
|
|
|
|
return snd_rawmidi_info_user(rfile->input, info);
|
|
|
|
case SNDRV_RAWMIDI_STREAM_OUTPUT:
|
|
|
|
return snd_rawmidi_info_user(rfile->output, info);
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
2021-09-20 17:18:50 +00:00
|
|
|
case SNDRV_RAWMIDI_IOCTL_USER_PVERSION:
|
|
|
|
if (get_user(rfile->user_pversion, (unsigned int __user *)arg))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
case SNDRV_RAWMIDI_IOCTL_PARAMS:
|
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_params params;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
if (copy_from_user(¶ms, argp, sizeof(struct snd_rawmidi_params)))
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EFAULT;
|
2021-09-20 17:18:50 +00:00
|
|
|
if (rfile->user_pversion < SNDRV_PROTOCOL_VERSION(2, 0, 2)) {
|
|
|
|
params.mode = 0;
|
|
|
|
memset(params.reserved, 0, sizeof(params.reserved));
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
switch (params.stream) {
|
|
|
|
case SNDRV_RAWMIDI_STREAM_OUTPUT:
|
|
|
|
if (rfile->output == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
return snd_rawmidi_output_params(rfile->output, ¶ms);
|
|
|
|
case SNDRV_RAWMIDI_STREAM_INPUT:
|
|
|
|
if (rfile->input == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
return snd_rawmidi_input_params(rfile->input, ¶ms);
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
2018-04-24 12:06:12 +00:00
|
|
|
case SNDRV_RAWMIDI_IOCTL_STATUS32:
|
|
|
|
return snd_rawmidi_ioctl_status32(rfile, argp);
|
|
|
|
case SNDRV_RAWMIDI_IOCTL_STATUS64:
|
|
|
|
return snd_rawmidi_ioctl_status64(rfile, argp);
|
2005-04-16 22:20:36 +00:00
|
|
|
case SNDRV_RAWMIDI_IOCTL_DROP:
|
|
|
|
{
|
|
|
|
int val;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (get_user(val, (int __user *) argp))
|
|
|
|
return -EFAULT;
|
|
|
|
switch (val) {
|
|
|
|
case SNDRV_RAWMIDI_STREAM_OUTPUT:
|
|
|
|
if (rfile->output == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
return snd_rawmidi_drop_output(rfile->output);
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case SNDRV_RAWMIDI_IOCTL_DRAIN:
|
|
|
|
{
|
|
|
|
int val;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (get_user(val, (int __user *) argp))
|
|
|
|
return -EFAULT;
|
|
|
|
switch (val) {
|
|
|
|
case SNDRV_RAWMIDI_STREAM_OUTPUT:
|
|
|
|
if (rfile->output == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
return snd_rawmidi_drain_output(rfile->output);
|
|
|
|
case SNDRV_RAWMIDI_STREAM_INPUT:
|
|
|
|
if (rfile->input == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
return snd_rawmidi_drain_input(rfile->input);
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
2023-05-23 07:53:23 +00:00
|
|
|
rmidi = rfile->rmidi;
|
|
|
|
if (rmidi->ops && rmidi->ops->ioctl)
|
|
|
|
return rmidi->ops->ioctl(rmidi, cmd, argp);
|
|
|
|
rmidi_dbg(rmidi, "rawmidi: unknown command = 0x%x\n", cmd);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
return -ENOTTY;
|
|
|
|
}
|
|
|
|
|
2023-05-23 07:53:25 +00:00
|
|
|
/* ioctl to find the next device; either legacy or UMP depending on @find_ump */
|
|
|
|
static int snd_rawmidi_next_device(struct snd_card *card, int __user *argp,
|
|
|
|
bool find_ump)
|
|
|
|
|
|
|
|
{
|
|
|
|
struct snd_rawmidi *rmidi;
|
|
|
|
int device;
|
|
|
|
bool is_ump;
|
|
|
|
|
|
|
|
if (get_user(device, argp))
|
|
|
|
return -EFAULT;
|
|
|
|
if (device >= SNDRV_RAWMIDI_DEVICES) /* next device is -1 */
|
|
|
|
device = SNDRV_RAWMIDI_DEVICES - 1;
|
2024-02-27 08:52:51 +00:00
|
|
|
scoped_guard(mutex, ®ister_mutex) {
|
|
|
|
device = device < 0 ? 0 : device + 1;
|
|
|
|
for (; device < SNDRV_RAWMIDI_DEVICES; device++) {
|
|
|
|
rmidi = snd_rawmidi_search(card, device);
|
|
|
|
if (!rmidi)
|
|
|
|
continue;
|
|
|
|
is_ump = rawmidi_is_ump(rmidi);
|
|
|
|
if (find_ump == is_ump)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (device == SNDRV_RAWMIDI_DEVICES)
|
|
|
|
device = -1;
|
2023-05-23 07:53:25 +00:00
|
|
|
}
|
|
|
|
if (put_user(device, argp))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-05-23 07:53:26 +00:00
|
|
|
#if IS_ENABLED(CONFIG_SND_UMP)
|
|
|
|
/* inquiry of UMP endpoint and block info via control API */
|
|
|
|
static int snd_rawmidi_call_ump_ioctl(struct snd_card *card, int cmd,
|
|
|
|
void __user *argp)
|
|
|
|
{
|
|
|
|
struct snd_ump_endpoint_info __user *info = argp;
|
|
|
|
struct snd_rawmidi *rmidi;
|
2024-02-27 08:52:51 +00:00
|
|
|
int device;
|
2023-05-23 07:53:26 +00:00
|
|
|
|
|
|
|
if (get_user(device, &info->device))
|
|
|
|
return -EFAULT;
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(mutex)(®ister_mutex);
|
2023-05-23 07:53:26 +00:00
|
|
|
rmidi = snd_rawmidi_search(card, device);
|
|
|
|
if (rmidi && rmidi->ops && rmidi->ops->ioctl)
|
2024-02-27 08:52:51 +00:00
|
|
|
return rmidi->ops->ioctl(rmidi, cmd, argp);
|
2023-05-23 07:53:26 +00:00
|
|
|
else
|
2024-02-27 08:52:51 +00:00
|
|
|
return -ENXIO;
|
2023-05-23 07:53:26 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static int snd_rawmidi_control_ioctl(struct snd_card *card,
|
|
|
|
struct snd_ctl_file *control,
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned int cmd,
|
|
|
|
unsigned long arg)
|
|
|
|
{
|
|
|
|
void __user *argp = (void __user *)arg;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE:
|
2023-05-23 07:53:25 +00:00
|
|
|
return snd_rawmidi_next_device(card, argp, false);
|
|
|
|
#if IS_ENABLED(CONFIG_SND_UMP)
|
|
|
|
case SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE:
|
|
|
|
return snd_rawmidi_next_device(card, argp, true);
|
2023-05-23 07:53:26 +00:00
|
|
|
case SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO:
|
|
|
|
return snd_rawmidi_call_ump_ioctl(card, SNDRV_UMP_IOCTL_ENDPOINT_INFO, argp);
|
|
|
|
case SNDRV_CTL_IOCTL_UMP_BLOCK_INFO:
|
|
|
|
return snd_rawmidi_call_ump_ioctl(card, SNDRV_UMP_IOCTL_BLOCK_INFO, argp);
|
2023-05-23 07:53:25 +00:00
|
|
|
#endif
|
2005-04-16 22:20:36 +00:00
|
|
|
case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE:
|
|
|
|
{
|
|
|
|
int val;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (get_user(val, (int __user *)argp))
|
|
|
|
return -EFAULT;
|
2014-02-19 13:30:29 +00:00
|
|
|
control->preferred_subdevice[SND_CTL_SUBDEV_RAWMIDI] = val;
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
case SNDRV_CTL_IOCTL_RAWMIDI_INFO:
|
|
|
|
return snd_rawmidi_info_select_user(card, argp);
|
|
|
|
}
|
|
|
|
return -ENOIOCTLCMD;
|
|
|
|
}
|
|
|
|
|
2021-05-15 07:15:33 +00:00
|
|
|
static int receive_with_tstamp_framing(struct snd_rawmidi_substream *substream,
|
|
|
|
const unsigned char *buffer, int src_count, const struct timespec64 *tstamp)
|
|
|
|
{
|
|
|
|
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_rawmidi_framing_tstamp *dest_ptr;
|
|
|
|
struct snd_rawmidi_framing_tstamp frame = { .tv_sec = tstamp->tv_sec, .tv_nsec = tstamp->tv_nsec };
|
|
|
|
int orig_count = src_count;
|
|
|
|
int frame_size = sizeof(struct snd_rawmidi_framing_tstamp);
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
int align = get_align(runtime);
|
2021-05-15 07:15:33 +00:00
|
|
|
|
|
|
|
BUILD_BUG_ON(frame_size != 0x20);
|
|
|
|
if (snd_BUG_ON((runtime->hw_ptr & 0x1f) != 0))
|
|
|
|
return -EINVAL;
|
|
|
|
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
while (src_count > align) {
|
2021-05-15 07:15:33 +00:00
|
|
|
if ((int)(runtime->buffer_size - runtime->avail) < frame_size) {
|
|
|
|
runtime->xruns += src_count;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (src_count >= SNDRV_RAWMIDI_FRAMING_DATA_LENGTH)
|
|
|
|
frame.length = SNDRV_RAWMIDI_FRAMING_DATA_LENGTH;
|
|
|
|
else {
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
frame.length = get_aligned_size(runtime, src_count);
|
|
|
|
if (!frame.length)
|
|
|
|
break;
|
2021-05-15 07:15:33 +00:00
|
|
|
memset(frame.data, 0, SNDRV_RAWMIDI_FRAMING_DATA_LENGTH);
|
|
|
|
}
|
|
|
|
memcpy(frame.data, buffer, frame.length);
|
|
|
|
buffer += frame.length;
|
|
|
|
src_count -= frame.length;
|
|
|
|
dest_ptr = (struct snd_rawmidi_framing_tstamp *) (runtime->buffer + runtime->hw_ptr);
|
|
|
|
*dest_ptr = frame;
|
|
|
|
runtime->avail += frame_size;
|
|
|
|
runtime->hw_ptr += frame_size;
|
|
|
|
runtime->hw_ptr %= runtime->buffer_size;
|
|
|
|
}
|
|
|
|
return orig_count - src_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream)
|
|
|
|
{
|
|
|
|
struct timespec64 ts64 = {0, 0};
|
|
|
|
|
|
|
|
switch (substream->clock_type) {
|
|
|
|
case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW:
|
|
|
|
ktime_get_raw_ts64(&ts64);
|
|
|
|
break;
|
|
|
|
case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC:
|
|
|
|
ktime_get_ts64(&ts64);
|
|
|
|
break;
|
|
|
|
case SNDRV_RAWMIDI_MODE_CLOCK_REALTIME:
|
|
|
|
ktime_get_real_ts64(&ts64);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ts64;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/**
|
|
|
|
* snd_rawmidi_receive - receive the input data from the device
|
|
|
|
* @substream: the rawmidi substream
|
|
|
|
* @buffer: the buffer pointer
|
|
|
|
* @count: the data size to read
|
|
|
|
*
|
|
|
|
* Reads the data from the internal buffer.
|
|
|
|
*
|
2013-03-11 21:05:14 +00:00
|
|
|
* Return: The size of read data, or a negative error code on failure.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2005-11-17 12:56:51 +00:00
|
|
|
int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
|
|
|
|
const unsigned char *buffer, int count)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2021-05-15 07:15:33 +00:00
|
|
|
struct timespec64 ts64 = get_framing_tstamp(substream);
|
2005-04-16 22:20:36 +00:00
|
|
|
int result = 0, count1;
|
2022-06-17 14:40:50 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(spinlock_irqsave)(&substream->lock);
|
|
|
|
if (!substream->opened)
|
|
|
|
return -EBADFD;
|
2022-06-17 14:40:50 +00:00
|
|
|
runtime = substream->runtime;
|
|
|
|
if (!runtime || !runtime->buffer) {
|
2014-02-04 17:21:39 +00:00
|
|
|
rmidi_dbg(substream->rmidi,
|
|
|
|
"snd_rawmidi_receive: input is not active!!!\n");
|
2024-02-27 08:52:51 +00:00
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2021-05-15 07:15:33 +00:00
|
|
|
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
count = get_aligned_size(runtime, count);
|
|
|
|
if (!count)
|
2024-02-27 08:52:51 +00:00
|
|
|
return result;
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
|
2021-05-15 07:15:33 +00:00
|
|
|
if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) {
|
|
|
|
result = receive_with_tstamp_framing(substream, buffer, count, &ts64);
|
|
|
|
} else if (count == 1) { /* special case, faster code */
|
2005-04-16 22:20:36 +00:00
|
|
|
substream->bytes++;
|
|
|
|
if (runtime->avail < runtime->buffer_size) {
|
|
|
|
runtime->buffer[runtime->hw_ptr++] = buffer[0];
|
|
|
|
runtime->hw_ptr %= runtime->buffer_size;
|
|
|
|
runtime->avail++;
|
|
|
|
result++;
|
|
|
|
} else {
|
|
|
|
runtime->xruns++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
substream->bytes += count;
|
|
|
|
count1 = runtime->buffer_size - runtime->hw_ptr;
|
|
|
|
if (count1 > count)
|
|
|
|
count1 = count;
|
|
|
|
if (count1 > (int)(runtime->buffer_size - runtime->avail))
|
|
|
|
count1 = runtime->buffer_size - runtime->avail;
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
count1 = get_aligned_size(runtime, count1);
|
|
|
|
if (!count1)
|
2024-02-27 08:52:51 +00:00
|
|
|
return result;
|
2005-04-16 22:20:36 +00:00
|
|
|
memcpy(runtime->buffer + runtime->hw_ptr, buffer, count1);
|
|
|
|
runtime->hw_ptr += count1;
|
|
|
|
runtime->hw_ptr %= runtime->buffer_size;
|
|
|
|
runtime->avail += count1;
|
|
|
|
count -= count1;
|
|
|
|
result += count1;
|
|
|
|
if (count > 0) {
|
|
|
|
buffer += count1;
|
|
|
|
count1 = count;
|
|
|
|
if (count1 > (int)(runtime->buffer_size - runtime->avail)) {
|
|
|
|
count1 = runtime->buffer_size - runtime->avail;
|
|
|
|
runtime->xruns += count - count1;
|
|
|
|
}
|
|
|
|
if (count1 > 0) {
|
|
|
|
memcpy(runtime->buffer, buffer, count1);
|
|
|
|
runtime->hw_ptr = count1;
|
|
|
|
runtime->avail += count1;
|
|
|
|
result += count1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (result > 0) {
|
|
|
|
if (runtime->event)
|
2011-06-14 12:37:06 +00:00
|
|
|
schedule_work(&runtime->event_work);
|
2020-12-06 08:35:27 +00:00
|
|
|
else if (__snd_rawmidi_ready(runtime))
|
2005-04-16 22:20:36 +00:00
|
|
|
wake_up(&runtime->sleep);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_receive);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
|
2008-01-09 16:56:07 +00:00
|
|
|
unsigned char __user *userbuf,
|
|
|
|
unsigned char *kernelbuf, long count)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
long result = 0, count1;
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
2016-02-03 13:41:22 +00:00
|
|
|
unsigned long appl_ptr;
|
2020-05-07 11:44:56 +00:00
|
|
|
int err = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_lock_irqsave(&substream->lock, flags);
|
2020-05-07 11:44:56 +00:00
|
|
|
snd_rawmidi_buffer_ref(runtime);
|
2005-04-16 22:20:36 +00:00
|
|
|
while (count > 0 && runtime->avail) {
|
|
|
|
count1 = runtime->buffer_size - runtime->appl_ptr;
|
|
|
|
if (count1 > count)
|
|
|
|
count1 = count;
|
|
|
|
if (count1 > (int)runtime->avail)
|
|
|
|
count1 = runtime->avail;
|
2016-02-03 13:41:22 +00:00
|
|
|
|
|
|
|
/* update runtime->appl_ptr before unlocking for userbuf */
|
|
|
|
appl_ptr = runtime->appl_ptr;
|
|
|
|
runtime->appl_ptr += count1;
|
|
|
|
runtime->appl_ptr %= runtime->buffer_size;
|
|
|
|
runtime->avail -= count1;
|
|
|
|
|
2008-01-09 16:56:07 +00:00
|
|
|
if (kernelbuf)
|
2016-02-03 13:41:22 +00:00
|
|
|
memcpy(kernelbuf + result, runtime->buffer + appl_ptr, count1);
|
2008-01-09 16:56:07 +00:00
|
|
|
if (userbuf) {
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_unlock_irqrestore(&substream->lock, flags);
|
2008-01-09 16:56:07 +00:00
|
|
|
if (copy_to_user(userbuf + result,
|
2020-05-07 11:44:56 +00:00
|
|
|
runtime->buffer + appl_ptr, count1))
|
|
|
|
err = -EFAULT;
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_lock_irqsave(&substream->lock, flags);
|
2020-05-07 11:44:56 +00:00
|
|
|
if (err)
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
result += count1;
|
|
|
|
count -= count1;
|
|
|
|
}
|
2020-05-07 11:44:56 +00:00
|
|
|
out:
|
|
|
|
snd_rawmidi_buffer_unref(runtime);
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_unlock_irqrestore(&substream->lock, flags);
|
2020-05-07 11:44:56 +00:00
|
|
|
return result > 0 ? result : err;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream,
|
|
|
|
unsigned char *buf, long count)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
snd_rawmidi_input_trigger(substream, 1);
|
2008-01-09 16:56:07 +00:00
|
|
|
return snd_rawmidi_kernel_read1(substream, NULL/*userbuf*/, buf, count);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_kernel_read);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t count,
|
|
|
|
loff_t *offset)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
long result;
|
|
|
|
int count1;
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_file *rfile;
|
|
|
|
struct snd_rawmidi_substream *substream;
|
|
|
|
struct snd_rawmidi_runtime *runtime;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
rfile = file->private_data;
|
|
|
|
substream = rfile->input;
|
|
|
|
if (substream == NULL)
|
|
|
|
return -EIO;
|
|
|
|
runtime = substream->runtime;
|
|
|
|
snd_rawmidi_input_trigger(substream, 1);
|
|
|
|
result = 0;
|
|
|
|
while (count > 0) {
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_lock_irq(&substream->lock);
|
2020-12-06 08:35:27 +00:00
|
|
|
while (!__snd_rawmidi_ready(runtime)) {
|
2017-06-20 10:06:13 +00:00
|
|
|
wait_queue_entry_t wait;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_unlock_irq(&substream->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return result > 0 ? result : -EAGAIN;
|
|
|
|
}
|
|
|
|
init_waitqueue_entry(&wait, current);
|
|
|
|
add_wait_queue(&runtime->sleep, &wait);
|
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_unlock_irq(&substream->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
schedule();
|
|
|
|
remove_wait_queue(&runtime->sleep, &wait);
|
2012-10-16 14:43:39 +00:00
|
|
|
if (rfile->rmidi->card->shutdown)
|
|
|
|
return -ENODEV;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (signal_pending(current))
|
|
|
|
return result > 0 ? result : -ERESTARTSYS;
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_lock_irq(&substream->lock);
|
2020-12-06 08:35:27 +00:00
|
|
|
if (!runtime->avail) {
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_unlock_irq(&substream->lock);
|
2020-12-06 08:35:27 +00:00
|
|
|
return result > 0 ? result : -EIO;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_unlock_irq(&substream->lock);
|
2005-09-05 08:35:20 +00:00
|
|
|
count1 = snd_rawmidi_kernel_read1(substream,
|
2008-01-09 16:56:07 +00:00
|
|
|
(unsigned char __user *)buf,
|
|
|
|
NULL/*kernelbuf*/,
|
|
|
|
count);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (count1 < 0)
|
|
|
|
return result > 0 ? result : count1;
|
|
|
|
result += count1;
|
|
|
|
buf += count1;
|
|
|
|
count -= count1;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* snd_rawmidi_transmit_empty - check whether the output buffer is empty
|
|
|
|
* @substream: the rawmidi substream
|
2013-03-11 21:05:14 +00:00
|
|
|
*
|
|
|
|
* Return: 1 if the internal output buffer is empty, 0 if not.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2005-11-17 12:56:51 +00:00
|
|
|
int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2022-06-17 14:40:50 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(spinlock_irqsave)(&substream->lock);
|
2022-06-17 14:40:50 +00:00
|
|
|
runtime = substream->runtime;
|
|
|
|
if (!substream->opened || !runtime || !runtime->buffer) {
|
2014-02-04 17:21:39 +00:00
|
|
|
rmidi_dbg(substream->rmidi,
|
|
|
|
"snd_rawmidi_transmit_empty: output is not active!!!\n");
|
2024-02-27 08:52:51 +00:00
|
|
|
return 1;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2024-02-27 08:52:51 +00:00
|
|
|
return (runtime->avail >= runtime->buffer_size);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2022-06-17 14:40:47 +00:00
|
|
|
/*
|
2016-01-31 10:57:41 +00:00
|
|
|
* __snd_rawmidi_transmit_peek - copy data from the internal buffer
|
2005-04-16 22:20:36 +00:00
|
|
|
* @substream: the rawmidi substream
|
|
|
|
* @buffer: the buffer pointer
|
|
|
|
* @count: data size to transfer
|
|
|
|
*
|
2016-01-31 10:57:41 +00:00
|
|
|
* This is a variant of snd_rawmidi_transmit_peek() without spinlock.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2022-06-17 14:40:47 +00:00
|
|
|
static int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
|
|
|
|
unsigned char *buffer, int count)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int result, count1;
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (runtime->buffer == NULL) {
|
2014-02-04 17:21:39 +00:00
|
|
|
rmidi_dbg(substream->rmidi,
|
|
|
|
"snd_rawmidi_transmit_peek: output is not active!!!\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
result = 0;
|
|
|
|
if (runtime->avail >= runtime->buffer_size) {
|
|
|
|
/* warning: lowlevel layer MUST trigger down the hardware */
|
|
|
|
goto __skip;
|
|
|
|
}
|
|
|
|
if (count == 1) { /* special case, faster code */
|
|
|
|
*buffer = runtime->buffer[runtime->hw_ptr];
|
|
|
|
result++;
|
|
|
|
} else {
|
|
|
|
count1 = runtime->buffer_size - runtime->hw_ptr;
|
|
|
|
if (count1 > count)
|
|
|
|
count1 = count;
|
|
|
|
if (count1 > (int)(runtime->buffer_size - runtime->avail))
|
|
|
|
count1 = runtime->buffer_size - runtime->avail;
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
count1 = get_aligned_size(runtime, count1);
|
|
|
|
if (!count1)
|
|
|
|
goto __skip;
|
2005-04-16 22:20:36 +00:00
|
|
|
memcpy(buffer, runtime->buffer + runtime->hw_ptr, count1);
|
|
|
|
count -= count1;
|
|
|
|
result += count1;
|
|
|
|
if (count > 0) {
|
|
|
|
if (count > (int)(runtime->buffer_size - runtime->avail - count1))
|
|
|
|
count = runtime->buffer_size - runtime->avail - count1;
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
count = get_aligned_size(runtime, count);
|
|
|
|
if (!count)
|
|
|
|
goto __skip;
|
2005-04-16 22:20:36 +00:00
|
|
|
memcpy(buffer + count1, runtime->buffer, count);
|
|
|
|
result += count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
__skip:
|
2016-01-31 10:57:41 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* snd_rawmidi_transmit_peek - copy data from the internal buffer
|
|
|
|
* @substream: the rawmidi substream
|
|
|
|
* @buffer: the buffer pointer
|
|
|
|
* @count: data size to transfer
|
|
|
|
*
|
|
|
|
* Copies data from the internal output buffer to the given buffer.
|
|
|
|
*
|
|
|
|
* Call this in the interrupt handler when the midi output is ready,
|
|
|
|
* and call snd_rawmidi_transmit_ack() after the transmission is
|
|
|
|
* finished.
|
|
|
|
*
|
|
|
|
* Return: The size of copied data, or a negative error code on failure.
|
|
|
|
*/
|
|
|
|
int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
|
|
|
|
unsigned char *buffer, int count)
|
|
|
|
{
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(spinlock_irqsave)(&substream->lock);
|
2022-06-17 14:40:50 +00:00
|
|
|
if (!substream->opened || !substream->runtime)
|
2024-02-27 08:52:51 +00:00
|
|
|
return -EBADFD;
|
|
|
|
return __snd_rawmidi_transmit_peek(substream, buffer, count);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2022-06-17 14:40:47 +00:00
|
|
|
/*
|
2016-01-31 10:57:41 +00:00
|
|
|
* __snd_rawmidi_transmit_ack - acknowledge the transmission
|
2005-04-16 22:20:36 +00:00
|
|
|
* @substream: the rawmidi substream
|
2014-02-08 15:47:36 +00:00
|
|
|
* @count: the transferred count
|
2005-04-16 22:20:36 +00:00
|
|
|
*
|
2016-01-31 10:57:41 +00:00
|
|
|
* This is a variant of __snd_rawmidi_transmit_ack() without spinlock.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2022-06-17 14:40:47 +00:00
|
|
|
static int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream,
|
|
|
|
int count)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (runtime->buffer == NULL) {
|
2014-02-04 17:21:39 +00:00
|
|
|
rmidi_dbg(substream->rmidi,
|
|
|
|
"snd_rawmidi_transmit_ack: output is not active!!!\n");
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2008-08-08 15:09:09 +00:00
|
|
|
snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
count = get_aligned_size(runtime, count);
|
2005-04-16 22:20:36 +00:00
|
|
|
runtime->hw_ptr += count;
|
|
|
|
runtime->hw_ptr %= runtime->buffer_size;
|
|
|
|
runtime->avail += count;
|
|
|
|
substream->bytes += count;
|
|
|
|
if (count > 0) {
|
2020-12-06 08:35:27 +00:00
|
|
|
if (runtime->drain || __snd_rawmidi_ready(runtime))
|
2005-04-16 22:20:36 +00:00
|
|
|
wake_up(&runtime->sleep);
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
2016-01-31 10:57:41 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* snd_rawmidi_transmit_ack - acknowledge the transmission
|
|
|
|
* @substream: the rawmidi substream
|
|
|
|
* @count: the transferred count
|
|
|
|
*
|
|
|
|
* Advances the hardware pointer for the internal output buffer with
|
|
|
|
* the given size and updates the condition.
|
|
|
|
* Call after the transmission is finished.
|
|
|
|
*
|
|
|
|
* Return: The advanced size if successful, or a negative error code on failure.
|
|
|
|
*/
|
|
|
|
int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
|
|
|
|
{
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(spinlock_irqsave)(&substream->lock);
|
2022-06-17 14:40:50 +00:00
|
|
|
if (!substream->opened || !substream->runtime)
|
2024-02-27 08:52:51 +00:00
|
|
|
return -EBADFD;
|
|
|
|
return __snd_rawmidi_transmit_ack(substream, count);
|
2016-01-31 10:57:41 +00:00
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* snd_rawmidi_transmit - copy from the buffer to the device
|
|
|
|
* @substream: the rawmidi substream
|
2005-09-07 11:38:19 +00:00
|
|
|
* @buffer: the buffer pointer
|
2005-04-16 22:20:36 +00:00
|
|
|
* @count: the data size to transfer
|
2018-07-17 20:32:52 +00:00
|
|
|
*
|
2005-04-16 22:20:36 +00:00
|
|
|
* Copies data from the buffer to the device and advances the pointer.
|
|
|
|
*
|
2013-03-11 21:05:14 +00:00
|
|
|
* Return: The copied size if successful, or a negative error code on failure.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2005-11-17 12:56:51 +00:00
|
|
|
int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
|
|
|
|
unsigned char *buffer, int count)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(spinlock_irqsave)(&substream->lock);
|
2008-11-03 07:17:05 +00:00
|
|
|
if (!substream->opened)
|
2024-02-27 08:52:51 +00:00
|
|
|
return -EBADFD;
|
|
|
|
count = __snd_rawmidi_transmit_peek(substream, buffer, count);
|
|
|
|
if (count <= 0)
|
|
|
|
return count;
|
|
|
|
return __snd_rawmidi_transmit_ack(substream, count);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_transmit);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2018-09-13 06:20:43 +00:00
|
|
|
/**
|
|
|
|
* snd_rawmidi_proceed - Discard the all pending bytes and proceed
|
|
|
|
* @substream: rawmidi substream
|
|
|
|
*
|
|
|
|
* Return: the number of discarded bytes
|
|
|
|
*/
|
|
|
|
int snd_rawmidi_proceed(struct snd_rawmidi_substream *substream)
|
|
|
|
{
|
2022-06-17 14:40:50 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime;
|
2018-09-13 06:20:43 +00:00
|
|
|
int count = 0;
|
|
|
|
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(spinlock_irqsave)(&substream->lock);
|
2022-06-17 14:40:50 +00:00
|
|
|
runtime = substream->runtime;
|
|
|
|
if (substream->opened && runtime &&
|
|
|
|
runtime->avail < runtime->buffer_size) {
|
2018-09-13 06:20:43 +00:00
|
|
|
count = runtime->buffer_size - runtime->avail;
|
|
|
|
__snd_rawmidi_transmit_ack(substream, count);
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(snd_rawmidi_proceed);
|
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
|
2008-01-09 16:56:07 +00:00
|
|
|
const unsigned char __user *userbuf,
|
|
|
|
const unsigned char *kernelbuf,
|
|
|
|
long count)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
long count1, result;
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
2016-02-03 13:41:22 +00:00
|
|
|
unsigned long appl_ptr;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2016-02-01 11:04:55 +00:00
|
|
|
if (!kernelbuf && !userbuf)
|
2008-08-08 15:09:09 +00:00
|
|
|
return -EINVAL;
|
|
|
|
if (snd_BUG_ON(!runtime->buffer))
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
result = 0;
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_lock_irqsave(&substream->lock, flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (substream->append) {
|
|
|
|
if ((long)runtime->avail < count) {
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_unlock_irqrestore(&substream->lock, flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
}
|
2020-05-07 11:44:56 +00:00
|
|
|
snd_rawmidi_buffer_ref(runtime);
|
2005-04-16 22:20:36 +00:00
|
|
|
while (count > 0 && runtime->avail > 0) {
|
|
|
|
count1 = runtime->buffer_size - runtime->appl_ptr;
|
|
|
|
if (count1 > count)
|
|
|
|
count1 = count;
|
|
|
|
if (count1 > (long)runtime->avail)
|
|
|
|
count1 = runtime->avail;
|
2016-02-03 13:41:22 +00:00
|
|
|
|
|
|
|
/* update runtime->appl_ptr before unlocking for userbuf */
|
|
|
|
appl_ptr = runtime->appl_ptr;
|
|
|
|
runtime->appl_ptr += count1;
|
|
|
|
runtime->appl_ptr %= runtime->buffer_size;
|
|
|
|
runtime->avail -= count1;
|
|
|
|
|
2008-01-09 16:56:07 +00:00
|
|
|
if (kernelbuf)
|
2016-02-03 13:41:22 +00:00
|
|
|
memcpy(runtime->buffer + appl_ptr,
|
2008-01-09 16:56:07 +00:00
|
|
|
kernelbuf + result, count1);
|
|
|
|
else if (userbuf) {
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_unlock_irqrestore(&substream->lock, flags);
|
2016-02-03 13:41:22 +00:00
|
|
|
if (copy_from_user(runtime->buffer + appl_ptr,
|
2008-01-09 16:56:07 +00:00
|
|
|
userbuf + result, count1)) {
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_lock_irqsave(&substream->lock, flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
result = result > 0 ? result : -EFAULT;
|
|
|
|
goto __end;
|
|
|
|
}
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_lock_irqsave(&substream->lock, flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
result += count1;
|
|
|
|
count -= count1;
|
|
|
|
}
|
|
|
|
__end:
|
|
|
|
count1 = runtime->avail < runtime->buffer_size;
|
2020-05-07 11:44:56 +00:00
|
|
|
snd_rawmidi_buffer_unref(runtime);
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_unlock_irqrestore(&substream->lock, flags);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (count1)
|
|
|
|
snd_rawmidi_output_trigger(substream, 1);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
long snd_rawmidi_kernel_write(struct snd_rawmidi_substream *substream,
|
|
|
|
const unsigned char *buf, long count)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-01-09 16:56:07 +00:00
|
|
|
return snd_rawmidi_kernel_write1(substream, NULL, buf, count);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_kernel_write);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
|
|
|
|
size_t count, loff_t *offset)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
long result, timeout;
|
|
|
|
int count1;
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_file *rfile;
|
|
|
|
struct snd_rawmidi_runtime *runtime;
|
|
|
|
struct snd_rawmidi_substream *substream;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
rfile = file->private_data;
|
|
|
|
substream = rfile->output;
|
|
|
|
runtime = substream->runtime;
|
|
|
|
/* we cannot put an atomic message to our buffer */
|
|
|
|
if (substream->append && count > runtime->buffer_size)
|
|
|
|
return -EIO;
|
|
|
|
result = 0;
|
|
|
|
while (count > 0) {
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_lock_irq(&substream->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
while (!snd_rawmidi_ready_append(substream, count)) {
|
2017-06-20 10:06:13 +00:00
|
|
|
wait_queue_entry_t wait;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (file->f_flags & O_NONBLOCK) {
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_unlock_irq(&substream->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
return result > 0 ? result : -EAGAIN;
|
|
|
|
}
|
|
|
|
init_waitqueue_entry(&wait, current);
|
|
|
|
add_wait_queue(&runtime->sleep, &wait);
|
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_unlock_irq(&substream->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
timeout = schedule_timeout(30 * HZ);
|
|
|
|
remove_wait_queue(&runtime->sleep, &wait);
|
2012-10-16 14:43:39 +00:00
|
|
|
if (rfile->rmidi->card->shutdown)
|
|
|
|
return -ENODEV;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (signal_pending(current))
|
|
|
|
return result > 0 ? result : -ERESTARTSYS;
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_lock_irq(&substream->lock);
|
2020-12-06 08:35:27 +00:00
|
|
|
if (!runtime->avail && !timeout) {
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_unlock_irq(&substream->lock);
|
2020-12-06 08:35:27 +00:00
|
|
|
return result > 0 ? result : -EIO;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_unlock_irq(&substream->lock);
|
2008-01-09 16:56:07 +00:00
|
|
|
count1 = snd_rawmidi_kernel_write1(substream, buf, NULL, count);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (count1 < 0)
|
|
|
|
return result > 0 ? result : count1;
|
|
|
|
result += count1;
|
|
|
|
buf += count1;
|
|
|
|
if ((size_t)count1 < count && (file->f_flags & O_NONBLOCK))
|
|
|
|
break;
|
|
|
|
count -= count1;
|
|
|
|
}
|
2009-10-27 10:05:28 +00:00
|
|
|
if (file->f_flags & O_DSYNC) {
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_lock_irq(&substream->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
while (runtime->avail != runtime->buffer_size) {
|
2017-06-20 10:06:13 +00:00
|
|
|
wait_queue_entry_t wait;
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned int last_avail = runtime->avail;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
init_waitqueue_entry(&wait, current);
|
|
|
|
add_wait_queue(&runtime->sleep, &wait);
|
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_unlock_irq(&substream->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
timeout = schedule_timeout(30 * HZ);
|
|
|
|
remove_wait_queue(&runtime->sleep, &wait);
|
|
|
|
if (signal_pending(current))
|
|
|
|
return result > 0 ? result : -ERESTARTSYS;
|
|
|
|
if (runtime->avail == last_avail && !timeout)
|
|
|
|
return result > 0 ? result : -EIO;
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_lock_irq(&substream->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_unlock_irq(&substream->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-07-17 20:32:52 +00:00
|
|
|
static __poll_t snd_rawmidi_poll(struct file *file, poll_table *wait)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_file *rfile;
|
|
|
|
struct snd_rawmidi_runtime *runtime;
|
2017-07-03 03:27:36 +00:00
|
|
|
__poll_t mask;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
rfile = file->private_data;
|
|
|
|
if (rfile->input != NULL) {
|
|
|
|
runtime = rfile->input->runtime;
|
|
|
|
snd_rawmidi_input_trigger(rfile->input, 1);
|
|
|
|
poll_wait(file, &runtime->sleep, wait);
|
|
|
|
}
|
|
|
|
if (rfile->output != NULL) {
|
|
|
|
runtime = rfile->output->runtime;
|
|
|
|
poll_wait(file, &runtime->sleep, wait);
|
|
|
|
}
|
|
|
|
mask = 0;
|
|
|
|
if (rfile->input != NULL) {
|
|
|
|
if (snd_rawmidi_ready(rfile->input))
|
2018-02-11 22:34:03 +00:00
|
|
|
mask |= EPOLLIN | EPOLLRDNORM;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
if (rfile->output != NULL) {
|
|
|
|
if (snd_rawmidi_ready(rfile->output))
|
2018-02-11 22:34:03 +00:00
|
|
|
mask |= EPOLLOUT | EPOLLWRNORM;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
#include "rawmidi_compat.c"
|
|
|
|
#else
|
|
|
|
#define snd_rawmidi_ioctl_compat NULL
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
|
|
|
|
struct snd_info_buffer *buffer)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi *rmidi;
|
|
|
|
struct snd_rawmidi_substream *substream;
|
|
|
|
struct snd_rawmidi_runtime *runtime;
|
2020-12-06 08:35:27 +00:00
|
|
|
unsigned long buffer_size, avail, xruns;
|
2021-05-15 07:15:33 +00:00
|
|
|
unsigned int clock_type;
|
|
|
|
static const char *clock_names[4] = { "none", "realtime", "monotonic", "monotonic raw" };
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
rmidi = entry->private_data;
|
|
|
|
snd_iprintf(buffer, "%s\n\n", rmidi->name);
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
if (IS_ENABLED(CONFIG_SND_UMP))
|
|
|
|
snd_iprintf(buffer, "Type: %s\n",
|
|
|
|
rawmidi_is_ump(rmidi) ? "UMP" : "Legacy");
|
2023-09-16 06:07:25 +00:00
|
|
|
if (rmidi->ops && rmidi->ops->proc_read)
|
2023-05-23 07:53:27 +00:00
|
|
|
rmidi->ops->proc_read(entry, buffer);
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(mutex)(&rmidi->open_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) {
|
2006-10-05 14:02:22 +00:00
|
|
|
list_for_each_entry(substream,
|
|
|
|
&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams,
|
|
|
|
list) {
|
2005-04-16 22:20:36 +00:00
|
|
|
snd_iprintf(buffer,
|
|
|
|
"Output %d\n"
|
|
|
|
" Tx bytes : %lu\n",
|
|
|
|
substream->number,
|
|
|
|
(unsigned long) substream->bytes);
|
|
|
|
if (substream->opened) {
|
2009-11-10 09:14:04 +00:00
|
|
|
snd_iprintf(buffer,
|
|
|
|
" Owner PID : %d\n",
|
|
|
|
pid_vnr(substream->pid));
|
2005-04-16 22:20:36 +00:00
|
|
|
runtime = substream->runtime;
|
2024-02-27 08:52:51 +00:00
|
|
|
scoped_guard(spinlock_irq, &substream->lock) {
|
|
|
|
buffer_size = runtime->buffer_size;
|
|
|
|
avail = runtime->avail;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
snd_iprintf(buffer,
|
|
|
|
" Mode : %s\n"
|
|
|
|
" Buffer size : %lu\n"
|
|
|
|
" Avail : %lu\n",
|
|
|
|
runtime->oss ? "OSS compatible" : "native",
|
2020-12-06 08:35:27 +00:00
|
|
|
buffer_size, avail);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT) {
|
2006-10-05 14:02:22 +00:00
|
|
|
list_for_each_entry(substream,
|
|
|
|
&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams,
|
|
|
|
list) {
|
2005-04-16 22:20:36 +00:00
|
|
|
snd_iprintf(buffer,
|
|
|
|
"Input %d\n"
|
|
|
|
" Rx bytes : %lu\n",
|
|
|
|
substream->number,
|
|
|
|
(unsigned long) substream->bytes);
|
|
|
|
if (substream->opened) {
|
2009-11-10 09:14:04 +00:00
|
|
|
snd_iprintf(buffer,
|
|
|
|
" Owner PID : %d\n",
|
|
|
|
pid_vnr(substream->pid));
|
2005-04-16 22:20:36 +00:00
|
|
|
runtime = substream->runtime;
|
2024-02-27 08:52:51 +00:00
|
|
|
scoped_guard(spinlock_irq, &substream->lock) {
|
|
|
|
buffer_size = runtime->buffer_size;
|
|
|
|
avail = runtime->avail;
|
|
|
|
xruns = runtime->xruns;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
snd_iprintf(buffer,
|
|
|
|
" Buffer size : %lu\n"
|
|
|
|
" Avail : %lu\n"
|
|
|
|
" Overruns : %lu\n",
|
2020-12-06 08:35:27 +00:00
|
|
|
buffer_size, avail, xruns);
|
2021-05-15 07:15:33 +00:00
|
|
|
if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) {
|
|
|
|
clock_type = substream->clock_type >> SNDRV_RAWMIDI_MODE_CLOCK_SHIFT;
|
2021-05-19 10:54:24 +00:00
|
|
|
if (!snd_BUG_ON(clock_type >= ARRAY_SIZE(clock_names)))
|
2021-05-15 07:15:33 +00:00
|
|
|
snd_iprintf(buffer,
|
|
|
|
" Framing : tstamp\n"
|
|
|
|
" Clock type : %s\n",
|
|
|
|
clock_names[clock_type]);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Register functions
|
|
|
|
*/
|
|
|
|
|
2018-07-17 20:32:52 +00:00
|
|
|
static const struct file_operations snd_rawmidi_f_ops = {
|
2005-04-16 22:20:36 +00:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.read = snd_rawmidi_read,
|
|
|
|
.write = snd_rawmidi_write,
|
|
|
|
.open = snd_rawmidi_open,
|
|
|
|
.release = snd_rawmidi_release,
|
|
|
|
.poll = snd_rawmidi_poll,
|
|
|
|
.unlocked_ioctl = snd_rawmidi_ioctl,
|
|
|
|
.compat_ioctl = snd_rawmidi_ioctl_compat,
|
|
|
|
};
|
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi,
|
|
|
|
struct snd_rawmidi_str *stream,
|
2005-04-16 22:20:36 +00:00
|
|
|
int direction,
|
|
|
|
int count)
|
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_substream *substream;
|
2005-04-16 22:20:36 +00:00
|
|
|
int idx;
|
|
|
|
|
|
|
|
for (idx = 0; idx < count; idx++) {
|
2005-09-09 12:20:23 +00:00
|
|
|
substream = kzalloc(sizeof(*substream), GFP_KERNEL);
|
2015-03-10 14:42:14 +00:00
|
|
|
if (!substream)
|
2005-04-16 22:20:36 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
substream->stream = direction;
|
|
|
|
substream->number = idx;
|
|
|
|
substream->rmidi = rmidi;
|
|
|
|
substream->pstr = stream;
|
2022-06-17 14:40:48 +00:00
|
|
|
spin_lock_init(&substream->lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
list_add_tail(&substream->list, &stream->substreams);
|
|
|
|
stream->substream_count++;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
/* used for both rawmidi and ump */
|
|
|
|
int snd_rawmidi_init(struct snd_rawmidi *rmidi,
|
|
|
|
struct snd_card *card, char *id, int device,
|
|
|
|
int output_count, int input_count,
|
|
|
|
unsigned int info_flags)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int err;
|
2020-01-03 08:16:20 +00:00
|
|
|
static const struct snd_device_ops ops = {
|
2005-04-16 22:20:36 +00:00
|
|
|
.dev_free = snd_rawmidi_dev_free,
|
|
|
|
.dev_register = snd_rawmidi_dev_register,
|
|
|
|
.dev_disconnect = snd_rawmidi_dev_disconnect,
|
|
|
|
};
|
|
|
|
|
|
|
|
rmidi->card = card;
|
|
|
|
rmidi->device = device;
|
2006-01-16 15:29:08 +00:00
|
|
|
mutex_init(&rmidi->open_mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
init_waitqueue_head(&rmidi->open_wait);
|
2006-11-23 11:02:33 +00:00
|
|
|
INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams);
|
|
|
|
INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams);
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
rmidi->info_flags = info_flags;
|
2006-11-23 11:02:33 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (id != NULL)
|
ALSA: Convert strlcpy to strscpy when return value is unused
strlcpy is deprecated. see: Documentation/process/deprecated.rst
Change the calls that do not use the strlcpy return value to the
preferred strscpy.
Done with cocci script:
@@
expression e1, e2, e3;
@@
- strlcpy(
+ strscpy(
e1, e2, e3);
This cocci script leaves the instances where the return value is
used unchanged.
After this patch, sound/ has 3 uses of strlcpy() that need to be
manually inspected for conversion and changed one day.
$ git grep -w strlcpy sound/
sound/usb/card.c: len = strlcpy(card->longname, s, sizeof(card->longname));
sound/usb/mixer.c: return strlcpy(buf, p->name, buflen);
sound/usb/mixer.c: return strlcpy(buf, p->names[index], buflen);
Miscellenea:
o Remove trailing whitespace in conversion of sound/core/hwdep.c
Link: https://lore.kernel.org/lkml/CAHk-=wgfRnXz0W3D37d01q3JFkr_i_uTL=V6A6G1oUZcprmknw@mail.gmail.com/
Signed-off-by: Joe Perches <joe@perches.com>
Acked-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/22b393d1790bb268769d0bab7bacf0866dcb0c14.camel@perches.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-01-04 17:17:34 +00:00
|
|
|
strscpy(rmidi->id, id, sizeof(rmidi->id));
|
2015-01-29 16:55:52 +00:00
|
|
|
|
2023-08-16 16:02:48 +00:00
|
|
|
err = snd_device_alloc(&rmidi->dev, card);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
if (rawmidi_is_ump(rmidi))
|
2023-08-16 16:02:48 +00:00
|
|
|
dev_set_name(rmidi->dev, "umpC%iD%i", card->number, device);
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
else
|
2023-08-16 16:02:48 +00:00
|
|
|
dev_set_name(rmidi->dev, "midiC%iD%i", card->number, device);
|
2015-01-29 16:55:52 +00:00
|
|
|
|
2018-07-17 20:32:52 +00:00
|
|
|
err = snd_rawmidi_alloc_substreams(rmidi,
|
|
|
|
&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT],
|
|
|
|
SNDRV_RAWMIDI_STREAM_INPUT,
|
|
|
|
input_count);
|
2018-07-17 20:45:50 +00:00
|
|
|
if (err < 0)
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
return err;
|
2018-07-17 20:32:52 +00:00
|
|
|
err = snd_rawmidi_alloc_substreams(rmidi,
|
|
|
|
&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT],
|
|
|
|
SNDRV_RAWMIDI_STREAM_OUTPUT,
|
|
|
|
output_count);
|
2018-07-17 20:45:50 +00:00
|
|
|
if (err < 0)
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
return err;
|
2018-07-17 20:32:52 +00:00
|
|
|
err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops);
|
2018-07-17 20:45:50 +00:00
|
|
|
if (err < 0)
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
return err;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(snd_rawmidi_init);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* snd_rawmidi_new - create a rawmidi instance
|
|
|
|
* @card: the card instance
|
|
|
|
* @id: the id string
|
|
|
|
* @device: the device index
|
|
|
|
* @output_count: the number of output streams
|
|
|
|
* @input_count: the number of input streams
|
|
|
|
* @rrawmidi: the pointer to store the new rawmidi instance
|
|
|
|
*
|
|
|
|
* Creates a new rawmidi instance.
|
|
|
|
* Use snd_rawmidi_set_ops() to set the operators to the new instance.
|
|
|
|
*
|
|
|
|
* Return: Zero if successful, or a negative error code on failure.
|
|
|
|
*/
|
|
|
|
int snd_rawmidi_new(struct snd_card *card, char *id, int device,
|
|
|
|
int output_count, int input_count,
|
|
|
|
struct snd_rawmidi **rrawmidi)
|
|
|
|
{
|
|
|
|
struct snd_rawmidi *rmidi;
|
|
|
|
int err;
|
2018-07-17 20:45:50 +00:00
|
|
|
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
if (rrawmidi)
|
|
|
|
*rrawmidi = NULL;
|
|
|
|
rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);
|
|
|
|
if (!rmidi)
|
|
|
|
return -ENOMEM;
|
|
|
|
err = snd_rawmidi_init(rmidi, card, id, device,
|
|
|
|
output_count, input_count, 0);
|
|
|
|
if (err < 0) {
|
|
|
|
snd_rawmidi_free(rmidi);
|
|
|
|
return err;
|
|
|
|
}
|
2008-08-08 15:09:09 +00:00
|
|
|
if (rrawmidi)
|
|
|
|
*rrawmidi = rmidi;
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_new);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_substream *substream;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
while (!list_empty(&stream->substreams)) {
|
2005-11-17 12:56:51 +00:00
|
|
|
substream = list_entry(stream->substreams.next, struct snd_rawmidi_substream, list);
|
2005-04-16 22:20:36 +00:00
|
|
|
list_del(&substream->list);
|
|
|
|
kfree(substream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
/* called from ump.c, too */
|
|
|
|
int snd_rawmidi_free(struct snd_rawmidi *rmidi)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-08-08 15:09:09 +00:00
|
|
|
if (!rmidi)
|
|
|
|
return 0;
|
2006-06-23 12:38:23 +00:00
|
|
|
|
|
|
|
snd_info_free_entry(rmidi->proc_entry);
|
|
|
|
rmidi->proc_entry = NULL;
|
|
|
|
if (rmidi->ops && rmidi->ops->dev_unregister)
|
|
|
|
rmidi->ops->dev_unregister(rmidi);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]);
|
|
|
|
snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]);
|
|
|
|
if (rmidi->private_free)
|
|
|
|
rmidi->private_free(rmidi);
|
2023-08-16 16:02:48 +00:00
|
|
|
put_device(rmidi->dev);
|
|
|
|
kfree(rmidi);
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
EXPORT_SYMBOL_GPL(snd_rawmidi_free);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static int snd_rawmidi_dev_free(struct snd_device *device)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi *rmidi = device->device_data;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
return snd_rawmidi_free(rmidi);
|
|
|
|
}
|
|
|
|
|
ALSA: seq: Allow the modular sequencer registration
Many drivers bind the sequencer stuff in off-load by another driver
module, so that it's loaded only on demand. In the current code, this
mechanism doesn't work when the driver is built-in while the sequencer
is module. We check with IS_REACHABLE() and enable only when the
sequencer is in the same level of build.
However, this is basically a overshoot. The binder code
(snd-seq-device) is an individual module from the sequencer core
(snd-seq), and we just have to make the former a built-in while
keeping the latter a module for allowing the scenario like the above.
This patch achieves that by rewriting Kconfig slightly. Now, a driver
that provides the manual sequencer device binding should select
CONFIG_SND_SEQ_DEVICE in a way as
select SND_SEQ_DEVICE if SND_SEQUENCER != n
Note that the "!=n" is needed here to avoid the influence of the
sequencer core is module while the driver is built-in.
Also, since rawmidi.o may be linked with snd_seq_device.o when
built-in, we have to shuffle the code to make the linker happy.
(the kernel linker isn't smart enough yet to handle such a case.)
That is, snd_seq_device.c is moved to sound/core from sound/core/seq,
as well as Makefile.
Last but not least, the patch replaces the code using IS_REACHABLE()
with IS_ENABLED(), since now the condition meets always when enabled.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2017-06-09 13:11:58 +00:00
|
|
|
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
|
2005-11-17 12:56:51 +00:00
|
|
|
static void snd_rawmidi_dev_seq_free(struct snd_seq_device *device)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi *rmidi = device->private_data;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
rmidi->seq_dev = NULL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static int snd_rawmidi_dev_register(struct snd_device *device)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-20 13:06:59 +00:00
|
|
|
int err;
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_info_entry *entry;
|
2005-04-16 22:20:36 +00:00
|
|
|
char name[16];
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi *rmidi = device->device_data;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (rmidi->device >= SNDRV_RAWMIDI_DEVICES)
|
|
|
|
return -ENOMEM;
|
2018-07-17 20:45:50 +00:00
|
|
|
err = 0;
|
2024-02-27 08:52:51 +00:00
|
|
|
scoped_guard(mutex, ®ister_mutex) {
|
|
|
|
if (snd_rawmidi_search(rmidi->card, rmidi->device))
|
|
|
|
err = -EBUSY;
|
|
|
|
else
|
|
|
|
list_add_tail(&rmidi->list, &snd_rawmidi_devices);
|
|
|
|
}
|
2018-07-17 20:45:50 +00:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2015-01-30 07:34:58 +00:00
|
|
|
err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI,
|
|
|
|
rmidi->card, rmidi->device,
|
2023-08-16 16:02:48 +00:00
|
|
|
&snd_rawmidi_f_ops, rmidi, rmidi->dev);
|
2015-01-29 16:55:52 +00:00
|
|
|
if (err < 0) {
|
|
|
|
rmidi_err(rmidi, "unable to register\n");
|
2018-07-17 20:45:50 +00:00
|
|
|
goto error;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2018-07-17 20:45:50 +00:00
|
|
|
if (rmidi->ops && rmidi->ops->dev_register) {
|
|
|
|
err = rmidi->ops->dev_register(rmidi);
|
|
|
|
if (err < 0)
|
|
|
|
goto error_unregister;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
#ifdef CONFIG_SND_OSSEMUL
|
|
|
|
rmidi->ossreg = 0;
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
if (!rawmidi_is_ump(rmidi) &&
|
|
|
|
(int)rmidi->device == midi_map[rmidi->card->number]) {
|
2005-04-16 22:20:36 +00:00
|
|
|
if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI,
|
2005-11-20 13:06:59 +00:00
|
|
|
rmidi->card, 0, &snd_rawmidi_f_ops,
|
2014-02-04 12:51:45 +00:00
|
|
|
rmidi) < 0) {
|
2014-02-04 17:21:39 +00:00
|
|
|
rmidi_err(rmidi,
|
|
|
|
"unable to register OSS rawmidi device %i:%i\n",
|
|
|
|
rmidi->card->number, 0);
|
2005-04-16 22:20:36 +00:00
|
|
|
} else {
|
|
|
|
rmidi->ossreg++;
|
|
|
|
#ifdef SNDRV_OSS_INFO_DEV_MIDI
|
|
|
|
snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number, rmidi->name);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
if (!rawmidi_is_ump(rmidi) &&
|
|
|
|
(int)rmidi->device == amidi_map[rmidi->card->number]) {
|
2005-04-16 22:20:36 +00:00
|
|
|
if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI,
|
2005-11-20 13:06:59 +00:00
|
|
|
rmidi->card, 1, &snd_rawmidi_f_ops,
|
2014-02-04 12:51:45 +00:00
|
|
|
rmidi) < 0) {
|
2014-02-04 17:21:39 +00:00
|
|
|
rmidi_err(rmidi,
|
|
|
|
"unable to register OSS rawmidi device %i:%i\n",
|
|
|
|
rmidi->card->number, 1);
|
2005-04-16 22:20:36 +00:00
|
|
|
} else {
|
|
|
|
rmidi->ossreg++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_SND_OSSEMUL */
|
|
|
|
sprintf(name, "midi%d", rmidi->device);
|
|
|
|
entry = snd_info_create_card_entry(rmidi->card, name, rmidi->card->proc_root);
|
|
|
|
if (entry) {
|
|
|
|
entry->private_data = rmidi;
|
|
|
|
entry->c.text.read = snd_rawmidi_proc_info_read;
|
|
|
|
if (snd_info_register(entry) < 0) {
|
|
|
|
snd_info_free_entry(entry);
|
|
|
|
entry = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rmidi->proc_entry = entry;
|
ALSA: seq: Allow the modular sequencer registration
Many drivers bind the sequencer stuff in off-load by another driver
module, so that it's loaded only on demand. In the current code, this
mechanism doesn't work when the driver is built-in while the sequencer
is module. We check with IS_REACHABLE() and enable only when the
sequencer is in the same level of build.
However, this is basically a overshoot. The binder code
(snd-seq-device) is an individual module from the sequencer core
(snd-seq), and we just have to make the former a built-in while
keeping the latter a module for allowing the scenario like the above.
This patch achieves that by rewriting Kconfig slightly. Now, a driver
that provides the manual sequencer device binding should select
CONFIG_SND_SEQ_DEVICE in a way as
select SND_SEQ_DEVICE if SND_SEQUENCER != n
Note that the "!=n" is needed here to avoid the influence of the
sequencer core is module while the driver is built-in.
Also, since rawmidi.o may be linked with snd_seq_device.o when
built-in, we have to shuffle the code to make the linker happy.
(the kernel linker isn't smart enough yet to handle such a case.)
That is, snd_seq_device.c is moved to sound/core from sound/core/seq,
as well as Makefile.
Last but not least, the patch replaces the code using IS_REACHABLE()
with IS_ENABLED(), since now the condition meets always when enabled.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2017-06-09 13:11:58 +00:00
|
|
|
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
|
ALSA: rawmidi: UMP support
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.
The basic design is that a rawmidi instance is assigned to each UMP
Endpoint. A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.
Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint. A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group. A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*". The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.
The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.
A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.
As of this commit, no ALSA sequencer instance is attached to UMP
devices yet. They will be supported by later patches.
Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2023-05-23 07:53:24 +00:00
|
|
|
/* no own registration mechanism? */
|
|
|
|
if (!rmidi->ops || !rmidi->ops->dev_register) {
|
2005-04-16 22:20:36 +00:00
|
|
|
if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) {
|
|
|
|
rmidi->seq_dev->private_data = rmidi;
|
|
|
|
rmidi->seq_dev->private_free = snd_rawmidi_dev_seq_free;
|
|
|
|
sprintf(rmidi->seq_dev->name, "MIDI %d-%d", rmidi->card->number, rmidi->device);
|
|
|
|
snd_device_register(rmidi->card, rmidi->seq_dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return 0;
|
2018-07-17 20:45:50 +00:00
|
|
|
|
|
|
|
error_unregister:
|
2023-08-16 16:02:48 +00:00
|
|
|
snd_unregister_device(rmidi->dev);
|
2018-07-17 20:45:50 +00:00
|
|
|
error:
|
2024-02-27 08:52:51 +00:00
|
|
|
scoped_guard(mutex, ®ister_mutex)
|
|
|
|
list_del(&rmidi->list);
|
2018-07-17 20:45:50 +00:00
|
|
|
return err;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2005-11-17 12:56:51 +00:00
|
|
|
static int snd_rawmidi_dev_disconnect(struct snd_device *device)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi *rmidi = device->device_data;
|
2012-10-16 14:43:39 +00:00
|
|
|
int dir;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2024-02-27 08:52:51 +00:00
|
|
|
guard(mutex)(®ister_mutex);
|
|
|
|
guard(mutex)(&rmidi->open_mutex);
|
2012-10-16 14:43:39 +00:00
|
|
|
wake_up(&rmidi->open_wait);
|
2005-11-20 13:06:59 +00:00
|
|
|
list_del_init(&rmidi->list);
|
2012-10-16 14:43:39 +00:00
|
|
|
for (dir = 0; dir < 2; dir++) {
|
|
|
|
struct snd_rawmidi_substream *s;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2012-10-16 14:43:39 +00:00
|
|
|
list_for_each_entry(s, &rmidi->streams[dir].substreams, list) {
|
|
|
|
if (s->runtime)
|
|
|
|
wake_up(&s->runtime->sleep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
#ifdef CONFIG_SND_OSSEMUL
|
|
|
|
if (rmidi->ossreg) {
|
|
|
|
if ((int)rmidi->device == midi_map[rmidi->card->number]) {
|
|
|
|
snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 0);
|
|
|
|
#ifdef SNDRV_OSS_INFO_DEV_MIDI
|
|
|
|
snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIDI, rmidi->card->number);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
if ((int)rmidi->device == amidi_map[rmidi->card->number])
|
|
|
|
snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 1);
|
|
|
|
rmidi->ossreg = 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_SND_OSSEMUL */
|
2023-08-16 16:02:48 +00:00
|
|
|
snd_unregister_device(rmidi->dev);
|
2006-06-23 12:38:23 +00:00
|
|
|
return 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* snd_rawmidi_set_ops - set the rawmidi operators
|
|
|
|
* @rmidi: the rawmidi instance
|
|
|
|
* @stream: the stream direction, SNDRV_RAWMIDI_STREAM_XXX
|
|
|
|
* @ops: the operator table
|
|
|
|
*
|
|
|
|
* Sets the rawmidi operators for the given stream direction.
|
|
|
|
*/
|
2005-11-17 12:56:51 +00:00
|
|
|
void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream,
|
2017-01-05 16:01:14 +00:00
|
|
|
const struct snd_rawmidi_ops *ops)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-17 12:56:51 +00:00
|
|
|
struct snd_rawmidi_substream *substream;
|
2018-07-17 20:32:52 +00:00
|
|
|
|
2006-10-05 14:02:22 +00:00
|
|
|
list_for_each_entry(substream, &rmidi->streams[stream].substreams, list)
|
2005-04-16 22:20:36 +00:00
|
|
|
substream->ops = ops;
|
|
|
|
}
|
2014-02-27 15:00:17 +00:00
|
|
|
EXPORT_SYMBOL(snd_rawmidi_set_ops);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ENTRY functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int __init alsa_rawmidi_init(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
snd_ctl_register_ioctl(snd_rawmidi_control_ioctl);
|
|
|
|
snd_ctl_register_ioctl_compat(snd_rawmidi_control_ioctl);
|
|
|
|
#ifdef CONFIG_SND_OSSEMUL
|
|
|
|
{ int i;
|
|
|
|
/* check device map table */
|
|
|
|
for (i = 0; i < SNDRV_CARDS; i++) {
|
|
|
|
if (midi_map[i] < 0 || midi_map[i] >= SNDRV_RAWMIDI_DEVICES) {
|
2014-02-04 17:21:39 +00:00
|
|
|
pr_err("ALSA: rawmidi: invalid midi_map[%d] = %d\n",
|
|
|
|
i, midi_map[i]);
|
2005-04-16 22:20:36 +00:00
|
|
|
midi_map[i] = 0;
|
|
|
|
}
|
|
|
|
if (amidi_map[i] < 0 || amidi_map[i] >= SNDRV_RAWMIDI_DEVICES) {
|
2014-02-04 17:21:39 +00:00
|
|
|
pr_err("ALSA: rawmidi: invalid amidi_map[%d] = %d\n",
|
|
|
|
i, amidi_map[i]);
|
2005-04-16 22:20:36 +00:00
|
|
|
amidi_map[i] = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_SND_OSSEMUL */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit alsa_rawmidi_exit(void)
|
|
|
|
{
|
|
|
|
snd_ctl_unregister_ioctl(snd_rawmidi_control_ioctl);
|
|
|
|
snd_ctl_unregister_ioctl_compat(snd_rawmidi_control_ioctl);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(alsa_rawmidi_init)
|
|
|
|
module_exit(alsa_rawmidi_exit)
|