diff mbox series

[v5] sound: rawmidi: Add framing mode

Message ID 20210418151217.208582-1-coding@diwic.se (mailing list archive)
State Superseded
Headers show
Series [v5] sound: rawmidi: Add framing mode | expand

Commit Message

David Henningsson April 18, 2021, 3:12 p.m. UTC
This commit adds a new framing mode that frames all MIDI data into
32-byte frames with a timestamp.

The main benefit is that we can get accurate timestamps even if
userspace wakeup and processing is not immediate.

Testing on a Celeron N3150 with this mode has a max jitter of 2.8 ms,
compared to the in-kernel seq implementation which has a max jitter
of 5 ms during idle and much worse when running scheduler stress tests
in parallel.

Signed-off-by: David Henningsson <coding@diwic.se>
---

This version of the patch has been compile tested only, and
compared to v4, has only the changes with no controversy.

 include/sound/rawmidi.h     |  2 +
 include/uapi/sound/asound.h | 26 ++++++++++++-
 sound/core/rawmidi.c        | 73 ++++++++++++++++++++++++++++++++++++-
 sound/core/rawmidi_compat.c |  6 ++-
 4 files changed, 103 insertions(+), 4 deletions(-)

Comments

kernel test robot April 18, 2021, 4:49 p.m. UTC | #1
Hi David,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on sound/for-next]
[also build test WARNING on v5.12-rc7 next-20210416]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/David-Henningsson/sound-rawmidi-Add-framing-mode/20210418-231512
base:   https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git for-next
config: x86_64-randconfig-a001-20210418 (attached as .config)
compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 1c10201d9660c1d6f43a7226ca7381bfa255105d)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # https://github.com/0day-ci/linux/commit/6b82e8d56896a3eb7f9024ae5838c4c3402bc4a4
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review David-Henningsson/sound-rawmidi-Add-framing-mode/20210418-231512
        git checkout 6b82e8d56896a3eb7f9024ae5838c4c3402bc4a4
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> sound/core/rawmidi.c:1014:19: warning: no previous prototype for function 'get_framing_tstamp' [-Wmissing-prototypes]
   struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream)
                     ^
   sound/core/rawmidi.c:1014:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream)
   ^
   static 
   1 warning generated.


vim +/get_framing_tstamp +1014 sound/core/rawmidi.c

  1013	
> 1014	struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream)
  1015	{
  1016		struct timespec64 ts64 = {0, 0};
  1017	
  1018		if (substream->framing != SNDRV_RAWMIDI_FRAMING_TSTAMP)
  1019			return ts64;
  1020		if (substream->clock_type == CLOCK_MONOTONIC_RAW)
  1021			ktime_get_raw_ts64(&ts64);
  1022		else
  1023			ktime_get_ts64(&ts64);
  1024		return ts64;
  1025	}
  1026	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
kernel test robot April 18, 2021, 4:49 p.m. UTC | #2
Hi David,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on sound/for-next]
[also build test WARNING on v5.12-rc7 next-20210416]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/David-Henningsson/sound-rawmidi-Add-framing-mode/20210418-231512
base:   https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git for-next
config: nios2-randconfig-r031-20210418 (attached as .config)
compiler: nios2-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/6b82e8d56896a3eb7f9024ae5838c4c3402bc4a4
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review David-Henningsson/sound-rawmidi-Add-framing-mode/20210418-231512
        git checkout 6b82e8d56896a3eb7f9024ae5838c4c3402bc4a4
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross W=1 ARCH=nios2 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> sound/core/rawmidi.c:1014:19: warning: no previous prototype for 'get_framing_tstamp' [-Wmissing-prototypes]
    1014 | struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream)
         |                   ^~~~~~~~~~~~~~~~~~


vim +/get_framing_tstamp +1014 sound/core/rawmidi.c

  1013	
> 1014	struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream)
  1015	{
  1016		struct timespec64 ts64 = {0, 0};
  1017	
  1018		if (substream->framing != SNDRV_RAWMIDI_FRAMING_TSTAMP)
  1019			return ts64;
  1020		if (substream->clock_type == CLOCK_MONOTONIC_RAW)
  1021			ktime_get_raw_ts64(&ts64);
  1022		else
  1023			ktime_get_ts64(&ts64);
  1024		return ts64;
  1025	}
  1026	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
kernel test robot April 18, 2021, 5:08 p.m. UTC | #3
Hi David,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on sound/for-next]
[also build test WARNING on v5.12-rc7 next-20210416]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/David-Henningsson/sound-rawmidi-Add-framing-mode/20210418-231512
base:   https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git for-next
config: sparc64-randconfig-m031-20210418 (attached as .config)
compiler: sparc64-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/6b82e8d56896a3eb7f9024ae5838c4c3402bc4a4
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review David-Henningsson/sound-rawmidi-Add-framing-mode/20210418-231512
        git checkout 6b82e8d56896a3eb7f9024ae5838c4c3402bc4a4
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross W=1 ARCH=sparc64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> sound/core/rawmidi.c:1014:19: warning: no previous prototype for 'get_framing_tstamp' [-Wmissing-prototypes]
    1014 | struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream)
         |                   ^~~~~~~~~~~~~~~~~~


vim +/get_framing_tstamp +1014 sound/core/rawmidi.c

  1013	
> 1014	struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream)
  1015	{
  1016		struct timespec64 ts64 = {0, 0};
  1017	
  1018		if (substream->framing != SNDRV_RAWMIDI_FRAMING_TSTAMP)
  1019			return ts64;
  1020		if (substream->clock_type == CLOCK_MONOTONIC_RAW)
  1021			ktime_get_raw_ts64(&ts64);
  1022		else
  1023			ktime_get_ts64(&ts64);
  1024		return ts64;
  1025	}
  1026	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Jaroslav Kysela April 18, 2021, 6:24 p.m. UTC | #4
Dne 18. 04. 21 v 17:12 David Henningsson napsal(a):

> +#define SND_RAWMIDI_FRAMING_DATA_LENGTH 16

SNDRV_ prefix should be here.

> +
> +struct snd_rawmidi_framing_tstamp {
> +	/* For now, frame_type is always 0. Midi 2.0 is expected to add new
> +	 * types here. Applications are expected to skip unknown frame types.
> +	 */
> +	u8 frame_type;
> +	u8 length; /* number of valid bytes in data field */
> +	u8 reserved[2];
> +	u32 tv_nsec;		/* nanoseconds */
> +	u64 tv_sec;		/* seconds */
> +	u8 data[SND_RAWMIDI_FRAMING_DATA_LENGTH];

What about to move the fields to union (except for frame_type) like we do for
'struct snd_ctl_event' in case when we need to reorganize the contents for
future types?

> +};
> +
>  struct snd_rawmidi_params {
>  	int stream;
>  	size_t buffer_size;		/* queue size in bytes */
>  	size_t avail_min;		/* minimum avail bytes for wakeup */
>  	unsigned int no_active_sensing: 1; /* do not send active sensing byte in close() */
> -	unsigned char reserved[16];	/* reserved for future use */
> +	unsigned char framing;		/* For input data only, frame incoming data */
> +	unsigned char clock_type;	/* Type of clock to use for framing, same as clockid_t */
> +	unsigned char reserved[14];	/* reserved for future use */

As I noted, I would prefer to add 'unsigned int mode;' and define
SNDRV_RAWMID_MODE_XXX bit flags and groups with framing and clock_type groups.
There's no reason to stick with 'clockid_t' (which is integer anyway). We're
using just a subset.

#define SNDRV_RAWMIDI_MODE_FRAMING_MASK        (7<<0)
#define SNDRV_RAWMIDI_MODE_FRAMING_SHIFT       0
#define SNDRV_RAWMIDI_MODE_FRAMING_NONE	       (0<<0)
#define SNDRV_RAWMIDI_MODE_FRAMING_32BYTES     (1<<0)
#define SNDRV_RAWMIDI_MODE_CLOCK_MASK          (7<<3)
#define SNDRV_RAWMIDI_MODE_CLOCK_SHIFT         3
#define SNDRV_RAWMIDI_MODE_CLOCK_NONE	       (0<<3)
#define SNDRV_RAWMIDI_MODE_CLOCK_REALTIME      (1<<3)
#define SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC     (2<<3)
#define SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW (3<<3)

In this case, we can use 26-bits in future for extensions.

> +struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream)
> +{
> +	struct timespec64 ts64 = {0, 0};
> +
> +	if (substream->framing != SNDRV_RAWMIDI_FRAMING_TSTAMP)
> +		return ts64;
> +	if (substream->clock_type == CLOCK_MONOTONIC_RAW)
> +		ktime_get_raw_ts64(&ts64);
> +	else
> +		ktime_get_ts64(&ts64);
> +	return ts64;
> +}

Missing the realtime clock type here.

					Jaroslav
David Henningsson April 19, 2021, 6:22 a.m. UTC | #5
On 2021-04-18 20:24, Jaroslav Kysela wrote:
> Dne 18. 04. 21 v 17:12 David Henningsson napsal(a):
>
>> +#define SND_RAWMIDI_FRAMING_DATA_LENGTH 16
> SNDRV_ prefix should be here.

Ack

>
>> +
>> +struct snd_rawmidi_framing_tstamp {
>> +	/* For now, frame_type is always 0. Midi 2.0 is expected to add new
>> +	 * types here. Applications are expected to skip unknown frame types.
>> +	 */
>> +	u8 frame_type;
>> +	u8 length; /* number of valid bytes in data field */
>> +	u8 reserved[2];
>> +	u32 tv_nsec;		/* nanoseconds */
>> +	u64 tv_sec;		/* seconds */
>> +	u8 data[SND_RAWMIDI_FRAMING_DATA_LENGTH];
> What about to move the fields to union (except for frame_type) like we do for
> 'struct snd_ctl_event' in case when we need to reorganize the contents for
> future types?

So the two degrees of freedom would be

1) the SNDRV_RAWMIDI_MODE_FRAMING_32BYTES indicates that the frame size 
is 32 bytes and the first byte of that frame is frame_type

2) the frame_type of every frame indicates the format of the other 31 
bytes, and an application is expected to ignore unknown frame_types, so 
we can add new frame_types in a backwards compatible way.

We'll end up with:

struct snd_rawmidi_framing_32bytes {
     u8 frame_type;
     union {
         struct {
             u8 length; /* number of valid bytes in data field */
             u8 reserved[2];
             u32 tv_nsec;        /* nanoseconds */
             u64 tv_sec;        /* seconds */
             u8 data[SNDRV_RAWMIDI_FRAMING_32BYTES_FOO_LENGTH];
         } foo;
         u8 reserved[31];
     } data;
};

...except I don't know what we should replace foo with. We can't call it 
"midi1" or "type0" or such because many different frame_types might 
share the same interior format.


>
>> +};
>> +
>>   struct snd_rawmidi_params {
>>   	int stream;
>>   	size_t buffer_size;		/* queue size in bytes */
>>   	size_t avail_min;		/* minimum avail bytes for wakeup */
>>   	unsigned int no_active_sensing: 1; /* do not send active sensing byte in close() */
>> -	unsigned char reserved[16];	/* reserved for future use */
>> +	unsigned char framing;		/* For input data only, frame incoming data */
>> +	unsigned char clock_type;	/* Type of clock to use for framing, same as clockid_t */
>> +	unsigned char reserved[14];	/* reserved for future use */
> As I noted, I would prefer to add 'unsigned int mode;' and define
> SNDRV_RAWMID_MODE_XXX bit flags and groups with framing and clock_type groups.
> There's no reason to stick with 'clockid_t' (which is integer anyway). We're
> using just a subset.
>
> #define SNDRV_RAWMIDI_MODE_FRAMING_MASK        (7<<0)
> #define SNDRV_RAWMIDI_MODE_FRAMING_SHIFT       0
> #define SNDRV_RAWMIDI_MODE_FRAMING_NONE	       (0<<0)
> #define SNDRV_RAWMIDI_MODE_FRAMING_32BYTES     (1<<0)
> #define SNDRV_RAWMIDI_MODE_CLOCK_MASK          (7<<3)
> #define SNDRV_RAWMIDI_MODE_CLOCK_SHIFT         3
> #define SNDRV_RAWMIDI_MODE_CLOCK_NONE	       (0<<3)
> #define SNDRV_RAWMIDI_MODE_CLOCK_REALTIME      (1<<3)
> #define SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC     (2<<3)
> #define SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW (3<<3)
>
> In this case, we can use 26-bits in future for extensions.

Well, for me this is mostly bikeshedding. But as long as you and Takashi 
can't agree on whether bitfields/bimasks/etc are good or bad, I'm just 
stuck between the two of you and can't actually improve Linux's 
capability to be a great pro audio OS, and that is utterly frustrating. 
I don't care if the two of you decides who's going to win this through 
this list, a conference call, a game of SuperTuxKart or thumb wrestling, 
just reach consensus somehow. Okay?

>
>> +struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream)
>> +{
>> +	struct timespec64 ts64 = {0, 0};
>> +
>> +	if (substream->framing != SNDRV_RAWMIDI_FRAMING_TSTAMP)
>> +		return ts64;
>> +	if (substream->clock_type == CLOCK_MONOTONIC_RAW)
>> +		ktime_get_raw_ts64(&ts64);
>> +	else
>> +		ktime_get_ts64(&ts64);
>> +	return ts64;
>> +}
> Missing the realtime clock type here.
>
> 					Jaroslav
>
Takashi Iwai April 19, 2021, 7:34 a.m. UTC | #6
On Mon, 19 Apr 2021 08:22:50 +0200,
David Henningsson wrote:
> 
> 
> On 2021-04-18 20:24, Jaroslav Kysela wrote:
> > Dne 18. 04. 21 v 17:12 David Henningsson napsal(a):
> >
> >> +#define SND_RAWMIDI_FRAMING_DATA_LENGTH 16
> > SNDRV_ prefix should be here.
> 
> Ack
> 
> >
> >> +
> >> +struct snd_rawmidi_framing_tstamp {
> >> +	/* For now, frame_type is always 0. Midi 2.0 is expected to add new
> >> +	 * types here. Applications are expected to skip unknown frame types.
> >> +	 */
> >> +	u8 frame_type;
> >> +	u8 length; /* number of valid bytes in data field */
> >> +	u8 reserved[2];
> >> +	u32 tv_nsec;		/* nanoseconds */
> >> +	u64 tv_sec;		/* seconds */
> >> +	u8 data[SND_RAWMIDI_FRAMING_DATA_LENGTH];
> > What about to move the fields to union (except for frame_type) like we do for
> > 'struct snd_ctl_event' in case when we need to reorganize the contents for
> > future types?
> 
> So the two degrees of freedom would be
> 
> 1) the SNDRV_RAWMIDI_MODE_FRAMING_32BYTES indicates that the frame
> size is 32 bytes and the first byte of that frame is frame_type
> 
> 2) the frame_type of every frame indicates the format of the other 31
> bytes, and an application is expected to ignore unknown frame_types,
> so we can add new frame_types in a backwards compatible way.
> 
> We'll end up with:
> 
> struct snd_rawmidi_framing_32bytes {
>     u8 frame_type;
>     union {
>         struct {
>             u8 length; /* number of valid bytes in data field */
>             u8 reserved[2];
>             u32 tv_nsec;        /* nanoseconds */
>             u64 tv_sec;        /* seconds */
>             u8 data[SNDRV_RAWMIDI_FRAMING_32BYTES_FOO_LENGTH];
>         } foo;
>         u8 reserved[31];
>     } data;
> };
> 
> ...except I don't know what we should replace foo with. We can't call
> it "midi1" or "type0" or such because many different frame_types might
> share the same interior format.

I thought of the use of union, but concluded that it doesn't give any
good benefit.  Practically seen, defining two structs would be far
easier, and if you want to code something in user-space for another
new frame format, you would just use the new struct as is, as long as
the size fits correctly.

If we think of the future-proof compatibility, you may align the
struct to a TLV pattern; the struct already shows a kind of that. But
it would work only if the kernel read side allows the partial read,
though.  So maybe it's not the case.

> >> +};
> >> +
> >>   struct snd_rawmidi_params {
> >>   	int stream;
> >>   	size_t buffer_size;		/* queue size in bytes */
> >>   	size_t avail_min;		/* minimum avail bytes for wakeup */
> >>   	unsigned int no_active_sensing: 1; /* do not send active sensing byte in close() */
> >> -	unsigned char reserved[16];	/* reserved for future use */
> >> +	unsigned char framing;		/* For input data only, frame incoming data */
> >> +	unsigned char clock_type;	/* Type of clock to use for framing, same as clockid_t */
> >> +	unsigned char reserved[14];	/* reserved for future use */
> > As I noted, I would prefer to add 'unsigned int mode;' and define
> > SNDRV_RAWMID_MODE_XXX bit flags and groups with framing and clock_type groups.
> > There's no reason to stick with 'clockid_t' (which is integer anyway). We're
> > using just a subset.
> >
> > #define SNDRV_RAWMIDI_MODE_FRAMING_MASK        (7<<0)
> > #define SNDRV_RAWMIDI_MODE_FRAMING_SHIFT       0
> > #define SNDRV_RAWMIDI_MODE_FRAMING_NONE	       (0<<0)
> > #define SNDRV_RAWMIDI_MODE_FRAMING_32BYTES     (1<<0)
> > #define SNDRV_RAWMIDI_MODE_CLOCK_MASK          (7<<3)
> > #define SNDRV_RAWMIDI_MODE_CLOCK_SHIFT         3
> > #define SNDRV_RAWMIDI_MODE_CLOCK_NONE	       (0<<3)
> > #define SNDRV_RAWMIDI_MODE_CLOCK_REALTIME      (1<<3)
> > #define SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC     (2<<3)
> > #define SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW (3<<3)
> >
> > In this case, we can use 26-bits in future for extensions.
> 
> Well, for me this is mostly bikeshedding. But as long as you and
> Takashi can't agree on whether bitfields/bimasks/etc are good or bad,
> I'm just stuck between the two of you and can't actually improve
> Linux's capability to be a great pro audio OS, and that is utterly
> frustrating. I don't care if the two of you decides who's going to win
> this through this list, a conference call, a game of SuperTuxKart or
> thumb wrestling, just reach consensus somehow. Okay?

Oh, don't get me wrong, I have no objection to the bit flags at all.
My objection was against the use of C language bit-fields
(e.g. unsigned int foo:5) as Jaroslav suggested in his earlier reply.
That's not portable, hence unsuitable for ioctl structs.  OTOH, the
explicit bit flags are very common in ABI definitions.

And I have no strong opinion on the flag definitions.  I find it OK to
keep two uchar fields (that are mostly enough for near future
extensions), but having a 32bit full bit flag would be of course OK
from ABI design POV (and one less code in the compat function).

That said, there is no disagreement about the flag definition at all.
You can pick up what you believe the best.


BTW, regarding the portability: it'd be safer to put __packed
attribute to the newly defined frame struct.  Although it's certainly
fit in the designed size with gcc, essentially a compiler is still
free to put any padding.  Unlike the ioctl struct, the difference of
the access size couldn't be caught easily for read syscall (as we
often allow partial reads).


thanks,

Takashi
Jaroslav Kysela April 19, 2021, 8:14 a.m. UTC | #7
Dne 19. 04. 21 v 9:34 Takashi Iwai napsal(a):
> On Mon, 19 Apr 2021 08:22:50 +0200,
> David Henningsson wrote:
>>
>>
>> On 2021-04-18 20:24, Jaroslav Kysela wrote:
>>> Dne 18. 04. 21 v 17:12 David Henningsson napsal(a):
>>>
>>>> +#define SND_RAWMIDI_FRAMING_DATA_LENGTH 16
>>> SNDRV_ prefix should be here.
>>
>> Ack
>>
>>>
>>>> +
>>>> +struct snd_rawmidi_framing_tstamp {
>>>> +	/* For now, frame_type is always 0. Midi 2.0 is expected to add new
>>>> +	 * types here. Applications are expected to skip unknown frame types.
>>>> +	 */
>>>> +	u8 frame_type;
>>>> +	u8 length; /* number of valid bytes in data field */
>>>> +	u8 reserved[2];
>>>> +	u32 tv_nsec;		/* nanoseconds */
>>>> +	u64 tv_sec;		/* seconds */
>>>> +	u8 data[SND_RAWMIDI_FRAMING_DATA_LENGTH];
>>> What about to move the fields to union (except for frame_type) like we do for
>>> 'struct snd_ctl_event' in case when we need to reorganize the contents for
>>> future types?
>>
>> So the two degrees of freedom would be
>>
>> 1) the SNDRV_RAWMIDI_MODE_FRAMING_32BYTES indicates that the frame
>> size is 32 bytes and the first byte of that frame is frame_type
>>
>> 2) the frame_type of every frame indicates the format of the other 31
>> bytes, and an application is expected to ignore unknown frame_types,
>> so we can add new frame_types in a backwards compatible way.
>>
>> We'll end up with:
>>
>> struct snd_rawmidi_framing_32bytes {
>>     u8 frame_type;
>>     union {
>>         struct {
>>             u8 length; /* number of valid bytes in data field */
>>             u8 reserved[2];
>>             u32 tv_nsec;        /* nanoseconds */
>>             u64 tv_sec;        /* seconds */
>>             u8 data[SNDRV_RAWMIDI_FRAMING_32BYTES_FOO_LENGTH];
>>         } foo;
>>         u8 reserved[31];
>>     } data;
>> };
>>
>> ...except I don't know what we should replace foo with. We can't call
>> it "midi1" or "type0" or such because many different frame_types might
>> share the same interior format.
> 
> I thought of the use of union, but concluded that it doesn't give any
> good benefit.  Practically seen, defining two structs would be far
> easier, and if you want to code something in user-space for another
> new frame format, you would just use the new struct as is, as long as
> the size fits correctly.

Ok.

>>> As I noted, I would prefer to add 'unsigned int mode;' and define
>>> SNDRV_RAWMID_MODE_XXX bit flags and groups with framing and clock_type groups.
>>> There's no reason to stick with 'clockid_t' (which is integer anyway). We're
>>> using just a subset.
>>>
>>> #define SNDRV_RAWMIDI_MODE_FRAMING_MASK        (7<<0)
>>> #define SNDRV_RAWMIDI_MODE_FRAMING_SHIFT       0
>>> #define SNDRV_RAWMIDI_MODE_FRAMING_NONE	       (0<<0)
>>> #define SNDRV_RAWMIDI_MODE_FRAMING_32BYTES     (1<<0)
>>> #define SNDRV_RAWMIDI_MODE_CLOCK_MASK          (7<<3)
>>> #define SNDRV_RAWMIDI_MODE_CLOCK_SHIFT         3
>>> #define SNDRV_RAWMIDI_MODE_CLOCK_NONE	       (0<<3)
>>> #define SNDRV_RAWMIDI_MODE_CLOCK_REALTIME      (1<<3)
>>> #define SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC     (2<<3)
>>> #define SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW (3<<3)
>>>
>>> In this case, we can use 26-bits in future for extensions.
>>
>> Well, for me this is mostly bikeshedding. But as long as you and
>> Takashi can't agree on whether bitfields/bimasks/etc are good or bad,
>> I'm just stuck between the two of you and can't actually improve
>> Linux's capability to be a great pro audio OS, and that is utterly
>> frustrating. I don't care if the two of you decides who's going to win
>> this through this list, a conference call, a game of SuperTuxKart or
>> thumb wrestling, just reach consensus somehow. Okay?
> 
> Oh, don't get me wrong, I have no objection to the bit flags at all.
> My objection was against the use of C language bit-fields
> (e.g. unsigned int foo:5) as Jaroslav suggested in his earlier reply.
> That's not portable, hence unsuitable for ioctl structs.  OTOH, the
> explicit bit flags are very common in ABI definitions.
> 
> And I have no strong opinion on the flag definitions.  I find it OK to
> keep two uchar fields (that are mostly enough for near future
> extensions), but having a 32bit full bit flag would be of course OK
> from ABI design POV (and one less code in the compat function).
> 
> That said, there is no disagreement about the flag definition at all.
> You can pick up what you believe the best.

I have two reasons to pick the flags rather uchar fields (a bit non-standard
in the ALSA ioctl API):

1) save bits for future use
2) align to 4 bytes - with 2 uchar, we have 2 bytes pad

So, instead to split the 4 bytes from the reserved area, allocate the whole
32-bit word and allocate bits from it.

					Jaroslav
diff mbox series

Patch

diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h
index 334842daa904..b0057a193c31 100644
--- a/include/sound/rawmidi.h
+++ b/include/sound/rawmidi.h
@@ -81,6 +81,8 @@  struct snd_rawmidi_substream {
 	bool opened;			/* open flag */
 	bool append;			/* append flag (merge more streams) */
 	bool active_sensing;		/* send active sensing when close */
+	u8 framing;			/* whether to frame input data */
+	clockid_t clock_type;		/* clock source to use for input framing */
 	int use_count;			/* use counter (for output) */
 	size_t bytes;
 	struct snd_rawmidi *rmidi;
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 535a7229e1d9..124ac74a13e9 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -710,7 +710,7 @@  enum {
  *  Raw MIDI section - /dev/snd/midi??
  */
 
-#define SNDRV_RAWMIDI_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 1)
+#define SNDRV_RAWMIDI_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 2)
 
 enum {
 	SNDRV_RAWMIDI_STREAM_OUTPUT = 0,
@@ -736,12 +736,34 @@  struct snd_rawmidi_info {
 	unsigned char reserved[64];	/* reserved for future use */
 };
 
+enum {
+	SNDRV_RAWMIDI_FRAMING_NONE = 0,
+	SNDRV_RAWMIDI_FRAMING_TSTAMP,
+	SNDRV_RAWMIDI_FRAMING_LAST = SNDRV_RAWMIDI_FRAMING_TSTAMP,
+};
+
+#define SND_RAWMIDI_FRAMING_DATA_LENGTH 16
+
+struct snd_rawmidi_framing_tstamp {
+	/* For now, frame_type is always 0. Midi 2.0 is expected to add new
+	 * types here. Applications are expected to skip unknown frame types.
+	 */
+	u8 frame_type;
+	u8 length; /* number of valid bytes in data field */
+	u8 reserved[2];
+	u32 tv_nsec;		/* nanoseconds */
+	u64 tv_sec;		/* seconds */
+	u8 data[SND_RAWMIDI_FRAMING_DATA_LENGTH];
+};
+
 struct snd_rawmidi_params {
 	int stream;
 	size_t buffer_size;		/* queue size in bytes */
 	size_t avail_min;		/* minimum avail bytes for wakeup */
 	unsigned int no_active_sensing: 1; /* do not send active sensing byte in close() */
-	unsigned char reserved[16];	/* reserved for future use */
+	unsigned char framing;		/* For input data only, frame incoming data */
+	unsigned char clock_type;	/* Type of clock to use for framing, same as clockid_t */
+	unsigned char reserved[14];	/* reserved for future use */
 };
 
 #ifndef __KERNEL__
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index aca00af93afe..c3a4940a919d 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -683,6 +683,8 @@  static int resize_runtime_buffer(struct snd_rawmidi_runtime *runtime,
 
 	if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L)
 		return -EINVAL;
+	if (params->framing == SNDRV_RAWMIDI_FRAMING_TSTAMP && (params->buffer_size & 0x1f) != 0)
+		return -EINVAL;
 	if (params->avail_min < 1 || params->avail_min > params->buffer_size)
 		return -EINVAL;
 	if (params->buffer_size != runtime->buffer_size) {
@@ -720,7 +722,16 @@  EXPORT_SYMBOL(snd_rawmidi_output_params);
 int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
 			     struct snd_rawmidi_params *params)
 {
+	if (params->framing) {
+		if (params->framing > SNDRV_RAWMIDI_FRAMING_LAST)
+			return -EINVAL;
+		/* framing requires a valid clock type */
+		if (params->clock_type != CLOCK_MONOTONIC_RAW && params->clock_type != CLOCK_MONOTONIC)
+			return -EINVAL;
+	}
 	snd_rawmidi_drain_input(substream);
+	substream->framing = params->framing;
+	substream->clock_type = params->clock_type;
 	return resize_runtime_buffer(substream->runtime, params, true);
 }
 EXPORT_SYMBOL(snd_rawmidi_input_params);
@@ -963,6 +974,56 @@  static int snd_rawmidi_control_ioctl(struct snd_card *card,
 	return -ENOIOCTLCMD;
 }
 
+static int receive_with_tstamp_framing(struct snd_rawmidi_substream *substream,
+			const unsigned char *buffer, int src_count, const struct timespec64 *tstamp)
+{
+	struct snd_rawmidi_runtime *runtime = substream->runtime;
+	struct snd_rawmidi_framing_tstamp *dest_ptr;
+	struct snd_rawmidi_framing_tstamp frame = { .tv_sec = tstamp->tv_sec, .tv_nsec = tstamp->tv_nsec };
+	int dest_frames = 0;
+	int frame_size = sizeof(struct snd_rawmidi_framing_tstamp);
+
+	BUILD_BUG_ON(frame_size != 0x20);
+	if (snd_BUG_ON((runtime->hw_ptr & 0x1f) != 0))
+		return -EINVAL;
+
+	while (src_count > 0) {
+		if ((int)(runtime->buffer_size - runtime->avail) < frame_size) {
+			runtime->xruns += src_count;
+			break;
+		}
+		if (src_count >= SND_RAWMIDI_FRAMING_DATA_LENGTH)
+			frame.length = SND_RAWMIDI_FRAMING_DATA_LENGTH;
+		else {
+			frame.length = src_count;
+			memset(frame.data, 0, SND_RAWMIDI_FRAMING_DATA_LENGTH);
+		}
+		memcpy(frame.data, buffer, frame.length);
+		buffer += frame.length;
+		src_count -= frame.length;
+		dest_ptr = (struct snd_rawmidi_framing_tstamp *) (runtime->buffer + runtime->hw_ptr);
+		*dest_ptr = frame;
+		runtime->avail += frame_size;
+		runtime->hw_ptr += frame_size;
+		runtime->hw_ptr %= runtime->buffer_size;
+		dest_frames++;
+	}
+	return dest_frames * frame_size;
+}
+
+struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream)
+{
+	struct timespec64 ts64 = {0, 0};
+
+	if (substream->framing != SNDRV_RAWMIDI_FRAMING_TSTAMP)
+		return ts64;
+	if (substream->clock_type == CLOCK_MONOTONIC_RAW)
+		ktime_get_raw_ts64(&ts64);
+	else
+		ktime_get_ts64(&ts64);
+	return ts64;
+}
+
 /**
  * snd_rawmidi_receive - receive the input data from the device
  * @substream: the rawmidi substream
@@ -977,6 +1038,7 @@  int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
 			const unsigned char *buffer, int count)
 {
 	unsigned long flags;
+	struct timespec64 ts64 = get_framing_tstamp(substream);
 	int result = 0, count1;
 	struct snd_rawmidi_runtime *runtime = substream->runtime;
 
@@ -987,8 +1049,11 @@  int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
 			  "snd_rawmidi_receive: input is not active!!!\n");
 		return -EINVAL;
 	}
+
 	spin_lock_irqsave(&runtime->lock, flags);
-	if (count == 1) {	/* special case, faster code */
+	if (substream->framing == SNDRV_RAWMIDI_FRAMING_TSTAMP) {
+		result = receive_with_tstamp_framing(substream, buffer, count, &ts64);
+	} else if (count == 1) {	/* special case, faster code */
 		substream->bytes++;
 		if (runtime->avail < runtime->buffer_size) {
 			runtime->buffer[runtime->hw_ptr++] = buffer[0];
@@ -1596,6 +1661,12 @@  static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
 					    "  Avail        : %lu\n"
 					    "  Overruns     : %lu\n",
 					    buffer_size, avail, xruns);
+				if (substream->framing == SNDRV_RAWMIDI_FRAMING_TSTAMP) {
+					snd_iprintf(buffer,
+					    "  Framing      : tstamp\n"
+					    "  Clock type   : %s\n",
+					    substream->clock_type == CLOCK_MONOTONIC_RAW ? "monotonic raw" : "monotonic");
+				}
 			}
 		}
 	}
diff --git a/sound/core/rawmidi_compat.c b/sound/core/rawmidi_compat.c
index 7397130976d0..2603d2dd8abb 100644
--- a/sound/core/rawmidi_compat.c
+++ b/sound/core/rawmidi_compat.c
@@ -13,7 +13,9 @@  struct snd_rawmidi_params32 {
 	u32 buffer_size;
 	u32 avail_min;
 	unsigned int no_active_sensing; /* avoid bit-field */
-	unsigned char reserved[16];
+	unsigned char framing;
+	unsigned char clock_type;
+	unsigned char reserved[14];
 } __attribute__((packed));
 
 static int snd_rawmidi_ioctl_params_compat(struct snd_rawmidi_file *rfile,
@@ -25,6 +27,8 @@  static int snd_rawmidi_ioctl_params_compat(struct snd_rawmidi_file *rfile,
 	if (get_user(params.stream, &src->stream) ||
 	    get_user(params.buffer_size, &src->buffer_size) ||
 	    get_user(params.avail_min, &src->avail_min) ||
+	    get_user(params.framing, &src->framing) ||
+	    get_user(params.clock_type, &src->clock_type) ||
 	    get_user(val, &src->no_active_sensing))
 		return -EFAULT;
 	params.no_active_sensing = val;