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

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


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent f80e6d60
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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);
int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char *buf,
			     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,
			   snd_seq_dump_func_t func, void *private_data);

+67 −19
Original line number Diff line number Diff line
@@ -63,8 +63,9 @@ static int get_var_len(const struct snd_seq_event *event)
	return event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
}

int snd_seq_dump_var_event(const struct snd_seq_event *event,
			   snd_seq_dump_func_t func, void *private_data)
static int dump_var_event(const struct snd_seq_event *event,
			  snd_seq_dump_func_t func, void *private_data,
			  int offset, int maxlen)
{
	int len, err;
	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);
	if (len <= 0)
		return len;
	if (len <= offset)
		return 0;
	if (maxlen && len > offset + maxlen)
		len = offset + maxlen;

	if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
		char buf[32];
		char __user *curptr = (char __force __user *)event->data.ext.ptr;
		curptr += offset;
		len -= offset;
		while (len > 0) {
			int size = sizeof(buf);
			if (len < size)
@@ -91,20 +98,35 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event,
		return 0;
	}
	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;
	for (; len > 0 && cell; cell = cell->next) {
		int size = sizeof(struct snd_seq_event);
		char *curptr = (char *)&cell->event;

		if (offset >= size) {
			offset -= size;
			len -= size;
			continue;
		}
		if (len < size)
			size = len;
		err = func(private_data, &cell->event, size);
		err = func(private_data, curptr + offset, size - offset);
		if (err < 0)
			return err;
		offset = 0;
		len -= size;
	}
	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);


@@ -132,11 +154,27 @@ static int seq_copy_in_user(void *ptr, void *src, int size)
	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 in_kernel, int size_aligned)
{
	int len, newlen;
	int err;
	int len, newlen, err;

	len = get_var_len(event);
	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);
	if (count < newlen)
		return -EAGAIN;

	if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
		if (! in_kernel)
			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);
	err = expand_var_event(event, 0, len, buf, in_kernel);
	if (err < 0)
		return err;
	}
	if (len != newlen)
		memset(buf + len, 0, newlen - len);
	return newlen;
}
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
 */