diff options
author | Takashi Iwai <tiwai@suse.de> | 2023-06-13 07:36:39 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2023-06-13 07:37:59 +0200 |
commit | 508b662b6928808d1af3887f4adc20fb0cd315df (patch) | |
tree | da96313fc7566406ca8ecb10d4509abc255e8f07 /sound/core/seq/seq_ump_client.c | |
parent | 9b4469410cf9a0fcbccc92c480fd42f7c815a745 (diff) | |
parent | febdfa0e9c8a5d3a3d895245d1c294c26787daef (diff) | |
download | linux-508b662b6928808d1af3887f4adc20fb0cd315df.tar.gz linux-508b662b6928808d1af3887f4adc20fb0cd315df.tar.bz2 linux-508b662b6928808d1af3887f4adc20fb0cd315df.zip |
Merge branch 'topic/midi20' into for-next
As the updated MIDI 2.0 spec has been published freshly, this is a
catch up to add the support for new specs, especially UMP v1.1
features, on Linux kernel.
The new UMP v1.1 introduced the concept of Function Blocks (FB), which
is a kind of superset of USB MIDI 2.0 Group Terminal Blocks (GTB).
The patch set adds the support for FB as the primary information
source while keeping the parse of GTB as fallback. Also UMP v1.1
supports the groupless messages, the protocol switch, static FBs, and
other new fundamental features, and those are supported as well.
Link: https://www.midi.org/midi-articles/details-about-midi-2-0-midi-ci-profiles-and-property-exchange
Link: https://lore.kernel.org/r/20230612081054.17200-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/core/seq/seq_ump_client.c')
-rw-r--r-- | sound/core/seq/seq_ump_client.c | 79 |
1 files changed, 78 insertions, 1 deletions
diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index e24833804094..fe21c801af74 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -13,6 +13,7 @@ #include <sound/seq_kernel.h> #include <sound/seq_device.h> #include "seq_clientmgr.h" +#include "seq_system.h" struct seq_ump_client; struct seq_ump_group; @@ -48,6 +49,7 @@ struct seq_ump_client { struct seq_ump_input_buffer input; /* input parser context */ struct seq_ump_group groups[SNDRV_UMP_MAX_GROUPS]; /* table of groups */ void *ump_info[SNDRV_UMP_MAX_BLOCKS + 1]; /* shadow of seq client ump_info */ + struct work_struct group_notify_work; /* FB change notification */ }; /* number of 32bit words for each UMP message type */ @@ -73,7 +75,10 @@ static void seq_ump_input_receive(struct snd_ump_endpoint *ump, if (!client->opened[STR_IN]) return; - ev.source.port = ump_group_to_seq_port(ump_message_group(*val)); + if (ump_is_groupless_msg(ump_message_type(*val))) + ev.source.port = 0; /* UMP EP port */ + else + ev.source.port = ump_group_to_seq_port(ump_message_group(*val)); ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; ev.flags = SNDRV_SEQ_EVENT_UMP; memcpy(ev.ump, val, words << 2); @@ -241,6 +246,42 @@ static int seq_ump_group_init(struct seq_ump_client *client, int group_index) return err; } +/* update the sequencer ports; called from notify_fb_change callback */ +static void update_port_infos(struct seq_ump_client *client) +{ + struct snd_seq_port_info *old, *new; + int i, err; + + old = kzalloc(sizeof(*old), GFP_KERNEL); + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!old || !new) + goto error; + + for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) { + old->addr.client = client->seq_client; + old->addr.port = i; + err = snd_seq_kernel_client_ctl(client->seq_client, + SNDRV_SEQ_IOCTL_GET_PORT_INFO, + old); + if (err < 0) + goto error; + fill_port_info(new, client, &client->groups[i]); + if (old->capability == new->capability && + !strcmp(old->name, new->name)) + continue; + err = snd_seq_kernel_client_ctl(client->seq_client, + SNDRV_SEQ_IOCTL_SET_PORT_INFO, + new); + if (err < 0) + goto error; + /* notify to system port */ + snd_seq_system_client_ev_port_change(client->seq_client, i); + } + error: + kfree(new); + kfree(old); +} + /* update dir_bits and active flag for all groups in the client */ static void update_group_attrs(struct seq_ump_client *client) { @@ -350,6 +391,8 @@ static int create_ump_endpoint_port(struct seq_ump_client *client) /* release the client resources */ static void seq_ump_client_free(struct seq_ump_client *client) { + cancel_work_sync(&client->group_notify_work); + if (client->seq_client >= 0) snd_seq_delete_kernel_client(client->seq_client); @@ -374,8 +417,41 @@ static void setup_client_midi_version(struct seq_ump_client *client) snd_seq_kernel_client_put(cptr); } +/* UMP group change notification */ +static void handle_group_notify(struct work_struct *work) +{ + struct seq_ump_client *client = + container_of(work, struct seq_ump_client, group_notify_work); + + update_group_attrs(client); + update_port_infos(client); +} + +/* UMP FB change notification */ +static int seq_ump_notify_fb_change(struct snd_ump_endpoint *ump, + struct snd_ump_block *fb) +{ + struct seq_ump_client *client = ump->seq_client; + + if (!client) + return -ENODEV; + schedule_work(&client->group_notify_work); + return 0; +} + +/* UMP protocol change notification; just update the midi_version field */ +static int seq_ump_switch_protocol(struct snd_ump_endpoint *ump) +{ + if (!ump->seq_client) + return -ENODEV; + setup_client_midi_version(ump->seq_client); + return 0; +} + static const struct snd_seq_ump_ops seq_ump_ops = { .input_receive = seq_ump_input_receive, + .notify_fb_change = seq_ump_notify_fb_change, + .switch_protocol = seq_ump_switch_protocol, }; /* create a sequencer client and ports for the given UMP endpoint */ @@ -393,6 +469,7 @@ static int snd_seq_ump_probe(struct device *_dev) if (!client) return -ENOMEM; + INIT_WORK(&client->group_notify_work, handle_group_notify); client->ump = ump; client->seq_client = |