OSDN Git Service

ALSA: seq: Add ioctls for client UMP info query and setup
authorTakashi Iwai <tiwai@suse.de>
Tue, 23 May 2023 07:53:55 +0000 (09:53 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 23 May 2023 10:11:36 +0000 (12:11 +0200)
Add new ioctls for sequencer clients to query and set the UMP endpoint
and block information.

As a sequencer client corresponds to a UMP Endpoint, one UMP Endpoint
information can be assigned at most to a single sequencer client while
multiple UMP block infos can be assigned by passing the type with the
offset of block id (i.e. type = block_id + 1).

For the kernel client, only SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO is
allowed.

Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-35-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
include/uapi/sound/asequencer.h
sound/core/seq/seq_clientmgr.c
sound/core/seq/seq_clientmgr.h
sound/core/seq/seq_compat.c
sound/core/seq/seq_ump_client.c

index 3fa6b17..c75f594 100644 (file)
@@ -585,6 +585,18 @@ struct snd_seq_query_subs {
        char reserved[64];      /* for future use */
 };
 
+/*
+ * UMP-specific information
+ */
+/* type of UMP info query */
+#define SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT     0
+#define SNDRV_SEQ_CLIENT_UMP_INFO_BLOCK                1
+
+struct snd_seq_client_ump_info {
+       int client;                     /* client number to inquire/set */
+       int type;                       /* type to inquire/set */
+       unsigned char info[512];        /* info (either UMP ep or block info) */
+} __packed;
 
 /*
  *  IOCTL commands
@@ -598,6 +610,8 @@ struct snd_seq_query_subs {
 
 #define SNDRV_SEQ_IOCTL_GET_CLIENT_INFO        _IOWR('S', 0x10, struct snd_seq_client_info)
 #define SNDRV_SEQ_IOCTL_SET_CLIENT_INFO        _IOW ('S', 0x11, struct snd_seq_client_info)
+#define SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO    _IOWR('S', 0x12, struct snd_seq_client_ump_info)
+#define SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO    _IOWR('S', 0x13, struct snd_seq_client_ump_info)
 
 #define SNDRV_SEQ_IOCTL_CREATE_PORT    _IOWR('S', 0x20, struct snd_seq_port_info)
 #define SNDRV_SEQ_IOCTL_DELETE_PORT    _IOW ('S', 0x21, struct snd_seq_port_info)
index 3b1adcb..03ca78e 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/kmod.h>
 
 #include <sound/seq_kernel.h>
+#include <sound/ump.h>
 #include "seq_clientmgr.h"
 #include "seq_memory.h"
 #include "seq_queue.h"
@@ -71,6 +72,10 @@ static int snd_seq_deliver_single_event(struct snd_seq_client *client,
                                        struct snd_seq_event *event,
                                        int filter, int atomic, int hop);
 
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+static void free_ump_info(struct snd_seq_client *client);
+#endif
+
 /*
  */
 static inline unsigned short snd_seq_file_flags(struct file *file)
@@ -382,6 +387,9 @@ static int snd_seq_release(struct inode *inode, struct file *file)
                seq_free_client(client);
                if (client->data.user.fifo)
                        snd_seq_fifo_delete(&client->data.user.fifo);
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+               free_ump_info(client);
+#endif
                put_pid(client->data.user.owner);
                kfree(client);
        }
@@ -1282,7 +1290,6 @@ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client,
        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);
-
        return 0;
 }
 
@@ -2087,6 +2094,108 @@ static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client,
        return 0;
 }
 
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+#define NUM_UMP_INFOS (SNDRV_UMP_MAX_BLOCKS + 1)
+
+static void free_ump_info(struct snd_seq_client *client)
+{
+       int i;
+
+       if (!client->ump_info)
+               return;
+       for (i = 0; i < NUM_UMP_INFOS; i++)
+               kfree(client->ump_info[i]);
+       kfree(client->ump_info);
+       client->ump_info = NULL;
+}
+
+static void terminate_ump_info_strings(void *p, int type)
+{
+       if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT) {
+               struct snd_ump_endpoint_info *ep = p;
+               ep->name[sizeof(ep->name) - 1] = 0;
+       } else {
+               struct snd_ump_block_info *bp = p;
+               bp->name[sizeof(bp->name) - 1] = 0;
+       }
+}
+
+/* UMP-specific ioctls -- called directly without data copy */
+static int snd_seq_ioctl_client_ump_info(struct snd_seq_client *caller,
+                                        unsigned int cmd,
+                                        unsigned long arg)
+{
+       struct snd_seq_client_ump_info __user *argp =
+               (struct snd_seq_client_ump_info __user *)arg;
+       struct snd_seq_client *cptr;
+       int client, type, err = 0;
+       size_t size;
+       void *p;
+
+       if (get_user(client, &argp->client) || get_user(type, &argp->type))
+               return -EFAULT;
+       if (cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO &&
+           caller->number != client)
+               return -EPERM;
+       if (type < 0 || type >= NUM_UMP_INFOS)
+               return -EINVAL;
+       if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT)
+               size = sizeof(struct snd_ump_endpoint_info);
+       else
+               size = sizeof(struct snd_ump_block_info);
+       cptr = snd_seq_client_use_ptr(client);
+       if (!cptr)
+               return -ENOENT;
+
+       mutex_lock(&cptr->ioctl_mutex);
+       if (!cptr->midi_version) {
+               err = -EBADFD;
+               goto error;
+       }
+
+       if (cmd == SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO) {
+               if (!cptr->ump_info)
+                       p = NULL;
+               else
+                       p = cptr->ump_info[type];
+               if (!p) {
+                       err = -ENODEV;
+                       goto error;
+               }
+               if (copy_to_user(argp->info, p, size)) {
+                       err = -EFAULT;
+                       goto error;
+               }
+       } else {
+               if (cptr->type != USER_CLIENT) {
+                       err = -EBADFD;
+                       goto error;
+               }
+               if (!cptr->ump_info) {
+                       cptr->ump_info = kcalloc(NUM_UMP_INFOS,
+                                                sizeof(void *), GFP_KERNEL);
+                       if (!cptr->ump_info) {
+                               err = -ENOMEM;
+                               goto error;
+                       }
+               }
+               p = memdup_user(argp->info, size);
+               if (IS_ERR(p)) {
+                       err = PTR_ERR(p);
+                       goto error;
+               }
+               kfree(cptr->ump_info[type]);
+               terminate_ump_info_strings(p, type);
+               cptr->ump_info[type] = p;
+       }
+
+ error:
+       mutex_unlock(&cptr->ioctl_mutex);
+       snd_seq_client_unlock(cptr);
+       return err;
+}
+#endif
+
 /* -------------------------------------------------------- */
 
 static const struct ioctl_handler {
@@ -2157,6 +2266,15 @@ static long snd_seq_ioctl(struct file *file, unsigned int cmd,
        if (snd_BUG_ON(!client))
                return -ENXIO;
 
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+       /* exception - handling large data */
+       switch (cmd) {
+       case SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO:
+       case SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO:
+               return snd_seq_ioctl_client_ump_info(client, cmd, arg);
+       }
+#endif
+
        for (handler = ioctl_handlers; handler->cmd > 0; ++handler) {
                if (handler->cmd == cmd)
                        break;
index 9776289..be3fe55 100644 (file)
@@ -12,7 +12,6 @@
 #include "seq_ports.h"
 #include "seq_lock.h"
 
-
 /* client manager */
 
 struct snd_seq_user_client {
@@ -59,6 +58,9 @@ struct snd_seq_client {
                struct snd_seq_user_client user;
                struct snd_seq_kernel_client kernel;
        } data;
+
+       /* for UMP */
+       void **ump_info;
 };
 
 /* usage statistics */
index c0ce623..1e35bf0 100644 (file)
@@ -86,6 +86,8 @@ static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
        case SNDRV_SEQ_IOCTL_SYSTEM_INFO:
        case SNDRV_SEQ_IOCTL_GET_CLIENT_INFO:
        case SNDRV_SEQ_IOCTL_SET_CLIENT_INFO:
+       case SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO:
+       case SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO:
        case SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT:
        case SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT:
        case SNDRV_SEQ_IOCTL_CREATE_QUEUE:
index 600b061..e248338 100644 (file)
@@ -47,6 +47,7 @@ struct seq_ump_client {
        struct snd_rawmidi_file out_rfile; /* rawmidi for output */
        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 */
 };
 
 /* number of 32bit words for each UMP message type */
@@ -384,6 +385,8 @@ static int snd_seq_ump_probe(struct device *_dev)
        struct snd_ump_endpoint *ump = dev->private_data;
        struct snd_card *card = dev->card;
        struct seq_ump_client *client;
+       struct snd_ump_block *fb;
+       struct snd_seq_client *cptr;
        int p, err;
 
        client = kzalloc(sizeof(*client), GFP_KERNEL);
@@ -400,6 +403,10 @@ static int snd_seq_ump_probe(struct device *_dev)
                goto error;
        }
 
+       client->ump_info[0] = &ump->info;
+       list_for_each_entry(fb, &ump->block_list, list)
+               client->ump_info[fb->info.block_id + 1] = &fb->info;
+
        setup_client_midi_version(client);
        update_group_attrs(client);
 
@@ -413,6 +420,14 @@ static int snd_seq_ump_probe(struct device *_dev)
        if (err < 0)
                goto error;
 
+       cptr = snd_seq_kernel_client_get(client->seq_client);
+       if (!cptr) {
+               err = -EINVAL;
+               goto error;
+       }
+       cptr->ump_info = client->ump_info;
+       snd_seq_kernel_client_put(cptr);
+
        ump->seq_client = client;
        ump->seq_ops = &seq_ump_ops;
        return 0;