mirror of
https://github.com/lkl/linux.git
synced 2025-12-19 16:13:19 +09:00
ALSA: seq: Add snd_seq_expand_var_event_at() helper
Create a new variant of snd_seq_expand_var_event() for expanding the data starting from the given byte offset. It'll be used by the new UMP sequencer code later. Reviewed-by: Jaroslav Kysela <perex@perex.cz> Link: https://lore.kernel.org/r/20230523075358.9672-19-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
@@ -70,6 +70,8 @@ int snd_seq_kernel_client_ctl(int client, unsigned int cmd, void *arg);
|
|||||||
typedef int (*snd_seq_dump_func_t)(void *ptr, void *buf, int count);
|
typedef int (*snd_seq_dump_func_t)(void *ptr, void *buf, int count);
|
||||||
int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char *buf,
|
int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char *buf,
|
||||||
int in_kernel, int size_aligned);
|
int in_kernel, int size_aligned);
|
||||||
|
int snd_seq_expand_var_event_at(const struct snd_seq_event *event, int count,
|
||||||
|
char *buf, int offset);
|
||||||
int snd_seq_dump_var_event(const struct snd_seq_event *event,
|
int snd_seq_dump_var_event(const struct snd_seq_event *event,
|
||||||
snd_seq_dump_func_t func, void *private_data);
|
snd_seq_dump_func_t func, void *private_data);
|
||||||
|
|
||||||
|
|||||||
@@ -63,8 +63,9 @@ static int get_var_len(const struct snd_seq_event *event)
|
|||||||
return event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
|
return event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int snd_seq_dump_var_event(const struct snd_seq_event *event,
|
static int dump_var_event(const struct snd_seq_event *event,
|
||||||
snd_seq_dump_func_t func, void *private_data)
|
snd_seq_dump_func_t func, void *private_data,
|
||||||
|
int offset, int maxlen)
|
||||||
{
|
{
|
||||||
int len, err;
|
int len, err;
|
||||||
struct snd_seq_event_cell *cell;
|
struct snd_seq_event_cell *cell;
|
||||||
@@ -72,10 +73,16 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event,
|
|||||||
len = get_var_len(event);
|
len = get_var_len(event);
|
||||||
if (len <= 0)
|
if (len <= 0)
|
||||||
return len;
|
return len;
|
||||||
|
if (len <= offset)
|
||||||
|
return 0;
|
||||||
|
if (maxlen && len > offset + maxlen)
|
||||||
|
len = offset + maxlen;
|
||||||
|
|
||||||
if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
|
if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
|
||||||
char buf[32];
|
char buf[32];
|
||||||
char __user *curptr = (char __force __user *)event->data.ext.ptr;
|
char __user *curptr = (char __force __user *)event->data.ext.ptr;
|
||||||
|
curptr += offset;
|
||||||
|
len -= offset;
|
||||||
while (len > 0) {
|
while (len > 0) {
|
||||||
int size = sizeof(buf);
|
int size = sizeof(buf);
|
||||||
if (len < size)
|
if (len < size)
|
||||||
@@ -91,20 +98,35 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!(event->data.ext.len & SNDRV_SEQ_EXT_CHAINED))
|
if (!(event->data.ext.len & SNDRV_SEQ_EXT_CHAINED))
|
||||||
return func(private_data, event->data.ext.ptr, len);
|
return func(private_data, event->data.ext.ptr + offset,
|
||||||
|
len - offset);
|
||||||
|
|
||||||
cell = (struct snd_seq_event_cell *)event->data.ext.ptr;
|
cell = (struct snd_seq_event_cell *)event->data.ext.ptr;
|
||||||
for (; len > 0 && cell; cell = cell->next) {
|
for (; len > 0 && cell; cell = cell->next) {
|
||||||
int size = sizeof(struct snd_seq_event);
|
int size = sizeof(struct snd_seq_event);
|
||||||
|
char *curptr = (char *)&cell->event;
|
||||||
|
|
||||||
|
if (offset >= size) {
|
||||||
|
offset -= size;
|
||||||
|
len -= size;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (len < size)
|
if (len < size)
|
||||||
size = len;
|
size = len;
|
||||||
err = func(private_data, &cell->event, size);
|
err = func(private_data, curptr + offset, size - offset);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
offset = 0;
|
||||||
len -= size;
|
len -= size;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int snd_seq_dump_var_event(const struct snd_seq_event *event,
|
||||||
|
snd_seq_dump_func_t func, void *private_data)
|
||||||
|
{
|
||||||
|
return dump_var_event(event, func, private_data, 0, 0);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(snd_seq_dump_var_event);
|
EXPORT_SYMBOL(snd_seq_dump_var_event);
|
||||||
|
|
||||||
|
|
||||||
@@ -132,11 +154,27 @@ static int seq_copy_in_user(void *ptr, void *src, int size)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int expand_var_event(const struct snd_seq_event *event,
|
||||||
|
int offset, int size, char *buf, bool in_kernel)
|
||||||
|
{
|
||||||
|
if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
|
||||||
|
if (! in_kernel)
|
||||||
|
return -EINVAL;
|
||||||
|
if (copy_from_user(buf,
|
||||||
|
(char __force __user *)event->data.ext.ptr + offset,
|
||||||
|
size))
|
||||||
|
return -EFAULT;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return dump_var_event(event,
|
||||||
|
in_kernel ? seq_copy_in_kernel : seq_copy_in_user,
|
||||||
|
&buf, offset, size);
|
||||||
|
}
|
||||||
|
|
||||||
int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char *buf,
|
int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char *buf,
|
||||||
int in_kernel, int size_aligned)
|
int in_kernel, int size_aligned)
|
||||||
{
|
{
|
||||||
int len, newlen;
|
int len, newlen, err;
|
||||||
int err;
|
|
||||||
|
|
||||||
len = get_var_len(event);
|
len = get_var_len(event);
|
||||||
if (len < 0)
|
if (len < 0)
|
||||||
@@ -146,25 +184,35 @@ int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char
|
|||||||
newlen = roundup(len, size_aligned);
|
newlen = roundup(len, size_aligned);
|
||||||
if (count < newlen)
|
if (count < newlen)
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
err = expand_var_event(event, 0, len, buf, in_kernel);
|
||||||
if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
|
if (err < 0)
|
||||||
if (! in_kernel)
|
return err;
|
||||||
return -EINVAL;
|
|
||||||
if (copy_from_user(buf, (void __force __user *)event->data.ext.ptr, len))
|
|
||||||
return -EFAULT;
|
|
||||||
} else {
|
|
||||||
err = snd_seq_dump_var_event(event,
|
|
||||||
in_kernel ? seq_copy_in_kernel : seq_copy_in_user,
|
|
||||||
&buf);
|
|
||||||
if (err < 0)
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
if (len != newlen)
|
if (len != newlen)
|
||||||
memset(buf + len, 0, newlen - len);
|
memset(buf + len, 0, newlen - len);
|
||||||
return newlen;
|
return newlen;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(snd_seq_expand_var_event);
|
EXPORT_SYMBOL(snd_seq_expand_var_event);
|
||||||
|
|
||||||
|
int snd_seq_expand_var_event_at(const struct snd_seq_event *event, int count,
|
||||||
|
char *buf, int offset)
|
||||||
|
{
|
||||||
|
int len, err;
|
||||||
|
|
||||||
|
len = get_var_len(event);
|
||||||
|
if (len < 0)
|
||||||
|
return len;
|
||||||
|
if (len <= offset)
|
||||||
|
return 0;
|
||||||
|
len -= offset;
|
||||||
|
if (len > count)
|
||||||
|
len = count;
|
||||||
|
err = expand_var_event(event, offset, count, buf, true);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(snd_seq_expand_var_event_at);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* release this cell, free extended data if available
|
* release this cell, free extended data if available
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user