Skip to content

Commit

Permalink
ALSA: seq: Add ioctls for client UMP info query and setup
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Takashi Iwai committed May 23, 2023
1 parent 4025f0e commit d2d247e
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 2 deletions.
14 changes: 14 additions & 0 deletions include/uapi/sound/asequencer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
120 changes: 119 additions & 1 deletion sound/core/seq/seq_clientmgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion sound/core/seq/seq_clientmgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
#include "seq_ports.h"
#include "seq_lock.h"


/* client manager */

struct snd_seq_user_client {
Expand Down Expand Up @@ -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 */
Expand Down
2 changes: 2 additions & 0 deletions sound/core/seq/seq_compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
15 changes: 15 additions & 0 deletions sound/core/seq/seq_ump_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
Expand All @@ -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);

Expand All @@ -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;
Expand Down

0 comments on commit d2d247e

Please sign in to comment.