181fd444aSTakashi Iwai // SPDX-License-Identifier: GPL-2.0-or-later 281fd444aSTakashi Iwai /* ALSA sequencer binding for UMP device */ 381fd444aSTakashi Iwai 481fd444aSTakashi Iwai #include <linux/init.h> 581fd444aSTakashi Iwai #include <linux/slab.h> 681fd444aSTakashi Iwai #include <linux/errno.h> 781fd444aSTakashi Iwai #include <linux/mutex.h> 881fd444aSTakashi Iwai #include <linux/string.h> 981fd444aSTakashi Iwai #include <linux/module.h> 1081fd444aSTakashi Iwai #include <asm/byteorder.h> 1181fd444aSTakashi Iwai #include <sound/core.h> 1281fd444aSTakashi Iwai #include <sound/ump.h> 1381fd444aSTakashi Iwai #include <sound/seq_kernel.h> 1481fd444aSTakashi Iwai #include <sound/seq_device.h> 1581fd444aSTakashi Iwai #include "seq_clientmgr.h" 16174a6dfbSTakashi Iwai #include "seq_system.h" 1781fd444aSTakashi Iwai 1881fd444aSTakashi Iwai struct seq_ump_client; 1981fd444aSTakashi Iwai struct seq_ump_group; 2081fd444aSTakashi Iwai 2181fd444aSTakashi Iwai enum { 2281fd444aSTakashi Iwai STR_IN = SNDRV_RAWMIDI_STREAM_INPUT, 2381fd444aSTakashi Iwai STR_OUT = SNDRV_RAWMIDI_STREAM_OUTPUT 2481fd444aSTakashi Iwai }; 2581fd444aSTakashi Iwai 2681fd444aSTakashi Iwai /* object per UMP group; corresponding to a sequencer port */ 2781fd444aSTakashi Iwai struct seq_ump_group { 2881fd444aSTakashi Iwai int group; /* group index (0-based) */ 2981fd444aSTakashi Iwai unsigned int dir_bits; /* directions */ 3081fd444aSTakashi Iwai bool active; /* activeness */ 31*3bfd7c0bSTakashi Iwai bool valid; /* valid group (referred by blocks) */ 3281fd444aSTakashi Iwai char name[64]; /* seq port name */ 3381fd444aSTakashi Iwai }; 3481fd444aSTakashi Iwai 3581fd444aSTakashi Iwai /* context for UMP input parsing, per EP */ 3681fd444aSTakashi Iwai struct seq_ump_input_buffer { 3781fd444aSTakashi Iwai unsigned char len; /* total length in words */ 3881fd444aSTakashi Iwai unsigned char pending; /* pending words */ 3981fd444aSTakashi Iwai unsigned char type; /* parsed UMP packet type */ 4081fd444aSTakashi Iwai unsigned char group; /* parsed UMP packet group */ 4181fd444aSTakashi Iwai u32 buf[4]; /* incoming UMP packet */ 4281fd444aSTakashi Iwai }; 4381fd444aSTakashi Iwai 4481fd444aSTakashi Iwai /* sequencer client, per UMP EP (rawmidi) */ 4581fd444aSTakashi Iwai struct seq_ump_client { 4681fd444aSTakashi Iwai struct snd_ump_endpoint *ump; /* assigned endpoint */ 4781fd444aSTakashi Iwai int seq_client; /* sequencer client id */ 4881fd444aSTakashi Iwai int opened[2]; /* current opens for each direction */ 4981fd444aSTakashi Iwai struct snd_rawmidi_file out_rfile; /* rawmidi for output */ 5081fd444aSTakashi Iwai struct seq_ump_input_buffer input; /* input parser context */ 5181fd444aSTakashi Iwai struct seq_ump_group groups[SNDRV_UMP_MAX_GROUPS]; /* table of groups */ 52d2d247e3STakashi Iwai void *ump_info[SNDRV_UMP_MAX_BLOCKS + 1]; /* shadow of seq client ump_info */ 534a16a3afSTakashi Iwai struct work_struct group_notify_work; /* FB change notification */ 5481fd444aSTakashi Iwai }; 5581fd444aSTakashi Iwai 5681fd444aSTakashi Iwai /* number of 32bit words for each UMP message type */ 5781fd444aSTakashi Iwai static unsigned char ump_packet_words[0x10] = { 5881fd444aSTakashi Iwai 1, 1, 1, 2, 2, 4, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4 5981fd444aSTakashi Iwai }; 6081fd444aSTakashi Iwai 6181fd444aSTakashi Iwai /* conversion between UMP group and seq port; 6281fd444aSTakashi Iwai * assume the port number is equal with UMP group number (1-based) 6381fd444aSTakashi Iwai */ 6481fd444aSTakashi Iwai static unsigned char ump_group_to_seq_port(unsigned char group) 6581fd444aSTakashi Iwai { 6681fd444aSTakashi Iwai return group + 1; 6781fd444aSTakashi Iwai } 6881fd444aSTakashi Iwai 6981fd444aSTakashi Iwai /* process the incoming rawmidi stream */ 7081fd444aSTakashi Iwai static void seq_ump_input_receive(struct snd_ump_endpoint *ump, 7181fd444aSTakashi Iwai const u32 *val, int words) 7281fd444aSTakashi Iwai { 7381fd444aSTakashi Iwai struct seq_ump_client *client = ump->seq_client; 7481fd444aSTakashi Iwai struct snd_seq_ump_event ev = {}; 7581fd444aSTakashi Iwai 7681fd444aSTakashi Iwai if (!client->opened[STR_IN]) 7781fd444aSTakashi Iwai return; 7881fd444aSTakashi Iwai 795437ac9bSTakashi Iwai if (ump_is_groupless_msg(ump_message_type(*val))) 805437ac9bSTakashi Iwai ev.source.port = 0; /* UMP EP port */ 815437ac9bSTakashi Iwai else 8281fd444aSTakashi Iwai ev.source.port = ump_group_to_seq_port(ump_message_group(*val)); 8381fd444aSTakashi Iwai ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; 8481fd444aSTakashi Iwai ev.flags = SNDRV_SEQ_EVENT_UMP; 8581fd444aSTakashi Iwai memcpy(ev.ump, val, words << 2); 8681fd444aSTakashi Iwai snd_seq_kernel_client_dispatch(client->seq_client, 8781fd444aSTakashi Iwai (struct snd_seq_event *)&ev, 8881fd444aSTakashi Iwai true, 0); 8981fd444aSTakashi Iwai } 9081fd444aSTakashi Iwai 9181fd444aSTakashi Iwai /* process an input sequencer event; only deal with UMP types */ 9281fd444aSTakashi Iwai static int seq_ump_process_event(struct snd_seq_event *ev, int direct, 9381fd444aSTakashi Iwai void *private_data, int atomic, int hop) 9481fd444aSTakashi Iwai { 9581fd444aSTakashi Iwai struct seq_ump_client *client = private_data; 9681fd444aSTakashi Iwai struct snd_rawmidi_substream *substream; 9781fd444aSTakashi Iwai struct snd_seq_ump_event *ump_ev; 9881fd444aSTakashi Iwai unsigned char type; 9981fd444aSTakashi Iwai int len; 10081fd444aSTakashi Iwai 10181fd444aSTakashi Iwai substream = client->out_rfile.output; 10281fd444aSTakashi Iwai if (!substream) 10381fd444aSTakashi Iwai return -ENODEV; 10481fd444aSTakashi Iwai if (!snd_seq_ev_is_ump(ev)) 10581fd444aSTakashi Iwai return 0; /* invalid event, skip */ 10681fd444aSTakashi Iwai ump_ev = (struct snd_seq_ump_event *)ev; 10781fd444aSTakashi Iwai type = ump_message_type(ump_ev->ump[0]); 10881fd444aSTakashi Iwai len = ump_packet_words[type]; 10981fd444aSTakashi Iwai if (len > 4) 11081fd444aSTakashi Iwai return 0; // invalid - skip 11181fd444aSTakashi Iwai snd_rawmidi_kernel_write(substream, ev->data.raw8.d, len << 2); 11281fd444aSTakashi Iwai return 0; 11381fd444aSTakashi Iwai } 11481fd444aSTakashi Iwai 11581fd444aSTakashi Iwai /* open the rawmidi */ 11681fd444aSTakashi Iwai static int seq_ump_client_open(struct seq_ump_client *client, int dir) 11781fd444aSTakashi Iwai { 11881fd444aSTakashi Iwai struct snd_ump_endpoint *ump = client->ump; 1196487e363STakashi Iwai int err; 12081fd444aSTakashi Iwai 1216487e363STakashi Iwai guard(mutex)(&ump->open_mutex); 12281fd444aSTakashi Iwai if (dir == STR_OUT && !client->opened[dir]) { 12381fd444aSTakashi Iwai err = snd_rawmidi_kernel_open(&ump->core, 0, 12481fd444aSTakashi Iwai SNDRV_RAWMIDI_LFLG_OUTPUT | 12581fd444aSTakashi Iwai SNDRV_RAWMIDI_LFLG_APPEND, 12681fd444aSTakashi Iwai &client->out_rfile); 12781fd444aSTakashi Iwai if (err < 0) 1286487e363STakashi Iwai return err; 12981fd444aSTakashi Iwai } 13081fd444aSTakashi Iwai client->opened[dir]++; 1316487e363STakashi Iwai return 0; 13281fd444aSTakashi Iwai } 13381fd444aSTakashi Iwai 13481fd444aSTakashi Iwai /* close the rawmidi */ 13581fd444aSTakashi Iwai static int seq_ump_client_close(struct seq_ump_client *client, int dir) 13681fd444aSTakashi Iwai { 13781fd444aSTakashi Iwai struct snd_ump_endpoint *ump = client->ump; 13881fd444aSTakashi Iwai 1396487e363STakashi Iwai guard(mutex)(&ump->open_mutex); 14081fd444aSTakashi Iwai if (!--client->opened[dir]) 14181fd444aSTakashi Iwai if (dir == STR_OUT) 14281fd444aSTakashi Iwai snd_rawmidi_kernel_release(&client->out_rfile); 14381fd444aSTakashi Iwai return 0; 14481fd444aSTakashi Iwai } 14581fd444aSTakashi Iwai 14681fd444aSTakashi Iwai /* sequencer subscription ops for each client */ 14781fd444aSTakashi Iwai static int seq_ump_subscribe(void *pdata, struct snd_seq_port_subscribe *info) 14881fd444aSTakashi Iwai { 14981fd444aSTakashi Iwai struct seq_ump_client *client = pdata; 15081fd444aSTakashi Iwai 15181fd444aSTakashi Iwai return seq_ump_client_open(client, STR_IN); 15281fd444aSTakashi Iwai } 15381fd444aSTakashi Iwai 15481fd444aSTakashi Iwai static int seq_ump_unsubscribe(void *pdata, struct snd_seq_port_subscribe *info) 15581fd444aSTakashi Iwai { 15681fd444aSTakashi Iwai struct seq_ump_client *client = pdata; 15781fd444aSTakashi Iwai 15881fd444aSTakashi Iwai return seq_ump_client_close(client, STR_IN); 15981fd444aSTakashi Iwai } 16081fd444aSTakashi Iwai 16181fd444aSTakashi Iwai static int seq_ump_use(void *pdata, struct snd_seq_port_subscribe *info) 16281fd444aSTakashi Iwai { 16381fd444aSTakashi Iwai struct seq_ump_client *client = pdata; 16481fd444aSTakashi Iwai 16581fd444aSTakashi Iwai return seq_ump_client_open(client, STR_OUT); 16681fd444aSTakashi Iwai } 16781fd444aSTakashi Iwai 16881fd444aSTakashi Iwai static int seq_ump_unuse(void *pdata, struct snd_seq_port_subscribe *info) 16981fd444aSTakashi Iwai { 17081fd444aSTakashi Iwai struct seq_ump_client *client = pdata; 17181fd444aSTakashi Iwai 17281fd444aSTakashi Iwai return seq_ump_client_close(client, STR_OUT); 17381fd444aSTakashi Iwai } 17481fd444aSTakashi Iwai 17581fd444aSTakashi Iwai /* fill port_info from the given UMP EP and group info */ 17681fd444aSTakashi Iwai static void fill_port_info(struct snd_seq_port_info *port, 17781fd444aSTakashi Iwai struct seq_ump_client *client, 17881fd444aSTakashi Iwai struct seq_ump_group *group) 17981fd444aSTakashi Iwai { 18081fd444aSTakashi Iwai unsigned int rawmidi_info = client->ump->core.info_flags; 18181fd444aSTakashi Iwai 18281fd444aSTakashi Iwai port->addr.client = client->seq_client; 18381fd444aSTakashi Iwai port->addr.port = ump_group_to_seq_port(group->group); 18481fd444aSTakashi Iwai port->capability = 0; 18581fd444aSTakashi Iwai if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) 18681fd444aSTakashi Iwai port->capability |= SNDRV_SEQ_PORT_CAP_WRITE | 18781fd444aSTakashi Iwai SNDRV_SEQ_PORT_CAP_SYNC_WRITE | 18881fd444aSTakashi Iwai SNDRV_SEQ_PORT_CAP_SUBS_WRITE; 18981fd444aSTakashi Iwai if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) 19081fd444aSTakashi Iwai port->capability |= SNDRV_SEQ_PORT_CAP_READ | 19181fd444aSTakashi Iwai SNDRV_SEQ_PORT_CAP_SYNC_READ | 19281fd444aSTakashi Iwai SNDRV_SEQ_PORT_CAP_SUBS_READ; 19381fd444aSTakashi Iwai if (rawmidi_info & SNDRV_RAWMIDI_INFO_DUPLEX) 19481fd444aSTakashi Iwai port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; 19581fd444aSTakashi Iwai if (group->dir_bits & (1 << STR_IN)) 19681fd444aSTakashi Iwai port->direction |= SNDRV_SEQ_PORT_DIR_INPUT; 19781fd444aSTakashi Iwai if (group->dir_bits & (1 << STR_OUT)) 19881fd444aSTakashi Iwai port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT; 19981fd444aSTakashi Iwai port->ump_group = group->group + 1; 20081fd444aSTakashi Iwai if (!group->active) 20181fd444aSTakashi Iwai port->capability |= SNDRV_SEQ_PORT_CAP_INACTIVE; 20281fd444aSTakashi Iwai port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | 20381fd444aSTakashi Iwai SNDRV_SEQ_PORT_TYPE_MIDI_UMP | 20481fd444aSTakashi Iwai SNDRV_SEQ_PORT_TYPE_HARDWARE | 20581fd444aSTakashi Iwai SNDRV_SEQ_PORT_TYPE_PORT; 20681fd444aSTakashi Iwai port->midi_channels = 16; 20781fd444aSTakashi Iwai if (*group->name) 2080d422608STakashi Iwai snprintf(port->name, sizeof(port->name), "Group %d (%.53s)", 20981fd444aSTakashi Iwai group->group + 1, group->name); 21081fd444aSTakashi Iwai else 21181fd444aSTakashi Iwai sprintf(port->name, "Group %d", group->group + 1); 21281fd444aSTakashi Iwai } 21381fd444aSTakashi Iwai 214*3bfd7c0bSTakashi Iwai /* skip non-existing group for static blocks */ 215*3bfd7c0bSTakashi Iwai static bool skip_group(struct seq_ump_client *client, struct seq_ump_group *group) 216*3bfd7c0bSTakashi Iwai { 217*3bfd7c0bSTakashi Iwai return !group->valid && 218*3bfd7c0bSTakashi Iwai (client->ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS); 219*3bfd7c0bSTakashi Iwai } 220*3bfd7c0bSTakashi Iwai 22181fd444aSTakashi Iwai /* create a new sequencer port per UMP group */ 22281fd444aSTakashi Iwai static int seq_ump_group_init(struct seq_ump_client *client, int group_index) 22381fd444aSTakashi Iwai { 22481fd444aSTakashi Iwai struct seq_ump_group *group = &client->groups[group_index]; 225316e38efSTakashi Iwai struct snd_seq_port_info *port __free(kfree) = NULL; 22681fd444aSTakashi Iwai struct snd_seq_port_callback pcallbacks; 22781fd444aSTakashi Iwai 228*3bfd7c0bSTakashi Iwai if (skip_group(client, group)) 229*3bfd7c0bSTakashi Iwai return 0; 230*3bfd7c0bSTakashi Iwai 23181fd444aSTakashi Iwai port = kzalloc(sizeof(*port), GFP_KERNEL); 232316e38efSTakashi Iwai if (!port) 233316e38efSTakashi Iwai return -ENOMEM; 23481fd444aSTakashi Iwai 23581fd444aSTakashi Iwai fill_port_info(port, client, group); 23681fd444aSTakashi Iwai port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; 23781fd444aSTakashi Iwai memset(&pcallbacks, 0, sizeof(pcallbacks)); 23881fd444aSTakashi Iwai pcallbacks.owner = THIS_MODULE; 23981fd444aSTakashi Iwai pcallbacks.private_data = client; 24081fd444aSTakashi Iwai pcallbacks.subscribe = seq_ump_subscribe; 24181fd444aSTakashi Iwai pcallbacks.unsubscribe = seq_ump_unsubscribe; 24281fd444aSTakashi Iwai pcallbacks.use = seq_ump_use; 24381fd444aSTakashi Iwai pcallbacks.unuse = seq_ump_unuse; 24481fd444aSTakashi Iwai pcallbacks.event_input = seq_ump_process_event; 24581fd444aSTakashi Iwai port->kernel = &pcallbacks; 246316e38efSTakashi Iwai return snd_seq_kernel_client_ctl(client->seq_client, 24781fd444aSTakashi Iwai SNDRV_SEQ_IOCTL_CREATE_PORT, 24881fd444aSTakashi Iwai port); 24981fd444aSTakashi Iwai } 25081fd444aSTakashi Iwai 2514a16a3afSTakashi Iwai /* update the sequencer ports; called from notify_fb_change callback */ 2524a16a3afSTakashi Iwai static void update_port_infos(struct seq_ump_client *client) 2534a16a3afSTakashi Iwai { 254316e38efSTakashi Iwai struct snd_seq_port_info *old __free(kfree) = NULL; 255316e38efSTakashi Iwai struct snd_seq_port_info *new __free(kfree) = NULL; 2564a16a3afSTakashi Iwai int i, err; 2574a16a3afSTakashi Iwai 2584a16a3afSTakashi Iwai old = kzalloc(sizeof(*old), GFP_KERNEL); 2594a16a3afSTakashi Iwai new = kzalloc(sizeof(*new), GFP_KERNEL); 2604a16a3afSTakashi Iwai if (!old || !new) 261316e38efSTakashi Iwai return; 2624a16a3afSTakashi Iwai 2634a16a3afSTakashi Iwai for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) { 264*3bfd7c0bSTakashi Iwai if (skip_group(client, &client->groups[i])) 265*3bfd7c0bSTakashi Iwai continue; 266*3bfd7c0bSTakashi Iwai 2674a16a3afSTakashi Iwai old->addr.client = client->seq_client; 2684a16a3afSTakashi Iwai old->addr.port = i; 2694a16a3afSTakashi Iwai err = snd_seq_kernel_client_ctl(client->seq_client, 2704a16a3afSTakashi Iwai SNDRV_SEQ_IOCTL_GET_PORT_INFO, 2714a16a3afSTakashi Iwai old); 2724a16a3afSTakashi Iwai if (err < 0) 273316e38efSTakashi Iwai return; 2744a16a3afSTakashi Iwai fill_port_info(new, client, &client->groups[i]); 2754a16a3afSTakashi Iwai if (old->capability == new->capability && 2764a16a3afSTakashi Iwai !strcmp(old->name, new->name)) 2774a16a3afSTakashi Iwai continue; 2784a16a3afSTakashi Iwai err = snd_seq_kernel_client_ctl(client->seq_client, 2794a16a3afSTakashi Iwai SNDRV_SEQ_IOCTL_SET_PORT_INFO, 2804a16a3afSTakashi Iwai new); 2814a16a3afSTakashi Iwai if (err < 0) 282316e38efSTakashi Iwai return; 283174a6dfbSTakashi Iwai /* notify to system port */ 284174a6dfbSTakashi Iwai snd_seq_system_client_ev_port_change(client->seq_client, i); 2854a16a3afSTakashi Iwai } 2864a16a3afSTakashi Iwai } 2874a16a3afSTakashi Iwai 28881fd444aSTakashi Iwai /* update dir_bits and active flag for all groups in the client */ 28981fd444aSTakashi Iwai static void update_group_attrs(struct seq_ump_client *client) 29081fd444aSTakashi Iwai { 29181fd444aSTakashi Iwai struct snd_ump_block *fb; 29281fd444aSTakashi Iwai struct seq_ump_group *group; 29381fd444aSTakashi Iwai int i; 29481fd444aSTakashi Iwai 29581fd444aSTakashi Iwai for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) { 29681fd444aSTakashi Iwai group = &client->groups[i]; 29781fd444aSTakashi Iwai *group->name = 0; 29881fd444aSTakashi Iwai group->dir_bits = 0; 29981fd444aSTakashi Iwai group->active = 0; 30081fd444aSTakashi Iwai group->group = i; 301*3bfd7c0bSTakashi Iwai group->valid = false; 30281fd444aSTakashi Iwai } 30381fd444aSTakashi Iwai 30481fd444aSTakashi Iwai list_for_each_entry(fb, &client->ump->block_list, list) { 30559ea9138SWang Weiyang if (fb->info.first_group + fb->info.num_groups > SNDRV_UMP_MAX_GROUPS) 30681fd444aSTakashi Iwai break; 30781fd444aSTakashi Iwai group = &client->groups[fb->info.first_group]; 30881fd444aSTakashi Iwai for (i = 0; i < fb->info.num_groups; i++, group++) { 309*3bfd7c0bSTakashi Iwai group->valid = true; 31081fd444aSTakashi Iwai if (fb->info.active) 31181fd444aSTakashi Iwai group->active = 1; 31281fd444aSTakashi Iwai switch (fb->info.direction) { 31381fd444aSTakashi Iwai case SNDRV_UMP_DIR_INPUT: 31481fd444aSTakashi Iwai group->dir_bits |= (1 << STR_IN); 31581fd444aSTakashi Iwai break; 31681fd444aSTakashi Iwai case SNDRV_UMP_DIR_OUTPUT: 31781fd444aSTakashi Iwai group->dir_bits |= (1 << STR_OUT); 31881fd444aSTakashi Iwai break; 31981fd444aSTakashi Iwai case SNDRV_UMP_DIR_BIDIRECTION: 32081fd444aSTakashi Iwai group->dir_bits |= (1 << STR_OUT) | (1 << STR_IN); 32181fd444aSTakashi Iwai break; 32281fd444aSTakashi Iwai } 32381fd444aSTakashi Iwai if (!*fb->info.name) 32481fd444aSTakashi Iwai continue; 32581fd444aSTakashi Iwai if (!*group->name) { 32681fd444aSTakashi Iwai /* store the first matching name */ 32781fd444aSTakashi Iwai strscpy(group->name, fb->info.name, 32881fd444aSTakashi Iwai sizeof(group->name)); 32981fd444aSTakashi Iwai } else { 33081fd444aSTakashi Iwai /* when overlapping, concat names */ 33181fd444aSTakashi Iwai strlcat(group->name, ", ", sizeof(group->name)); 33281fd444aSTakashi Iwai strlcat(group->name, fb->info.name, 33381fd444aSTakashi Iwai sizeof(group->name)); 33481fd444aSTakashi Iwai } 33581fd444aSTakashi Iwai } 33681fd444aSTakashi Iwai } 33781fd444aSTakashi Iwai } 33881fd444aSTakashi Iwai 3394025f0e6STakashi Iwai /* create a UMP Endpoint port */ 3404025f0e6STakashi Iwai static int create_ump_endpoint_port(struct seq_ump_client *client) 3414025f0e6STakashi Iwai { 342316e38efSTakashi Iwai struct snd_seq_port_info *port __free(kfree) = NULL; 3434025f0e6STakashi Iwai struct snd_seq_port_callback pcallbacks; 3444025f0e6STakashi Iwai unsigned int rawmidi_info = client->ump->core.info_flags; 3454025f0e6STakashi Iwai int err; 3464025f0e6STakashi Iwai 3474025f0e6STakashi Iwai port = kzalloc(sizeof(*port), GFP_KERNEL); 3484025f0e6STakashi Iwai if (!port) 3494025f0e6STakashi Iwai return -ENOMEM; 3504025f0e6STakashi Iwai 3514025f0e6STakashi Iwai port->addr.client = client->seq_client; 3524025f0e6STakashi Iwai port->addr.port = 0; /* fixed */ 3534025f0e6STakashi Iwai port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; 3544025f0e6STakashi Iwai port->capability = SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT; 3554025f0e6STakashi Iwai if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) { 3564025f0e6STakashi Iwai port->capability |= SNDRV_SEQ_PORT_CAP_READ | 3574025f0e6STakashi Iwai SNDRV_SEQ_PORT_CAP_SYNC_READ | 3584025f0e6STakashi Iwai SNDRV_SEQ_PORT_CAP_SUBS_READ; 3594025f0e6STakashi Iwai port->direction |= SNDRV_SEQ_PORT_DIR_INPUT; 3604025f0e6STakashi Iwai } 3614025f0e6STakashi Iwai if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) { 3624025f0e6STakashi Iwai port->capability |= SNDRV_SEQ_PORT_CAP_WRITE | 3634025f0e6STakashi Iwai SNDRV_SEQ_PORT_CAP_SYNC_WRITE | 3644025f0e6STakashi Iwai SNDRV_SEQ_PORT_CAP_SUBS_WRITE; 3654025f0e6STakashi Iwai port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT; 3664025f0e6STakashi Iwai } 3674025f0e6STakashi Iwai if (rawmidi_info & SNDRV_RAWMIDI_INFO_DUPLEX) 3684025f0e6STakashi Iwai port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; 3694025f0e6STakashi Iwai port->ump_group = 0; /* no associated group, no conversion */ 3704025f0e6STakashi Iwai port->type = SNDRV_SEQ_PORT_TYPE_MIDI_UMP | 3714025f0e6STakashi Iwai SNDRV_SEQ_PORT_TYPE_HARDWARE | 3724025f0e6STakashi Iwai SNDRV_SEQ_PORT_TYPE_PORT; 3734025f0e6STakashi Iwai port->midi_channels = 16; 3744025f0e6STakashi Iwai strcpy(port->name, "MIDI 2.0"); 3754025f0e6STakashi Iwai memset(&pcallbacks, 0, sizeof(pcallbacks)); 3764025f0e6STakashi Iwai pcallbacks.owner = THIS_MODULE; 3774025f0e6STakashi Iwai pcallbacks.private_data = client; 3784025f0e6STakashi Iwai if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) { 3794025f0e6STakashi Iwai pcallbacks.subscribe = seq_ump_subscribe; 3804025f0e6STakashi Iwai pcallbacks.unsubscribe = seq_ump_unsubscribe; 3814025f0e6STakashi Iwai } 3824025f0e6STakashi Iwai if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) { 3834025f0e6STakashi Iwai pcallbacks.use = seq_ump_use; 3844025f0e6STakashi Iwai pcallbacks.unuse = seq_ump_unuse; 3854025f0e6STakashi Iwai pcallbacks.event_input = seq_ump_process_event; 3864025f0e6STakashi Iwai } 3874025f0e6STakashi Iwai port->kernel = &pcallbacks; 3884025f0e6STakashi Iwai err = snd_seq_kernel_client_ctl(client->seq_client, 3894025f0e6STakashi Iwai SNDRV_SEQ_IOCTL_CREATE_PORT, 3904025f0e6STakashi Iwai port); 3914025f0e6STakashi Iwai return err; 3924025f0e6STakashi Iwai } 3934025f0e6STakashi Iwai 39481fd444aSTakashi Iwai /* release the client resources */ 39581fd444aSTakashi Iwai static void seq_ump_client_free(struct seq_ump_client *client) 39681fd444aSTakashi Iwai { 3974a16a3afSTakashi Iwai cancel_work_sync(&client->group_notify_work); 3984a16a3afSTakashi Iwai 39981fd444aSTakashi Iwai if (client->seq_client >= 0) 40081fd444aSTakashi Iwai snd_seq_delete_kernel_client(client->seq_client); 40181fd444aSTakashi Iwai 40281fd444aSTakashi Iwai client->ump->seq_ops = NULL; 40381fd444aSTakashi Iwai client->ump->seq_client = NULL; 40481fd444aSTakashi Iwai 40581fd444aSTakashi Iwai kfree(client); 40681fd444aSTakashi Iwai } 40781fd444aSTakashi Iwai 40881fd444aSTakashi Iwai /* update the MIDI version for the given client */ 40981fd444aSTakashi Iwai static void setup_client_midi_version(struct seq_ump_client *client) 41081fd444aSTakashi Iwai { 41181fd444aSTakashi Iwai struct snd_seq_client *cptr; 41281fd444aSTakashi Iwai 41381fd444aSTakashi Iwai cptr = snd_seq_kernel_client_get(client->seq_client); 41481fd444aSTakashi Iwai if (!cptr) 41581fd444aSTakashi Iwai return; 41681fd444aSTakashi Iwai if (client->ump->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2) 41781fd444aSTakashi Iwai cptr->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_2_0; 41881fd444aSTakashi Iwai else 41981fd444aSTakashi Iwai cptr->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_1_0; 42081fd444aSTakashi Iwai snd_seq_kernel_client_put(cptr); 42181fd444aSTakashi Iwai } 42281fd444aSTakashi Iwai 42322eefaeaSTakashi Iwai /* set up client's group_filter bitmap */ 42422eefaeaSTakashi Iwai static void setup_client_group_filter(struct seq_ump_client *client) 42522eefaeaSTakashi Iwai { 42622eefaeaSTakashi Iwai struct snd_seq_client *cptr; 42722eefaeaSTakashi Iwai unsigned int filter; 42822eefaeaSTakashi Iwai int p; 42922eefaeaSTakashi Iwai 43022eefaeaSTakashi Iwai cptr = snd_seq_kernel_client_get(client->seq_client); 43122eefaeaSTakashi Iwai if (!cptr) 43222eefaeaSTakashi Iwai return; 43322eefaeaSTakashi Iwai filter = ~(1U << 0); /* always allow groupless messages */ 43422eefaeaSTakashi Iwai for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) { 43522eefaeaSTakashi Iwai if (client->groups[p].active) 43622eefaeaSTakashi Iwai filter &= ~(1U << (p + 1)); 43722eefaeaSTakashi Iwai } 43822eefaeaSTakashi Iwai cptr->group_filter = filter; 43922eefaeaSTakashi Iwai snd_seq_kernel_client_put(cptr); 44022eefaeaSTakashi Iwai } 44122eefaeaSTakashi Iwai 4424a16a3afSTakashi Iwai /* UMP group change notification */ 4434a16a3afSTakashi Iwai static void handle_group_notify(struct work_struct *work) 4444a16a3afSTakashi Iwai { 4454a16a3afSTakashi Iwai struct seq_ump_client *client = 4464a16a3afSTakashi Iwai container_of(work, struct seq_ump_client, group_notify_work); 4474a16a3afSTakashi Iwai 4484a16a3afSTakashi Iwai update_group_attrs(client); 4494a16a3afSTakashi Iwai update_port_infos(client); 45022eefaeaSTakashi Iwai setup_client_group_filter(client); 4514a16a3afSTakashi Iwai } 4524a16a3afSTakashi Iwai 4534a16a3afSTakashi Iwai /* UMP FB change notification */ 4544a16a3afSTakashi Iwai static int seq_ump_notify_fb_change(struct snd_ump_endpoint *ump, 4554a16a3afSTakashi Iwai struct snd_ump_block *fb) 4564a16a3afSTakashi Iwai { 4574a16a3afSTakashi Iwai struct seq_ump_client *client = ump->seq_client; 4584a16a3afSTakashi Iwai 4594a16a3afSTakashi Iwai if (!client) 4604a16a3afSTakashi Iwai return -ENODEV; 4614a16a3afSTakashi Iwai schedule_work(&client->group_notify_work); 4624a16a3afSTakashi Iwai return 0; 4634a16a3afSTakashi Iwai } 4644a16a3afSTakashi Iwai 4656a8b4800STakashi Iwai /* UMP protocol change notification; just update the midi_version field */ 4666a8b4800STakashi Iwai static int seq_ump_switch_protocol(struct snd_ump_endpoint *ump) 4676a8b4800STakashi Iwai { 4686a8b4800STakashi Iwai if (!ump->seq_client) 4696a8b4800STakashi Iwai return -ENODEV; 4706a8b4800STakashi Iwai setup_client_midi_version(ump->seq_client); 4716a8b4800STakashi Iwai return 0; 4726a8b4800STakashi Iwai } 4736a8b4800STakashi Iwai 47481fd444aSTakashi Iwai static const struct snd_seq_ump_ops seq_ump_ops = { 47581fd444aSTakashi Iwai .input_receive = seq_ump_input_receive, 4764a16a3afSTakashi Iwai .notify_fb_change = seq_ump_notify_fb_change, 4776a8b4800STakashi Iwai .switch_protocol = seq_ump_switch_protocol, 47881fd444aSTakashi Iwai }; 47981fd444aSTakashi Iwai 48081fd444aSTakashi Iwai /* create a sequencer client and ports for the given UMP endpoint */ 48181fd444aSTakashi Iwai static int snd_seq_ump_probe(struct device *_dev) 48281fd444aSTakashi Iwai { 48381fd444aSTakashi Iwai struct snd_seq_device *dev = to_seq_dev(_dev); 48481fd444aSTakashi Iwai struct snd_ump_endpoint *ump = dev->private_data; 48581fd444aSTakashi Iwai struct snd_card *card = dev->card; 48681fd444aSTakashi Iwai struct seq_ump_client *client; 487d2d247e3STakashi Iwai struct snd_ump_block *fb; 488d2d247e3STakashi Iwai struct snd_seq_client *cptr; 48981fd444aSTakashi Iwai int p, err; 49081fd444aSTakashi Iwai 49181fd444aSTakashi Iwai client = kzalloc(sizeof(*client), GFP_KERNEL); 49281fd444aSTakashi Iwai if (!client) 49381fd444aSTakashi Iwai return -ENOMEM; 49481fd444aSTakashi Iwai 4954a16a3afSTakashi Iwai INIT_WORK(&client->group_notify_work, handle_group_notify); 49681fd444aSTakashi Iwai client->ump = ump; 49781fd444aSTakashi Iwai 49881fd444aSTakashi Iwai client->seq_client = 49981fd444aSTakashi Iwai snd_seq_create_kernel_client(card, ump->core.device, 50081fd444aSTakashi Iwai ump->core.name); 50181fd444aSTakashi Iwai if (client->seq_client < 0) { 50281fd444aSTakashi Iwai err = client->seq_client; 50381fd444aSTakashi Iwai goto error; 50481fd444aSTakashi Iwai } 50581fd444aSTakashi Iwai 506d2d247e3STakashi Iwai client->ump_info[0] = &ump->info; 507d2d247e3STakashi Iwai list_for_each_entry(fb, &ump->block_list, list) 508d2d247e3STakashi Iwai client->ump_info[fb->info.block_id + 1] = &fb->info; 509d2d247e3STakashi Iwai 51081fd444aSTakashi Iwai setup_client_midi_version(client); 51181fd444aSTakashi Iwai update_group_attrs(client); 51281fd444aSTakashi Iwai 51381fd444aSTakashi Iwai for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) { 51481fd444aSTakashi Iwai err = seq_ump_group_init(client, p); 51581fd444aSTakashi Iwai if (err < 0) 51681fd444aSTakashi Iwai goto error; 51781fd444aSTakashi Iwai } 51881fd444aSTakashi Iwai 51922eefaeaSTakashi Iwai setup_client_group_filter(client); 52022eefaeaSTakashi Iwai 5214025f0e6STakashi Iwai err = create_ump_endpoint_port(client); 5224025f0e6STakashi Iwai if (err < 0) 5234025f0e6STakashi Iwai goto error; 5244025f0e6STakashi Iwai 525d2d247e3STakashi Iwai cptr = snd_seq_kernel_client_get(client->seq_client); 526d2d247e3STakashi Iwai if (!cptr) { 527d2d247e3STakashi Iwai err = -EINVAL; 528d2d247e3STakashi Iwai goto error; 529d2d247e3STakashi Iwai } 530d2d247e3STakashi Iwai cptr->ump_info = client->ump_info; 531d2d247e3STakashi Iwai snd_seq_kernel_client_put(cptr); 532d2d247e3STakashi Iwai 53381fd444aSTakashi Iwai ump->seq_client = client; 53481fd444aSTakashi Iwai ump->seq_ops = &seq_ump_ops; 53581fd444aSTakashi Iwai return 0; 53681fd444aSTakashi Iwai 53781fd444aSTakashi Iwai error: 53881fd444aSTakashi Iwai seq_ump_client_free(client); 53981fd444aSTakashi Iwai return err; 54081fd444aSTakashi Iwai } 54181fd444aSTakashi Iwai 54281fd444aSTakashi Iwai /* remove a sequencer client */ 54381fd444aSTakashi Iwai static int snd_seq_ump_remove(struct device *_dev) 54481fd444aSTakashi Iwai { 54581fd444aSTakashi Iwai struct snd_seq_device *dev = to_seq_dev(_dev); 54681fd444aSTakashi Iwai struct snd_ump_endpoint *ump = dev->private_data; 54781fd444aSTakashi Iwai 54881fd444aSTakashi Iwai if (ump->seq_client) 54981fd444aSTakashi Iwai seq_ump_client_free(ump->seq_client); 55081fd444aSTakashi Iwai return 0; 55181fd444aSTakashi Iwai } 55281fd444aSTakashi Iwai 55381fd444aSTakashi Iwai static struct snd_seq_driver seq_ump_driver = { 55481fd444aSTakashi Iwai .driver = { 55581fd444aSTakashi Iwai .name = KBUILD_MODNAME, 55681fd444aSTakashi Iwai .probe = snd_seq_ump_probe, 55781fd444aSTakashi Iwai .remove = snd_seq_ump_remove, 55881fd444aSTakashi Iwai }, 55981fd444aSTakashi Iwai .id = SNDRV_SEQ_DEV_ID_UMP, 56081fd444aSTakashi Iwai .argsize = 0, 56181fd444aSTakashi Iwai }; 56281fd444aSTakashi Iwai 56381fd444aSTakashi Iwai module_snd_seq_driver(seq_ump_driver); 56481fd444aSTakashi Iwai 56581fd444aSTakashi Iwai MODULE_DESCRIPTION("ALSA sequencer client for UMP rawmidi"); 56681fd444aSTakashi Iwai MODULE_LICENSE("GPL"); 567