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 */
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;
+ }
+ 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)
{
/* 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);
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;
+}
+
static const struct snd_seq_ump_ops seq_ump_ops = {
.input_receive = seq_ump_input_receive,
+ .notify_fb_change = seq_ump_notify_fb_change,
};
/* create a sequencer client and ports for the given UMP endpoint */
if (!client)
return -ENOMEM;
+ INIT_WORK(&client->group_notify_work, handle_group_notify);
client->ump = ump;
client->seq_client =
info->sysex8_streams, info->flags);
}
+/* check whether the FB info gets updated by the current message */
+static bool is_fb_info_updated(struct snd_ump_endpoint *ump,
+ struct snd_ump_block *fb,
+ const union snd_ump_stream_msg *buf)
+{
+ char tmpbuf[offsetof(struct snd_ump_block_info, name)];
+
+ memcpy(tmpbuf, &fb->info, sizeof(tmpbuf));
+ fill_fb_info(ump, (struct snd_ump_block_info *)tmpbuf, buf);
+ return memcmp(&fb->info, tmpbuf, sizeof(tmpbuf)) != 0;
+}
+
+/* notify the FB info/name change to sequencer */
+static void seq_notify_fb_change(struct snd_ump_endpoint *ump,
+ struct snd_ump_block *fb)
+{
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+ if (ump->seq_ops && ump->seq_ops->notify_fb_change)
+ ump->seq_ops->notify_fb_change(ump, fb);
+#endif
+}
+
/* handle FB info message; update FB info if the block is present */
static int ump_handle_fb_info_msg(struct snd_ump_endpoint *ump,
const union snd_ump_stream_msg *buf)
blk = buf->fb_info.function_block_id;
fb = snd_ump_get_block(ump, blk);
- if (fb) {
- fill_fb_info(ump, &fb->info, buf);
- } else if (ump->parsed) {
- /* complain only if updated after parsing */
+
+ /* complain only if updated after parsing */
+ if (!fb && ump->parsed) {
ump_info(ump, "Function Block Info Update for non-existing block %d\n",
blk);
return -ENODEV;
}
+
+ /* When updated after the initial parse, check the FB info update */
+ if (ump->parsed && !is_fb_info_updated(ump, fb, buf))
+ return 1; /* no content change */
+
+ if (fb) {
+ fill_fb_info(ump, &fb->info, buf);
+ if (ump->parsed)
+ seq_notify_fb_change(ump, fb);
+ }
+
return 1; /* finished */
}
{
unsigned char blk;
struct snd_ump_block *fb;
+ int ret;
blk = buf->fb_name.function_block_id;
fb = snd_ump_get_block(ump, blk);
if (!fb)
return -ENODEV;
- return ump_append_string(ump, fb->info.name, sizeof(fb->info.name),
- buf->raw, 3);
+ ret = ump_append_string(ump, fb->info.name, sizeof(fb->info.name),
+ buf->raw, 3);
+ /* notify the FB name update to sequencer, too */
+ if (ret > 0 && ump->parsed)
+ seq_notify_fb_change(ump, fb);
+ return ret;
}
static int create_block_from_fb_info(struct snd_ump_endpoint *ump, int blk)