ALSA: seq: Add snd_seq_expand_var_event_at() helper
authorTakashi Iwai <tiwai@suse.de>
Tue, 23 May 2023 07:53:39 +0000 (09:53 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 23 May 2023 10:11:14 +0000 (12:11 +0200)
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>
include/sound/seq_kernel.h
sound/core/seq/seq_memory.c

index 6589119..527e7f8 100644 (file)
@@ -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);
 
index c8d26bc..a8d2db4 100644 (file)
@@ -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);
-               if (err < 0)
-                       return err;
-       }
+       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
  */