All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Henningsson <coding@diwic.se>
To: tiwai@suse.de, alsa-devel@alsa-project.org, perex@perex.cz
Cc: David Henningsson <coding@diwic.se>
Subject: [PATCH v3 1/2] Add rawmidi framing API
Date: Tue, 17 Aug 2021 16:21:12 +0200	[thread overview]
Message-ID: <20210817142113.14750-2-coding@diwic.se> (raw)
In-Reply-To: <20210817142113.14750-1-coding@diwic.se>

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>
---
 include/rawmidi.h           | 38 +++++++++++++++++
 include/sound/uapi/asound.h | 30 +++++++++++++-
 src/rawmidi/rawmidi.c       | 83 +++++++++++++++++++++++++++++++++++++
 src/rawmidi/rawmidi_hw.c    |  2 +
 src/rawmidi/rawmidi_local.h |  2 +
 5 files changed, 153 insertions(+), 2 deletions(-)

diff --git a/include/rawmidi.h b/include/rawmidi.h
index d88c932c..83210181 100644
--- a/include/rawmidi.h
+++ b/include/rawmidi.h
@@ -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);
diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h
index ec610c27..9fe3943f 100644
--- a/include/sound/uapi/asound.h
+++ b/include/sound/uapi/asound.h
@@ -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 {
diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c
index 55f44821..ae4b2600 100644
--- a/src/rawmidi/rawmidi.c
+++ b/src/rawmidi/rawmidi.c
@@ -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;
 }
 
diff --git a/src/rawmidi/rawmidi_hw.c b/src/rawmidi/rawmidi_hw.c
index 99927d75..6bfbc1f3 100644
--- a/src/rawmidi/rawmidi_hw.c
+++ b/src/rawmidi/rawmidi_hw.c
@@ -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;
 	}
diff --git a/src/rawmidi/rawmidi_local.h b/src/rawmidi/rawmidi_local.h
index 721e1ec9..c4068d7c 100644
--- a/src/rawmidi/rawmidi_local.h
+++ b/src/rawmidi/rawmidi_local.h
@@ -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,
-- 
2.25.1


  reply	other threads:[~2021-08-17 14:23 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-17 14:21 [PATCH v3 0/2] alsa-lib patches for rawmidi framing David Henningsson
2021-08-17 14:21 ` David Henningsson [this message]
2021-08-17 14:21 ` [PATCH v3 2/2] Add test for rawmidi framing API David Henningsson
2021-08-20  6:49 ` [PATCH v3 0/2] alsa-lib patches for rawmidi framing Takashi Iwai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210817142113.14750-2-coding@diwic.se \
    --to=coding@diwic.se \
    --cc=alsa-devel@alsa-project.org \
    --cc=perex@perex.cz \
    --cc=tiwai@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.