Commit d2d247e3 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: seq: Add ioctls for client UMP info query and setup



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: default avatarJaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-35-tiwai@suse.de


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 4025f0e6
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -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)
+119 −1
Original line number Diff line number Diff line
@@ -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;
+3 −1
Original line number Diff line number Diff line
@@ -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 */
+2 −0
Original line number Diff line number Diff line
@@ -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:
+15 −0
Original line number Diff line number Diff line
@@ -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;