mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
ALSA: seq: Add UMP support
Starting from this commit, we add the basic support of UMP (Universal MIDI Packet) events on ALSA sequencer infrastructure. The biggest change here is that, for transferring UMP packets that are up to 128 bits, we extend the data payload of ALSA sequencer event record when the client is declared to support for the new UMP events. A new event flag bit, SNDRV_SEQ_EVENT_UMP, is defined and it shall be set for the UMP packet events that have the larger payload of 128 bits, defined as struct snd_seq_ump_event. For controlling the UMP feature enablement in kernel, a new Kconfig, CONFIG_SND_SEQ_UMP is introduced. The extended event for UMP is available only when this Kconfig item is set. Similarly, the size of the internal snd_seq_event_cell also increases (in 4 bytes) when the Kconfig item is set. (But the size increase is effective only for 32bit architectures; 64bit archs already have padding there.) Overall, when CONFIG_SND_SEQ_UMP isn't set, there is no change in the event and cell, keeping the old sizes. For applications that want to access the UMP packets, first of all, a sequencer client has to declare the user-protocol to match with the latest one via the new SNDRV_SEQ_IOCTL_USER_PVERSION; otherwise it's treated as if a legacy client without UMP support. Then the client can switch to the new UMP mode (MIDI 1.0 or MIDI 2.0) with a new field, midi_version, in snd_seq_client_info. When switched to UMP mode (midi_version = 1 or 2), the client can write the UMP events with SNDRV_SEQ_EVENT_UMP flag. For reads, the alignment size is changed from snd_seq_event (28 bytes) to snd_seq_ump_event (32 bytes). When a UMP sequencer event is delivered to a legacy sequencer client, it's ignored or handled as an error. Conceptually, ALSA sequencer client and port correspond to the UMP Endpoint and Group, respectively; each client may have multiple ports and each port has the fixed number (16) of channels, total up to 256 channels. As of this commit, ALSA sequencer core just sends and receives the UMP events as-is from/to clients. The automatic conversions between the legacy events and the new UMP events will be implemented in a later patch. Along with this commit, bump the sequencer protocol version to 1.0.3. Reviewed-by: Jaroslav Kysela <perex@perex.cz> Link: https://lore.kernel.org/r/20230523075358.9672-26-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
afb72505e4
commit
46397622a3
@ -65,6 +65,10 @@
|
|||||||
#define snd_seq_ev_is_abstime(ev) (snd_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_ABS)
|
#define snd_seq_ev_is_abstime(ev) (snd_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_ABS)
|
||||||
#define snd_seq_ev_is_reltime(ev) (snd_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_REL)
|
#define snd_seq_ev_is_reltime(ev) (snd_seq_ev_timemode_type(ev) == SNDRV_SEQ_TIME_MODE_REL)
|
||||||
|
|
||||||
|
/* check whether the given event is a UMP event */
|
||||||
|
#define snd_seq_ev_is_ump(ev) \
|
||||||
|
(IS_ENABLED(CONFIG_SND_SEQ_UMP) && ((ev)->flags & SNDRV_SEQ_EVENT_UMP))
|
||||||
|
|
||||||
/* queue sync port */
|
/* queue sync port */
|
||||||
#define snd_seq_queue_sync_port(q) ((q) + 16)
|
#define snd_seq_queue_sync_port(q) ((q) + 16)
|
||||||
|
|
||||||
|
@ -75,6 +75,14 @@ int snd_seq_expand_var_event_at(const struct snd_seq_event *event, int count,
|
|||||||
int snd_seq_dump_var_event(const struct snd_seq_event *event,
|
int snd_seq_dump_var_event(const struct snd_seq_event *event,
|
||||||
snd_seq_dump_func_t func, void *private_data);
|
snd_seq_dump_func_t func, void *private_data);
|
||||||
|
|
||||||
|
/* size of the event packet; it can be greater than snd_seq_event size */
|
||||||
|
static inline size_t snd_seq_event_packet_size(struct snd_seq_event *ev)
|
||||||
|
{
|
||||||
|
if (snd_seq_ev_is_ump(ev))
|
||||||
|
return sizeof(struct snd_seq_ump_event);
|
||||||
|
return sizeof(struct snd_seq_event);
|
||||||
|
}
|
||||||
|
|
||||||
/* interface for OSS emulation */
|
/* interface for OSS emulation */
|
||||||
int snd_seq_set_queue_tempo(int client, struct snd_seq_queue_tempo *tempo);
|
int snd_seq_set_queue_tempo(int client, struct snd_seq_queue_tempo *tempo);
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#include <sound/asound.h>
|
#include <sound/asound.h>
|
||||||
|
|
||||||
/** version of the sequencer */
|
/** version of the sequencer */
|
||||||
#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 2)
|
#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 3)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* definition of sequencer event types
|
* definition of sequencer event types
|
||||||
@ -174,6 +174,7 @@ struct snd_seq_connect {
|
|||||||
#define SNDRV_SEQ_PRIORITY_HIGH (1<<4) /* event should be processed before others */
|
#define SNDRV_SEQ_PRIORITY_HIGH (1<<4) /* event should be processed before others */
|
||||||
#define SNDRV_SEQ_PRIORITY_MASK (1<<4)
|
#define SNDRV_SEQ_PRIORITY_MASK (1<<4)
|
||||||
|
|
||||||
|
#define SNDRV_SEQ_EVENT_UMP (1<<5) /* event holds a UMP packet */
|
||||||
|
|
||||||
/* note event */
|
/* note event */
|
||||||
struct snd_seq_ev_note {
|
struct snd_seq_ev_note {
|
||||||
@ -252,6 +253,19 @@ struct snd_seq_ev_quote {
|
|||||||
struct snd_seq_event *event; /* quoted event */
|
struct snd_seq_event *event; /* quoted event */
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
union snd_seq_event_data { /* event data... */
|
||||||
|
struct snd_seq_ev_note note;
|
||||||
|
struct snd_seq_ev_ctrl control;
|
||||||
|
struct snd_seq_ev_raw8 raw8;
|
||||||
|
struct snd_seq_ev_raw32 raw32;
|
||||||
|
struct snd_seq_ev_ext ext;
|
||||||
|
struct snd_seq_ev_queue_control queue;
|
||||||
|
union snd_seq_timestamp time;
|
||||||
|
struct snd_seq_addr addr;
|
||||||
|
struct snd_seq_connect connect;
|
||||||
|
struct snd_seq_result result;
|
||||||
|
struct snd_seq_ev_quote quote;
|
||||||
|
};
|
||||||
|
|
||||||
/* sequencer event */
|
/* sequencer event */
|
||||||
struct snd_seq_event {
|
struct snd_seq_event {
|
||||||
@ -262,25 +276,27 @@ struct snd_seq_event {
|
|||||||
unsigned char queue; /* schedule queue */
|
unsigned char queue; /* schedule queue */
|
||||||
union snd_seq_timestamp time; /* schedule time */
|
union snd_seq_timestamp time; /* schedule time */
|
||||||
|
|
||||||
|
|
||||||
struct snd_seq_addr source; /* source address */
|
struct snd_seq_addr source; /* source address */
|
||||||
struct snd_seq_addr dest; /* destination address */
|
struct snd_seq_addr dest; /* destination address */
|
||||||
|
|
||||||
union { /* event data... */
|
union snd_seq_event_data data;
|
||||||
struct snd_seq_ev_note note;
|
|
||||||
struct snd_seq_ev_ctrl control;
|
|
||||||
struct snd_seq_ev_raw8 raw8;
|
|
||||||
struct snd_seq_ev_raw32 raw32;
|
|
||||||
struct snd_seq_ev_ext ext;
|
|
||||||
struct snd_seq_ev_queue_control queue;
|
|
||||||
union snd_seq_timestamp time;
|
|
||||||
struct snd_seq_addr addr;
|
|
||||||
struct snd_seq_connect connect;
|
|
||||||
struct snd_seq_result result;
|
|
||||||
struct snd_seq_ev_quote quote;
|
|
||||||
} data;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* (compatible) event for UMP-capable clients */
|
||||||
|
struct snd_seq_ump_event {
|
||||||
|
snd_seq_event_type_t type; /* event type */
|
||||||
|
unsigned char flags; /* event flags */
|
||||||
|
char tag;
|
||||||
|
unsigned char queue; /* schedule queue */
|
||||||
|
union snd_seq_timestamp time; /* schedule time */
|
||||||
|
struct snd_seq_addr source; /* source address */
|
||||||
|
struct snd_seq_addr dest; /* destination address */
|
||||||
|
|
||||||
|
union {
|
||||||
|
union snd_seq_event_data data;
|
||||||
|
unsigned int ump[4];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* bounce event - stored as variable size data
|
* bounce event - stored as variable size data
|
||||||
@ -344,9 +360,14 @@ struct snd_seq_client_info {
|
|||||||
int event_lost; /* number of lost events */
|
int event_lost; /* number of lost events */
|
||||||
int card; /* RO: card number[kernel] */
|
int card; /* RO: card number[kernel] */
|
||||||
int pid; /* RO: pid[user] */
|
int pid; /* RO: pid[user] */
|
||||||
char reserved[56]; /* for future use */
|
unsigned int midi_version; /* MIDI version */
|
||||||
|
char reserved[52]; /* for future use */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* MIDI version numbers in client info */
|
||||||
|
#define SNDRV_SEQ_CLIENT_LEGACY_MIDI 0 /* Legacy client */
|
||||||
|
#define SNDRV_SEQ_CLIENT_UMP_MIDI_1_0 1 /* UMP MIDI 1.0 */
|
||||||
|
#define SNDRV_SEQ_CLIENT_UMP_MIDI_2_0 2 /* UMP MIDI 2.0 */
|
||||||
|
|
||||||
/* client pool size */
|
/* client pool size */
|
||||||
struct snd_seq_client_pool {
|
struct snd_seq_client_pool {
|
||||||
|
@ -60,4 +60,11 @@ config SND_SEQ_MIDI_EMUL
|
|||||||
config SND_SEQ_VIRMIDI
|
config SND_SEQ_VIRMIDI
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
|
config SND_SEQ_UMP
|
||||||
|
bool "Support for UMP events"
|
||||||
|
help
|
||||||
|
Say Y here to enable the support for handling UMP (Universal MIDI
|
||||||
|
Packet) events via ALSA sequencer infrastructure, which is an
|
||||||
|
essential feature for enabling MIDI 2.0 support.
|
||||||
|
|
||||||
endif # SND_SEQUENCER
|
endif # SND_SEQUENCER
|
||||||
|
@ -387,6 +387,15 @@ static int snd_seq_release(struct inode *inode, struct file *file)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool event_is_compatible(const struct snd_seq_client *client,
|
||||||
|
const struct snd_seq_event *ev)
|
||||||
|
{
|
||||||
|
if (snd_seq_ev_is_ump(ev) && !client->midi_version)
|
||||||
|
return false;
|
||||||
|
if (snd_seq_ev_is_ump(ev) && snd_seq_ev_is_variable(ev))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* handle client read() */
|
/* handle client read() */
|
||||||
/* possible error values:
|
/* possible error values:
|
||||||
@ -400,6 +409,7 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
|
|||||||
{
|
{
|
||||||
struct snd_seq_client *client = file->private_data;
|
struct snd_seq_client *client = file->private_data;
|
||||||
struct snd_seq_fifo *fifo;
|
struct snd_seq_fifo *fifo;
|
||||||
|
size_t aligned_size;
|
||||||
int err;
|
int err;
|
||||||
long result = 0;
|
long result = 0;
|
||||||
struct snd_seq_event_cell *cell;
|
struct snd_seq_event_cell *cell;
|
||||||
@ -431,43 +441,54 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
|
|||||||
err = 0;
|
err = 0;
|
||||||
snd_seq_fifo_lock(fifo);
|
snd_seq_fifo_lock(fifo);
|
||||||
|
|
||||||
|
if (client->midi_version > 0)
|
||||||
|
aligned_size = sizeof(struct snd_seq_ump_event);
|
||||||
|
else
|
||||||
|
aligned_size = sizeof(struct snd_seq_event);
|
||||||
|
|
||||||
/* while data available in queue */
|
/* while data available in queue */
|
||||||
while (count >= sizeof(struct snd_seq_event)) {
|
while (count >= aligned_size) {
|
||||||
int nonblock;
|
int nonblock;
|
||||||
|
|
||||||
nonblock = (file->f_flags & O_NONBLOCK) || result > 0;
|
nonblock = (file->f_flags & O_NONBLOCK) || result > 0;
|
||||||
err = snd_seq_fifo_cell_out(fifo, &cell, nonblock);
|
err = snd_seq_fifo_cell_out(fifo, &cell, nonblock);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
break;
|
break;
|
||||||
|
if (!event_is_compatible(client, &cell->event)) {
|
||||||
|
snd_seq_cell_free(cell);
|
||||||
|
cell = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (snd_seq_ev_is_variable(&cell->event)) {
|
if (snd_seq_ev_is_variable(&cell->event)) {
|
||||||
struct snd_seq_event tmpev;
|
struct snd_seq_ump_event tmpev;
|
||||||
tmpev = cell->event;
|
|
||||||
|
memcpy(&tmpev, &cell->event, aligned_size);
|
||||||
tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK;
|
tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK;
|
||||||
if (copy_to_user(buf, &tmpev, sizeof(struct snd_seq_event))) {
|
if (copy_to_user(buf, &tmpev, aligned_size)) {
|
||||||
err = -EFAULT;
|
err = -EFAULT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
count -= sizeof(struct snd_seq_event);
|
count -= aligned_size;
|
||||||
buf += sizeof(struct snd_seq_event);
|
buf += aligned_size;
|
||||||
err = snd_seq_expand_var_event(&cell->event, count,
|
err = snd_seq_expand_var_event(&cell->event, count,
|
||||||
(char __force *)buf, 0,
|
(char __force *)buf, 0,
|
||||||
sizeof(struct snd_seq_event));
|
aligned_size);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
break;
|
break;
|
||||||
result += err;
|
result += err;
|
||||||
count -= err;
|
count -= err;
|
||||||
buf += err;
|
buf += err;
|
||||||
} else {
|
} else {
|
||||||
if (copy_to_user(buf, &cell->event, sizeof(struct snd_seq_event))) {
|
if (copy_to_user(buf, &cell->event, aligned_size)) {
|
||||||
err = -EFAULT;
|
err = -EFAULT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
count -= sizeof(struct snd_seq_event);
|
count -= aligned_size;
|
||||||
buf += sizeof(struct snd_seq_event);
|
buf += aligned_size;
|
||||||
}
|
}
|
||||||
snd_seq_cell_free(cell);
|
snd_seq_cell_free(cell);
|
||||||
cell = NULL; /* to be sure */
|
cell = NULL; /* to be sure */
|
||||||
result += sizeof(struct snd_seq_event);
|
result += aligned_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
@ -665,15 +686,17 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
|
|||||||
{
|
{
|
||||||
struct snd_seq_subscribers *subs;
|
struct snd_seq_subscribers *subs;
|
||||||
int err, result = 0, num_ev = 0;
|
int err, result = 0, num_ev = 0;
|
||||||
struct snd_seq_event event_saved;
|
|
||||||
struct snd_seq_client_port *src_port;
|
struct snd_seq_client_port *src_port;
|
||||||
|
union __snd_seq_event event_saved;
|
||||||
|
size_t saved_size;
|
||||||
struct snd_seq_port_subs_info *grp;
|
struct snd_seq_port_subs_info *grp;
|
||||||
|
|
||||||
src_port = snd_seq_port_use_ptr(client, event->source.port);
|
src_port = snd_seq_port_use_ptr(client, event->source.port);
|
||||||
if (src_port == NULL)
|
if (src_port == NULL)
|
||||||
return -EINVAL; /* invalid source port */
|
return -EINVAL; /* invalid source port */
|
||||||
/* save original event record */
|
/* save original event record */
|
||||||
event_saved = *event;
|
saved_size = snd_seq_event_packet_size(event);
|
||||||
|
memcpy(&event_saved, event, saved_size);
|
||||||
grp = &src_port->c_src;
|
grp = &src_port->c_src;
|
||||||
|
|
||||||
/* lock list */
|
/* lock list */
|
||||||
@ -700,14 +723,13 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
|
|||||||
}
|
}
|
||||||
num_ev++;
|
num_ev++;
|
||||||
/* restore original event record */
|
/* restore original event record */
|
||||||
*event = event_saved;
|
memcpy(event, &event_saved, saved_size);
|
||||||
}
|
}
|
||||||
if (atomic)
|
if (atomic)
|
||||||
read_unlock(&grp->list_lock);
|
read_unlock(&grp->list_lock);
|
||||||
else
|
else
|
||||||
up_read(&grp->list_mutex);
|
up_read(&grp->list_mutex);
|
||||||
*event = event_saved; /* restore */
|
memcpy(event, &event_saved, saved_size);
|
||||||
snd_seq_port_unlock(src_port);
|
|
||||||
return (result < 0) ? result : num_ev;
|
return (result < 0) ? result : num_ev;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -769,7 +791,8 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->event.type == SNDRV_SEQ_EVENT_NOTE) {
|
if (!snd_seq_ev_is_ump(&cell->event) &&
|
||||||
|
cell->event.type == SNDRV_SEQ_EVENT_NOTE) {
|
||||||
/* NOTE event:
|
/* NOTE event:
|
||||||
* the event cell is re-used as a NOTE-OFF event and
|
* the event cell is re-used as a NOTE-OFF event and
|
||||||
* enqueued again.
|
* enqueued again.
|
||||||
@ -793,7 +816,7 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
|
|||||||
/* add the duration time */
|
/* add the duration time */
|
||||||
switch (ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) {
|
switch (ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) {
|
||||||
case SNDRV_SEQ_TIME_STAMP_TICK:
|
case SNDRV_SEQ_TIME_STAMP_TICK:
|
||||||
ev->time.tick += ev->data.note.duration;
|
cell->event.time.tick += ev->data.note.duration;
|
||||||
break;
|
break;
|
||||||
case SNDRV_SEQ_TIME_STAMP_REAL:
|
case SNDRV_SEQ_TIME_STAMP_REAL:
|
||||||
/* unit for duration is ms */
|
/* unit for duration is ms */
|
||||||
@ -850,7 +873,8 @@ static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
|
|||||||
|
|
||||||
/* direct event processing without enqueued */
|
/* direct event processing without enqueued */
|
||||||
if (snd_seq_ev_is_direct(event)) {
|
if (snd_seq_ev_is_direct(event)) {
|
||||||
if (event->type == SNDRV_SEQ_EVENT_NOTE)
|
if (!snd_seq_ev_is_ump(event) &&
|
||||||
|
event->type == SNDRV_SEQ_EVENT_NOTE)
|
||||||
return -EINVAL; /* this event must be enqueued! */
|
return -EINVAL; /* this event must be enqueued! */
|
||||||
return snd_seq_deliver_event(client, event, atomic, hop);
|
return snd_seq_deliver_event(client, event, atomic, hop);
|
||||||
}
|
}
|
||||||
@ -920,7 +944,8 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
|
|||||||
struct snd_seq_client *client = file->private_data;
|
struct snd_seq_client *client = file->private_data;
|
||||||
int written = 0, len;
|
int written = 0, len;
|
||||||
int err, handled;
|
int err, handled;
|
||||||
struct snd_seq_event event;
|
union __snd_seq_event __event;
|
||||||
|
struct snd_seq_event *ev = &__event.legacy;
|
||||||
|
|
||||||
if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT))
|
if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT))
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
@ -946,49 +971,66 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
|
|||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
while (count >= sizeof(struct snd_seq_event)) {
|
while (count >= sizeof(struct snd_seq_event)) {
|
||||||
/* Read in the event header from the user */
|
/* Read in the event header from the user */
|
||||||
len = sizeof(event);
|
len = sizeof(struct snd_seq_event);
|
||||||
if (copy_from_user(&event, buf, len)) {
|
if (copy_from_user(ev, buf, len)) {
|
||||||
err = -EFAULT;
|
err = -EFAULT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
event.source.client = client->number; /* fill in client number */
|
/* read in the rest bytes for UMP events */
|
||||||
|
if (snd_seq_ev_is_ump(ev)) {
|
||||||
|
if (count < sizeof(struct snd_seq_ump_event))
|
||||||
|
break;
|
||||||
|
if (copy_from_user((char *)ev + len, buf + len,
|
||||||
|
sizeof(struct snd_seq_ump_event) - len)) {
|
||||||
|
err = -EFAULT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len = sizeof(struct snd_seq_ump_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
ev->source.client = client->number; /* fill in client number */
|
||||||
/* Check for extension data length */
|
/* Check for extension data length */
|
||||||
if (check_event_type_and_length(&event)) {
|
if (check_event_type_and_length(ev)) {
|
||||||
|
err = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!event_is_compatible(client, ev)) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check for special events */
|
/* check for special events */
|
||||||
if (event.type == SNDRV_SEQ_EVENT_NONE)
|
if (!snd_seq_ev_is_ump(ev)) {
|
||||||
goto __skip_event;
|
if (ev->type == SNDRV_SEQ_EVENT_NONE)
|
||||||
else if (snd_seq_ev_is_reserved(&event)) {
|
goto __skip_event;
|
||||||
err = -EINVAL;
|
else if (snd_seq_ev_is_reserved(ev)) {
|
||||||
break;
|
err = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (snd_seq_ev_is_variable(&event)) {
|
if (snd_seq_ev_is_variable(ev)) {
|
||||||
int extlen = event.data.ext.len & ~SNDRV_SEQ_EXT_MASK;
|
int extlen = ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
|
||||||
if ((size_t)(extlen + len) > count) {
|
if ((size_t)(extlen + len) > count) {
|
||||||
/* back out, will get an error this time or next */
|
/* back out, will get an error this time or next */
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* set user space pointer */
|
/* set user space pointer */
|
||||||
event.data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR;
|
ev->data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR;
|
||||||
event.data.ext.ptr = (char __force *)buf
|
ev->data.ext.ptr = (char __force *)buf + len;
|
||||||
+ sizeof(struct snd_seq_event);
|
|
||||||
len += extlen; /* increment data length */
|
len += extlen; /* increment data length */
|
||||||
} else {
|
} else {
|
||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
if (client->convert32 && snd_seq_ev_is_varusr(&event)) {
|
if (client->convert32 && snd_seq_ev_is_varusr(ev))
|
||||||
void *ptr = (void __force *)compat_ptr(event.data.raw32.d[1]);
|
ev->data.ext.ptr =
|
||||||
event.data.ext.ptr = ptr;
|
(void __force *)compat_ptr(ev->data.raw32.d[1]);
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ok, enqueue it */
|
/* ok, enqueue it */
|
||||||
err = snd_seq_client_enqueue_event(client, &event, file,
|
err = snd_seq_client_enqueue_event(client, ev, file,
|
||||||
!(file->f_flags & O_NONBLOCK),
|
!(file->f_flags & O_NONBLOCK),
|
||||||
0, 0, &client->ioctl_mutex);
|
0, 0, &client->ioctl_mutex);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
@ -1146,6 +1188,7 @@ static void get_client_info(struct snd_seq_client *cptr,
|
|||||||
else
|
else
|
||||||
info->card = -1;
|
info->card = -1;
|
||||||
|
|
||||||
|
info->midi_version = cptr->midi_version;
|
||||||
memset(info->reserved, 0, sizeof(info->reserved));
|
memset(info->reserved, 0, sizeof(info->reserved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1180,12 +1223,19 @@ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client,
|
|||||||
if (client->type != client_info->type)
|
if (client->type != client_info->type)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* check validity of midi_version field */
|
||||||
|
if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3) &&
|
||||||
|
client_info->midi_version > SNDRV_SEQ_CLIENT_UMP_MIDI_2_0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
/* fill the info fields */
|
/* fill the info fields */
|
||||||
if (client_info->name[0])
|
if (client_info->name[0])
|
||||||
strscpy(client->name, client_info->name, sizeof(client->name));
|
strscpy(client->name, client_info->name, sizeof(client->name));
|
||||||
|
|
||||||
client->filter = client_info->filter;
|
client->filter = client_info->filter;
|
||||||
client->event_lost = client_info->event_lost;
|
client->event_lost = client_info->event_lost;
|
||||||
|
if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3))
|
||||||
|
client->midi_version = client_info->midi_version;
|
||||||
memcpy(client->event_filter, client_info->event_filter, 32);
|
memcpy(client->event_filter, client_info->event_filter, 32);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -2181,10 +2231,12 @@ int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev,
|
|||||||
if (snd_BUG_ON(!ev))
|
if (snd_BUG_ON(!ev))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (ev->type == SNDRV_SEQ_EVENT_NONE)
|
if (!snd_seq_ev_is_ump(ev)) {
|
||||||
return 0; /* ignore this */
|
if (ev->type == SNDRV_SEQ_EVENT_NONE)
|
||||||
if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
|
return 0; /* ignore this */
|
||||||
return -EINVAL; /* quoted events can't be enqueued */
|
if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
|
||||||
|
return -EINVAL; /* quoted events can't be enqueued */
|
||||||
|
}
|
||||||
|
|
||||||
/* fill in client number */
|
/* fill in client number */
|
||||||
ev->source.client = client;
|
ev->source.client = client;
|
||||||
@ -2376,6 +2428,19 @@ static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer,
|
|||||||
mutex_unlock(&client->ports_mutex);
|
mutex_unlock(&client->ports_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *midi_version_string(unsigned int version)
|
||||||
|
{
|
||||||
|
switch (version) {
|
||||||
|
case SNDRV_SEQ_CLIENT_LEGACY_MIDI:
|
||||||
|
return "Legacy";
|
||||||
|
case SNDRV_SEQ_CLIENT_UMP_MIDI_1_0:
|
||||||
|
return "UMP MIDI1";
|
||||||
|
case SNDRV_SEQ_CLIENT_UMP_MIDI_2_0:
|
||||||
|
return "UMP MIDI2";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* exported to seq_info.c */
|
/* exported to seq_info.c */
|
||||||
void snd_seq_info_clients_read(struct snd_info_entry *entry,
|
void snd_seq_info_clients_read(struct snd_info_entry *entry,
|
||||||
@ -2400,9 +2465,10 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
snd_iprintf(buffer, "Client %3d : \"%s\" [%s]\n",
|
snd_iprintf(buffer, "Client %3d : \"%s\" [%s %s]\n",
|
||||||
c, client->name,
|
c, client->name,
|
||||||
client->type == USER_CLIENT ? "User" : "Kernel");
|
client->type == USER_CLIENT ? "User" : "Kernel",
|
||||||
|
midi_version_string(client->midi_version));
|
||||||
snd_seq_info_dump_ports(buffer, client);
|
snd_seq_info_dump_ports(buffer, client);
|
||||||
if (snd_seq_write_pool_allocated(client)) {
|
if (snd_seq_write_pool_allocated(client)) {
|
||||||
snd_iprintf(buffer, " Output pool :\n");
|
snd_iprintf(buffer, " Output pool :\n");
|
||||||
|
@ -35,6 +35,7 @@ struct snd_seq_client {
|
|||||||
snd_seq_client_type_t type;
|
snd_seq_client_type_t type;
|
||||||
unsigned int accept_input: 1,
|
unsigned int accept_input: 1,
|
||||||
accept_output: 1;
|
accept_output: 1;
|
||||||
|
unsigned int midi_version;
|
||||||
unsigned int user_pversion;
|
unsigned int user_pversion;
|
||||||
char name[64]; /* client name */
|
char name[64]; /* client name */
|
||||||
int number; /* client number */
|
int number; /* client number */
|
||||||
|
@ -340,6 +340,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
|
|||||||
int ncells, err;
|
int ncells, err;
|
||||||
unsigned int extlen;
|
unsigned int extlen;
|
||||||
struct snd_seq_event_cell *cell;
|
struct snd_seq_event_cell *cell;
|
||||||
|
int size;
|
||||||
|
|
||||||
*cellp = NULL;
|
*cellp = NULL;
|
||||||
|
|
||||||
@ -357,7 +358,12 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
|
|||||||
return err;
|
return err;
|
||||||
|
|
||||||
/* copy the event */
|
/* copy the event */
|
||||||
cell->event = *event;
|
size = snd_seq_event_packet_size(event);
|
||||||
|
memcpy(&cell->ump, event, size);
|
||||||
|
#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
|
||||||
|
if (size < sizeof(cell->event))
|
||||||
|
cell->ump.raw.extra = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* decompose */
|
/* decompose */
|
||||||
if (snd_seq_ev_is_variable(event)) {
|
if (snd_seq_ev_is_variable(event)) {
|
||||||
@ -375,7 +381,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
|
|||||||
tail = NULL;
|
tail = NULL;
|
||||||
|
|
||||||
while (ncells-- > 0) {
|
while (ncells-- > 0) {
|
||||||
int size = sizeof(struct snd_seq_event);
|
size = sizeof(struct snd_seq_event);
|
||||||
if (len < size)
|
if (len < size)
|
||||||
size = len;
|
size = len;
|
||||||
err = snd_seq_cell_alloc(pool, &tmp, nonblock, file,
|
err = snd_seq_cell_alloc(pool, &tmp, nonblock, file,
|
||||||
|
@ -11,9 +11,26 @@
|
|||||||
|
|
||||||
struct snd_info_buffer;
|
struct snd_info_buffer;
|
||||||
|
|
||||||
|
/* aliasing for legacy and UMP event packet handling */
|
||||||
|
union __snd_seq_event {
|
||||||
|
struct snd_seq_event legacy;
|
||||||
|
#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
|
||||||
|
struct snd_seq_ump_event ump;
|
||||||
|
#endif
|
||||||
|
struct {
|
||||||
|
struct snd_seq_event event;
|
||||||
|
#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
|
||||||
|
u32 extra;
|
||||||
|
#endif
|
||||||
|
} __packed raw;
|
||||||
|
};
|
||||||
|
|
||||||
/* container for sequencer event (internal use) */
|
/* container for sequencer event (internal use) */
|
||||||
struct snd_seq_event_cell {
|
struct snd_seq_event_cell {
|
||||||
struct snd_seq_event event;
|
union {
|
||||||
|
struct snd_seq_event event;
|
||||||
|
union __snd_seq_event ump;
|
||||||
|
};
|
||||||
struct snd_seq_pool *pool; /* used pool */
|
struct snd_seq_pool *pool; /* used pool */
|
||||||
struct snd_seq_event_cell *next; /* next cell */
|
struct snd_seq_event_cell *next; /* next cell */
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user