mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
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: 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>
This commit is contained in:
parent
4025f0e627
commit
d2d247e35e
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user