]> git.alsa-project.org Git - alsa-lib.git/commitdiff
rawmidi: Add rawmidi framing API
authorDavid Henningsson <coding@diwic.se>
Tue, 17 Aug 2021 14:21:12 +0000 (16:21 +0200)
committerTakashi Iwai <tiwai@suse.de>
Fri, 20 Aug 2021 06:46:26 +0000 (08:46 +0200)
Optionally, incoming rawmidi bytes can be put inside a frame of type
snd_rawmidi_framing_tstamp_t.
The main current benefit is that can enable in-kernel timestamping of
incoming bytes, and that timestamp is likely to be more precise than
what userspace can offer.

Tstamp type framing requires a kernel >= 5.14 and a buffer size that
is a multiple of sizeof(snd_rawmidi_framing_tstamp_t). It is only
available on input streams.

Signed-off-by: David Henningsson <coding@diwic.se>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
include/rawmidi.h
include/sound/uapi/asound.h
src/rawmidi/rawmidi.c
src/rawmidi/rawmidi_hw.c
src/rawmidi/rawmidi_local.h

index d88c932c15c04343e270263de094bd7e750e47f9..83210181a51b6d963f638c8242799dbb007e0846 100644 (file)
@@ -79,6 +79,39 @@ typedef enum _snd_rawmidi_type {
        SND_RAWMIDI_TYPE_VIRTUAL
 } snd_rawmidi_type_t;
 
+/** Type of clock used with rawmidi tstamp framing */
+typedef enum _snd_rawmidi_clock {
+       SND_RAWMIDI_CLOCK_NONE = 0,
+       SND_RAWMIDI_CLOCK_REALTIME = 1,
+       SND_RAWMIDI_CLOCK_MONOTONIC = 2,
+       SND_RAWMIDI_CLOCK_MONOTONIC_RAW = 3,
+} snd_rawmidi_clock_t;
+
+/** Enable or disable rawmidi framing */
+typedef enum _snd_rawmidi_framing {
+       SND_RAWMIDI_FRAMING_NONE = 0,
+       SND_RAWMIDI_FRAMING_TSTAMP = 1,
+} snd_rawmidi_framing_t;
+
+#define SND_RAWMIDI_FRAME_TYPE_DEFAULT 0
+
+#define SND_RAWMIDI_FRAMING_DATA_LENGTH 16
+
+/** Incoming RawMidi bytes is put inside this container if tstamp type framing is enabled. */
+typedef struct _snd_rawmidi_framing_tstamp {
+       /**
+        * For now, frame_type is always SND_RAWMIDI_FRAME_TYPE_DEFAULT.
+        * 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];
+} snd_rawmidi_framing_tstamp_t;
+
 int snd_rawmidi_open(snd_rawmidi_t **in_rmidi, snd_rawmidi_t **out_rmidi,
                     const char *name, int mode);
 int snd_rawmidi_open_lconf(snd_rawmidi_t **in_rmidi, snd_rawmidi_t **out_rmidi,
@@ -126,6 +159,11 @@ int snd_rawmidi_params_set_avail_min(snd_rawmidi_t *rmidi, snd_rawmidi_params_t
 size_t snd_rawmidi_params_get_avail_min(const snd_rawmidi_params_t *params);
 int snd_rawmidi_params_set_no_active_sensing(snd_rawmidi_t *rmidi, snd_rawmidi_params_t *params, int val);
 int snd_rawmidi_params_get_no_active_sensing(const snd_rawmidi_params_t *params);
+int snd_rawmidi_params_set_framing_type(const snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *params, snd_rawmidi_framing_t val);
+snd_rawmidi_framing_t snd_rawmidi_params_get_framing_type(const snd_rawmidi_params_t *params);
+int snd_rawmidi_params_set_clock_type(const snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *params, snd_rawmidi_clock_t val);
+snd_rawmidi_clock_t snd_rawmidi_params_get_clock_type(const snd_rawmidi_params_t *params);
+
 int snd_rawmidi_params(snd_rawmidi_t *rmidi, snd_rawmidi_params_t * params);
 int snd_rawmidi_params_current(snd_rawmidi_t *rmidi, snd_rawmidi_params_t *params);
 size_t snd_rawmidi_status_sizeof(void);
index ec610c27041148e6700fa6d7d4eded432ee3ef0b..9fe3943f5fbb754de52031466114c53d60238528 100644 (file)
@@ -702,7 +702,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,
@@ -728,12 +728,38 @@ struct snd_rawmidi_info {
        unsigned char reserved[64];     /* reserved for future use */
 };
 
+#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_TSTAMP      (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)
+
+#define SNDRV_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[SNDRV_RAWMIDI_FRAMING_DATA_LENGTH];
+} __packed;
+
 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 int mode;              /* For input data only, frame incoming data */
+       unsigned char reserved[12];     /* reserved for future use */
 };
 
 struct snd_rawmidi_status {
index 55f4482104009cca71a7a0634256fb6c1f7a099c..ae4b260066c445e1bdc56c2ef22117f3072cc8e0 100644 (file)
@@ -118,6 +118,15 @@ hw:soundwave,1,2
 hw:DEV=1,CARD=soundwave,SUBDEV=2
 \endcode
 
+\section rawmidi_framing Framing of rawmidi data
+
+Optionally, incoming rawmidi bytes can be put inside a frame of type snd_rawmidi_framing_tstamp_t.
+The main current benefit is that can enable in-kernel timestamping of incoming bytes, and that
+timestamp is likely to be more precise than what userspace can offer.
+
+Tstamp type framing requires a kernel >= 5.14 and a buffer size that is a multiple of
+sizeof(snd_rawmidi_framing_tstamp_t). It is only available on input streams.
+
 \section rawmidi_examples Examples
 
 The full featured examples with cross-links:
@@ -154,6 +163,7 @@ static int snd_rawmidi_params_default(snd_rawmidi_t *rawmidi, snd_rawmidi_params
        params->buffer_size = page_size();
        params->avail_min = 1;
        params->no_active_sensing = 1;
+       params->mode = 0;
        return 0;
 }
 
@@ -811,6 +821,77 @@ int snd_rawmidi_params_get_no_active_sensing(const snd_rawmidi_params_t *params)
        return params->no_active_sensing;
 }
 
+/**
+ * \brief enable or disable rawmidi framing
+ * \param rawmidi RawMidi handle
+ * \param params pointer to snd_rawmidi_params_t structure
+ * \param val type of rawmidi framing
+ * \return 0 on success, otherwise a negative error code.
+ *
+ * Notable error codes:
+ * -EINVAL - "val" is invalid
+ * -ENOTSUP - Kernel is too old to support framing.
+ *
+ */
+int snd_rawmidi_params_set_framing_type(const snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *params, snd_rawmidi_framing_t val)
+{
+       assert(rawmidi && params);
+       if (val > SNDRV_RAWMIDI_MODE_FRAMING_MASK >> SNDRV_RAWMIDI_MODE_FRAMING_SHIFT)
+               return -EINVAL;
+       if (val != SNDRV_RAWMIDI_MODE_FRAMING_NONE &&
+               (rawmidi->version < SNDRV_PROTOCOL_VERSION(2, 0, 2) || rawmidi->stream != SND_RAWMIDI_STREAM_INPUT))
+               return -ENOTSUP;
+       params->mode = (params->mode & ~SNDRV_RAWMIDI_MODE_FRAMING_MASK) + (val << SNDRV_RAWMIDI_MODE_FRAMING_SHIFT);
+       return 0;
+}
+
+/**
+ * \brief get current framing type
+ * \param params pointer to snd_rawmidi_params_t structure
+ * \return the current type (0 = no framing, 1 = tstamp type framing)
+ */
+snd_rawmidi_framing_t snd_rawmidi_params_get_framing_type(const snd_rawmidi_params_t *params)
+{
+       assert(params);
+       return (params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK) >> SNDRV_RAWMIDI_MODE_FRAMING_SHIFT;
+}
+
+/**
+ * \brief sets clock type for tstamp type framing
+ * \param rawmidi RawMidi handle
+ * \param params pointer to snd_rawmidi_params_t structure
+ * \param val one of the SND_RAWMIDI_CLOCK_* constants
+ * \return 0 on success, otherwise a negative error code.
+ *
+ * Notable error codes:
+ * -EINVAL - "val" is invalid
+ * -ENOTSUP - Kernel is too old to support framing.
+ *
+ */
+int snd_rawmidi_params_set_clock_type(const snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *params, snd_rawmidi_clock_t val)
+{
+       assert(rawmidi && params);
+       if (val > SNDRV_RAWMIDI_MODE_CLOCK_MASK >> SNDRV_RAWMIDI_MODE_CLOCK_SHIFT)
+               return -EINVAL;
+       if (val != SNDRV_RAWMIDI_MODE_CLOCK_NONE &&
+               (rawmidi->version < SNDRV_PROTOCOL_VERSION(2, 0, 2) || rawmidi->stream != SND_RAWMIDI_STREAM_INPUT))
+               return -ENOTSUP;
+       params->mode = (params->mode & ~SNDRV_RAWMIDI_MODE_CLOCK_MASK) + (val << SNDRV_RAWMIDI_MODE_CLOCK_SHIFT);
+       return 0;
+}
+
+/**
+ * \brief get current clock type (for tstamp type framing)
+ * \param params pointer to snd_rawmidi_params_t structure
+ * \return the current clock type (one of the SND_RAWMIDI_CLOCK_* constants)
+ */
+snd_rawmidi_clock_t snd_rawmidi_params_get_clock_type(const snd_rawmidi_params_t *params)
+{
+       assert(params);
+       return (params->mode & SNDRV_RAWMIDI_MODE_CLOCK_MASK) >> SNDRV_RAWMIDI_MODE_CLOCK_SHIFT;
+}
+
+
 /**
  * \brief set parameters about rawmidi stream
  * \param rawmidi RawMidi handle
@@ -828,6 +909,7 @@ int snd_rawmidi_params(snd_rawmidi_t *rawmidi, snd_rawmidi_params_t * params)
        rawmidi->buffer_size = params->buffer_size;
        rawmidi->avail_min = params->avail_min;
        rawmidi->no_active_sensing = params->no_active_sensing;
+       rawmidi->params_mode = rawmidi->version < SNDRV_PROTOCOL_VERSION(2, 0, 2) ? 0 : params->mode;
        return 0;
 }
 
@@ -844,6 +926,7 @@ int snd_rawmidi_params_current(snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *par
        params->buffer_size = rawmidi->buffer_size;
        params->avail_min = rawmidi->avail_min;
        params->no_active_sensing = rawmidi->no_active_sensing;
+       params->mode = rawmidi->params_mode;
        return 0;
 }
 
index 99927d75e5ccc84acd239a25ffe628cbbef37670..6bfbc1f3681378e3b7ad233a4c12f162c2238f3a 100644 (file)
@@ -285,6 +285,7 @@ int snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
                rmidi->poll_fd = fd;
                rmidi->ops = &snd_rawmidi_hw_ops;
                rmidi->private_data = hw;
+               rmidi->version = ver;
                hw->open++;
                *inputp = rmidi;
        }
@@ -300,6 +301,7 @@ int snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
                rmidi->poll_fd = fd;
                rmidi->ops = &snd_rawmidi_hw_ops;
                rmidi->private_data = hw;
+               rmidi->version = ver;
                hw->open++;
                *outputp = rmidi;
        }
index 721e1ec9deec04e36ee14f18ebfd407487d7ff6e..c4068d7c8fb213302c6451fdda8f9d5910d337fc 100644 (file)
@@ -42,12 +42,14 @@ struct _snd_rawmidi {
        snd_rawmidi_type_t type;
        snd_rawmidi_stream_t stream;
        int mode;
+       int version;
        int poll_fd;
        const snd_rawmidi_ops_t *ops;
        void *private_data;
        size_t buffer_size;
        size_t avail_min;
        unsigned int no_active_sensing: 1;
+       int params_mode;
 };
 
 int snd_rawmidi_hw_open(snd_rawmidi_t **input, snd_rawmidi_t **output,