Squashed 'third_party/rawrtc/rawrtc-data-channel/' content from commit 7b1b8d57c

Change-Id: I84850720e2b51961981d55f67238f4d282314fff
git-subtree-dir: third_party/rawrtc/rawrtc-data-channel
git-subtree-split: 7b1b8d57c6d07da18cc0de8bbca8cc5e8bd06eae
diff --git a/src/data_channel/attributes.c b/src/data_channel/attributes.c
new file mode 100644
index 0000000..31abf35
--- /dev/null
+++ b/src/data_channel/attributes.c
@@ -0,0 +1,351 @@
+#include "channel.h"
+#include "../data_transport/transport.h"
+#include <rawrtcdc/config.h>
+#include <rawrtcdc/data_channel.h>
+#include <rawrtcdc/data_channel_parameters.h>
+#include <rawrtcc/code.h>
+#include <re.h>
+
+#define DEBUG_MODULE "data-channel"
+//#define RAWRTC_DEBUG_MODULE_LEVEL 7 // Note: Uncomment this to debug this module only
+#include <rawrtcc/debug.h>
+
+/*
+ * Get the current state of the data channel.
+ */
+enum rawrtc_code rawrtc_data_channel_get_state(
+    enum rawrtc_data_channel_state* const statep,  // de-referenced
+    struct rawrtc_data_channel* const channel) {
+    // Check arguments
+    if (!statep || !channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Set state & done
+    *statep = channel->state;
+    return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Get the currently buffered amount (bytes) of outgoing application
+ * data of the data channel.
+ */
+enum rawrtc_code rawrtc_data_channel_get_buffered_amount(
+    uint64_t* const buffered_amountp,  // de-referenced
+    struct rawrtc_data_channel* const channel) {
+    // Check arguments
+    if (!buffered_amountp || !channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // TODO: Implement this!
+    return RAWRTC_CODE_NOT_IMPLEMENTED;
+}
+
+/*
+ * Set the data channel's buffered amount (bytes) low threshold for
+ * outgoing application data.
+ */
+enum rawrtc_code rawrtc_data_channel_set_buffered_amount_low_threshold(
+    struct rawrtc_data_channel* const channel, uint64_t const buffered_amount_low_threshold) {
+    // Check arguments
+    if (!channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // TODO: Implement this!
+    (void) buffered_amount_low_threshold;
+    return RAWRTC_CODE_NOT_IMPLEMENTED;
+}
+
+/*
+ * Get the data channel's buffered amount (bytes) low threshold for
+ * outgoing application data.
+ */
+enum rawrtc_code rawrtc_data_channel_get_buffered_amount_low_threshold(
+    uint64_t* const buffered_amount_low_thresholdp,  // de-referenced
+    struct rawrtc_data_channel* const channel) {
+    // Check arguments
+    if (!buffered_amount_low_thresholdp || !channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // TODO: Implement this!
+    return RAWRTC_CODE_NOT_IMPLEMENTED;
+}
+
+/*
+ * Unset the handler argument and all handlers of the data channel.
+ */
+enum rawrtc_code rawrtc_data_channel_unset_handlers(struct rawrtc_data_channel* const channel) {
+    // Check arguments
+    if (!channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Unset handler argument
+    channel->arg = NULL;
+
+    // Unset all handlers
+    channel->message_handler = NULL;
+    channel->close_handler = NULL;
+    channel->error_handler = NULL;
+    channel->buffered_amount_low_handler = NULL;
+    channel->open_handler = NULL;
+
+    // Done
+    return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Get the data channel's parameters.
+ * `*parametersp` must be unreferenced.
+ */
+enum rawrtc_code rawrtc_data_channel_get_parameters(
+    struct rawrtc_data_channel_parameters** const parametersp,  // de-referenced
+    struct rawrtc_data_channel* const channel) {
+    // Check arguments
+    if (!parametersp || !channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Set pointer & done
+    *parametersp = mem_ref(channel->parameters);
+    return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Enable or disable streamed delivery.
+ *
+ * Note: In case an incoming message is currently pending (there are
+ *       queued chunks in the internal reassembly buffer), this will
+ *       fail with a *still in use* error.
+ */
+enum rawrtc_code rawrtc_data_channel_set_streaming(
+    struct rawrtc_data_channel* const channel, bool const on) {
+    enum rawrtc_code error;
+
+    // Check arguments
+    if (!channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Check state
+    if (channel->state == RAWRTC_DATA_CHANNEL_STATE_CLOSING ||
+        channel->state == RAWRTC_DATA_CHANNEL_STATE_CLOSED) {
+        return RAWRTC_CODE_INVALID_STATE;
+    }
+
+    // Does anything change?
+    if ((on && channel->flags & RAWRTC_DATA_CHANNEL_FLAGS_STREAMED) ||
+        (!on && !(channel->flags & RAWRTC_DATA_CHANNEL_FLAGS_STREAMED))) {
+        return RAWRTC_CODE_SUCCESS;
+    }
+
+    // Let the transport know we want to enable/disable streaming
+    error = channel->transport->channel_set_streaming(channel, on);
+    if (error) {
+        return error;
+    }
+
+    // Enable/disable streaming & done
+    if (on) {
+        channel->flags |= RAWRTC_DATA_CHANNEL_FLAGS_STREAMED;
+        DEBUG_PRINTF("Enabled streaming mode\n");
+    } else {
+        channel->flags &= ~RAWRTC_DATA_CHANNEL_FLAGS_STREAMED;
+        DEBUG_PRINTF("Disabled streaming mode\n");
+    }
+    return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Set the data channel's open handler.
+ */
+enum rawrtc_code rawrtc_data_channel_set_open_handler(
+    struct rawrtc_data_channel* const channel,
+    rawrtc_data_channel_open_handler const open_handler  // nullable
+) {
+    // Check arguments
+    if (!channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Set open handler & done
+    channel->open_handler = open_handler;
+    return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Get the data channel's open handler.
+ * Returns `RAWRTC_CODE_NO_VALUE` in case no handler has been set.
+ */
+enum rawrtc_code rawrtc_data_channel_get_open_handler(
+    rawrtc_data_channel_open_handler* const open_handlerp,  // de-referenced
+    struct rawrtc_data_channel* const channel) {
+    // Check arguments
+    if (!open_handlerp || !channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Get open handler (if any)
+    if (channel->open_handler) {
+        *open_handlerp = channel->open_handler;
+        return RAWRTC_CODE_SUCCESS;
+    } else {
+        return RAWRTC_CODE_NO_VALUE;
+    }
+}
+
+/*
+ * Set the data channel's buffered amount low handler.
+ */
+enum rawrtc_code rawrtc_data_channel_set_buffered_amount_low_handler(
+    struct rawrtc_data_channel* const channel,
+    rawrtc_data_channel_buffered_amount_low_handler const buffered_amount_low_handler  // nullable
+) {
+    // Check arguments
+    if (!channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Set buffered amount low handler & done
+    channel->buffered_amount_low_handler = buffered_amount_low_handler;
+    return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Get the data channel's buffered amount low handler.
+ * Returns `RAWRTC_CODE_NO_VALUE` in case no handler has been set.
+ */
+enum rawrtc_code rawrtc_data_channel_get_buffered_amount_low_handler(
+    rawrtc_data_channel_buffered_amount_low_handler* const
+        buffered_amount_low_handlerp,  // de-referenced
+    struct rawrtc_data_channel* const channel) {
+    // Check arguments
+    if (!buffered_amount_low_handlerp || !channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Get buffered amount low handler (if any)
+    if (channel->buffered_amount_low_handler) {
+        *buffered_amount_low_handlerp = channel->buffered_amount_low_handler;
+        return RAWRTC_CODE_SUCCESS;
+    } else {
+        return RAWRTC_CODE_NO_VALUE;
+    }
+}
+
+/*
+ * Set the data channel's error handler.
+ */
+enum rawrtc_code rawrtc_data_channel_set_error_handler(
+    struct rawrtc_data_channel* const channel,
+    rawrtc_data_channel_error_handler const error_handler  // nullable
+) {
+    // Check arguments
+    if (!channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Set error handler & done
+    channel->error_handler = error_handler;
+    return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Get the data channel's error handler.
+ * Returns `RAWRTC_CODE_NO_VALUE` in case no handler has been set.
+ */
+enum rawrtc_code rawrtc_data_channel_get_error_handler(
+    rawrtc_data_channel_error_handler* const error_handlerp,  // de-referenced
+    struct rawrtc_data_channel* const channel) {
+    // Check arguments
+    if (!error_handlerp || !channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Get error handler (if any)
+    if (channel->error_handler) {
+        *error_handlerp = channel->error_handler;
+        return RAWRTC_CODE_SUCCESS;
+    } else {
+        return RAWRTC_CODE_NO_VALUE;
+    }
+}
+
+/*
+ * Set the data channel's close handler.
+ */
+enum rawrtc_code rawrtc_data_channel_set_close_handler(
+    struct rawrtc_data_channel* const channel,
+    rawrtc_data_channel_close_handler const close_handler  // nullable
+) {
+    // Check arguments
+    if (!channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Set close handler & done
+    channel->close_handler = close_handler;
+    return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Get the data channel's close handler.
+ * Returns `RAWRTC_CODE_NO_VALUE` in case no handler has been set.
+ */
+enum rawrtc_code rawrtc_data_channel_get_close_handler(
+    rawrtc_data_channel_close_handler* const close_handlerp,  // de-referenced
+    struct rawrtc_data_channel* const channel) {
+    // Check arguments
+    if (!close_handlerp || !channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Get close handler (if any)
+    if (channel->close_handler) {
+        *close_handlerp = channel->close_handler;
+        return RAWRTC_CODE_SUCCESS;
+    } else {
+        return RAWRTC_CODE_NO_VALUE;
+    }
+}
+
+/*
+ * Set the data channel's message handler.
+ */
+enum rawrtc_code rawrtc_data_channel_set_message_handler(
+    struct rawrtc_data_channel* const channel,
+    rawrtc_data_channel_message_handler const message_handler  // nullable
+) {
+    // Check arguments
+    if (!channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Set message handler & done
+    channel->message_handler = message_handler;
+    return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Get the data channel's message handler.
+ * Returns `RAWRTC_CODE_NO_VALUE` in case no handler has been set.
+ */
+enum rawrtc_code rawrtc_data_channel_get_message_handler(
+    rawrtc_data_channel_message_handler* const message_handlerp,  // de-referenced
+    struct rawrtc_data_channel* const channel) {
+    // Check arguments
+    if (!message_handlerp || !channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Get message handler (if any)
+    if (channel->message_handler) {
+        *message_handlerp = channel->message_handler;
+        return RAWRTC_CODE_SUCCESS;
+    } else {
+        return RAWRTC_CODE_NO_VALUE;
+    }
+}
diff --git a/src/data_channel/channel.c b/src/data_channel/channel.c
new file mode 100644
index 0000000..e134430
--- /dev/null
+++ b/src/data_channel/channel.c
@@ -0,0 +1,251 @@
+#include "channel.h"
+#include "../data_channel_parameters/parameters.h"
+#include "../data_transport/transport.h"
+#include <rawrtcdc/config.h>
+#include <rawrtcdc/data_channel.h>
+#include <rawrtcc/code.h>
+#include <re.h>
+
+#define DEBUG_MODULE "data-channel"
+//#define RAWRTC_DEBUG_MODULE_LEVEL 7 // Note: Uncomment this to debug this module only
+#include <rawrtcc/debug.h>
+
+/*
+ * Change the state of the data channel.
+ * Will call the corresponding handler.
+ * Caller MUST ensure that the same state is not set twice.
+ */
+void rawrtc_data_channel_set_state(
+    struct rawrtc_data_channel* const channel,  // not checked
+    enum rawrtc_data_channel_state const state) {
+    // Set state
+    // Note: Keep this here as it will prevent infinite recursion during closing/destroying
+    channel->state = state;
+    DEBUG_PRINTF(
+        "Data channel '%s' state changed to %s\n",
+        channel->parameters->label ? channel->parameters->label : "n/a",
+        rawrtc_data_channel_state_to_name(state));
+
+    // TODO: Clear options flag?
+
+    // Call transport handler (if any) and user application handler
+    switch (state) {
+        case RAWRTC_DATA_CHANNEL_STATE_OPEN:
+            // Call handler
+            if (channel->open_handler) {
+                channel->open_handler(channel->arg);
+            }
+            break;
+
+        case RAWRTC_DATA_CHANNEL_STATE_CLOSING:
+            // Nothing to do.
+            break;
+
+        case RAWRTC_DATA_CHANNEL_STATE_CLOSED:
+            // Call handler
+            if (channel->close_handler) {
+                channel->close_handler(channel->arg);
+            }
+            break;
+        default:
+            break;
+    }
+}
+
+/*
+ * Destructor for an existing data channel.
+ */
+static void rawrtc_data_channel_destroy(void* arg) {
+    struct rawrtc_data_channel* const channel = arg;
+
+    // Unset all handlers
+    rawrtc_data_channel_unset_handlers(channel);
+
+    // Close channel
+    // Note: The function will ensure that the channel is not closed before it's initialised
+    rawrtc_data_channel_close(channel);
+
+    // Un-reference
+    mem_deref(channel->transport);
+    mem_deref(channel->transport_arg);
+    mem_deref(channel->parameters);
+}
+
+/*
+ * Create a data channel (internal).
+ */
+enum rawrtc_code rawrtc_data_channel_create_internal(
+    struct rawrtc_data_channel** const channelp,  // de-referenced
+    struct rawrtc_data_transport* const transport,  // referenced
+    struct rawrtc_data_channel_parameters* const parameters,  // referenced
+    rawrtc_data_channel_open_handler const open_handler,  // nullable
+    rawrtc_data_channel_buffered_amount_low_handler const buffered_amount_low_handler,  // nullable
+    rawrtc_data_channel_error_handler const error_handler,  // nullable
+    rawrtc_data_channel_close_handler const close_handler,  // nullable
+    rawrtc_data_channel_message_handler const message_handler,  // nullable
+    void* const arg,  // nullable
+    bool const call_handler) {
+    enum rawrtc_code error;
+    struct rawrtc_data_channel* channel;
+
+    // Check arguments
+    if (!channelp || !transport || !parameters) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Allocate
+    channel = mem_zalloc(sizeof(*channel), rawrtc_data_channel_destroy);
+    if (!channel) {
+        return RAWRTC_CODE_NO_MEMORY;
+    }
+
+    // Set fields/reference
+    channel->flags = 0;
+    channel->state = RAWRTC_DATA_CHANNEL_STATE_CONNECTING;
+    channel->transport = mem_ref(transport);
+    channel->parameters = mem_ref(parameters);
+    channel->open_handler = open_handler;
+    channel->buffered_amount_low_handler = buffered_amount_low_handler;
+    channel->error_handler = error_handler;
+    channel->close_handler = close_handler;
+    channel->message_handler = message_handler;
+    channel->arg = arg;
+
+    // Create data channel on transport
+    if (call_handler) {
+        error = transport->channel_create(transport, channel, parameters);
+        if (error) {
+            goto out;
+        }
+    } else {
+        error = RAWRTC_CODE_SUCCESS;
+    }
+
+    // Done
+    DEBUG_PRINTF(
+        "Created data channel: label=%s, type=%d, reliability-parameter=%" PRIu32 ", "
+        "protocol=%s, negotiated=%s, id=%" PRIu16 "\n",
+        parameters->label ? parameters->label : "n/a", parameters->channel_type,
+        parameters->reliability_parameter, parameters->protocol ? parameters->protocol : "n/a",
+        parameters->negotiated ? "yes" : "no", parameters->id);
+
+out:
+    if (error) {
+        mem_deref(channel);
+    } else {
+        // Update flags & set pointer
+        channel->flags |= RAWRTC_DATA_CHANNEL_FLAGS_INITIALIZED;
+        *channelp = channel;
+    }
+    return error;
+}
+
+/*
+ * Call the data channel handler (internal).
+ *
+ * Important: Data transport implementations SHALL call this function
+ * instead of calling the channel handler directly.
+ */
+void rawrtc_data_channel_call_channel_handler(
+    struct rawrtc_data_channel* const channel,  // not checked
+    rawrtc_data_channel_handler const channel_handler,  // nullable
+    void* const arg) {
+    // Call handler (if any)
+    if (channel_handler) {
+        channel_handler(channel, arg);
+    }
+}
+
+/*
+ * Create a data channel.
+ * `*channelp` must be unreferenced.
+ *
+ * Note: You should call `rawrtc_data_channel_set_streaming`
+ *       directly after this function returned if you want to enable
+ *       streamed delivery of data for this channel from the beginning
+ *       of the first incoming message.
+ */
+enum rawrtc_code rawrtc_data_channel_create(
+    struct rawrtc_data_channel** const channelp,  // de-referenced
+    struct rawrtc_data_transport* const transport,  // referenced
+    struct rawrtc_data_channel_parameters* const parameters,  // referenced
+    rawrtc_data_channel_open_handler const open_handler,  // nullable
+    rawrtc_data_channel_buffered_amount_low_handler const buffered_amount_low_handler,  // nullable
+    rawrtc_data_channel_error_handler const error_handler,  // nullable
+    rawrtc_data_channel_close_handler const close_handler,  // nullable
+    rawrtc_data_channel_message_handler const message_handler,  // nullable
+    void* const arg  // nullable
+) {
+    enum rawrtc_code const error = rawrtc_data_channel_create_internal(
+        channelp, transport, parameters, open_handler, buffered_amount_low_handler, error_handler,
+        close_handler, message_handler, arg, true);
+
+    // Done
+    return error;
+}
+
+/*
+ * Set the argument of a data channel that is passed to the various
+ * handlers.
+ */
+enum rawrtc_code rawrtc_data_channel_set_arg(
+    struct rawrtc_data_channel* const channel,
+    void* const arg  // nullable
+) {
+    // Check arguments
+    if (!channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Set handler argument & done
+    channel->arg = arg;
+    return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Send data via the data channel.
+ */
+enum rawrtc_code rawrtc_data_channel_send(
+    struct rawrtc_data_channel* const channel,
+    struct mbuf* const buffer,  // nullable (if empty message), referenced
+    bool const is_binary) {
+    // Check arguments
+    if (!channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Check state
+    // TODO: Is this correct or can we send during `connecting` as well?
+    if (channel->state != RAWRTC_DATA_CHANNEL_STATE_OPEN) {
+        return RAWRTC_CODE_INVALID_STATE;
+    }
+
+    // Call handler
+    return channel->transport->channel_send(channel, buffer, is_binary);
+}
+
+/*
+ * Close the data channel.
+ */
+enum rawrtc_code rawrtc_data_channel_close(struct rawrtc_data_channel* const channel) {
+    // Check arguments
+    if (!channel) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Don't close before the channel is initialised
+    // Note: This is needed as this function may be called in the destructor of the data channel
+    if (!(channel->flags & RAWRTC_DATA_CHANNEL_FLAGS_INITIALIZED)) {
+        return RAWRTC_CODE_SUCCESS;
+    }
+
+    // Check state
+    if (channel->state == RAWRTC_DATA_CHANNEL_STATE_CLOSING ||
+        channel->state == RAWRTC_DATA_CHANNEL_STATE_CLOSED) {
+        return RAWRTC_CODE_SUCCESS;
+    }
+
+    // Close channel
+    DEBUG_PRINTF("Closing data channel: %s\n", channel->parameters->label);
+    return channel->transport->channel_close(channel);
+}
diff --git a/src/data_channel/channel.h b/src/data_channel/channel.h
new file mode 100644
index 0000000..c6e8e89
--- /dev/null
+++ b/src/data_channel/channel.h
@@ -0,0 +1,58 @@
+#pragma once
+#include <rawrtcdc/data_channel.h>
+#include <rawrtcdc/data_channel_parameters.h>
+#include <rawrtcdc/data_transport.h>
+#include <rawrtcc/code.h>
+#include <re.h>
+
+/*
+ * Data channel flags.
+ */
+enum {
+    RAWRTC_DATA_CHANNEL_FLAGS_INITIALIZED = 1 << 0,
+    RAWRTC_DATA_CHANNEL_FLAGS_STREAMED = 1 << 1,
+};
+
+/*
+ * Data channel type unordered bit flag.
+ */
+enum {
+    RAWRTC_DATA_CHANNEL_TYPE_IS_UNORDERED = 0x80,
+};
+
+/*
+ * Data channel.
+ */
+struct rawrtc_data_channel {
+    uint_fast8_t flags;
+    enum rawrtc_data_channel_state state;
+    struct rawrtc_data_transport* transport;  // referenced
+    void* transport_arg;  // referenced
+    struct rawrtc_data_channel_parameters* parameters;  // referenced
+    rawrtc_data_channel_open_handler open_handler;  // nullable
+    rawrtc_data_channel_buffered_amount_low_handler buffered_amount_low_handler;  // nullable
+    rawrtc_data_channel_error_handler error_handler;  // nullable
+    rawrtc_data_channel_close_handler close_handler;  // nullable
+    rawrtc_data_channel_message_handler message_handler;  // nullable
+    void* arg;  // nullable
+};
+
+void rawrtc_data_channel_set_state(
+    struct rawrtc_data_channel* const channel, enum rawrtc_data_channel_state const state);
+
+enum rawrtc_code rawrtc_data_channel_create_internal(
+    struct rawrtc_data_channel** const channelp,  // de-referenced
+    struct rawrtc_data_transport* const transport,  // referenced
+    struct rawrtc_data_channel_parameters* const parameters,  // referenced
+    rawrtc_data_channel_open_handler const open_handler,  // nullable
+    rawrtc_data_channel_buffered_amount_low_handler const buffered_amount_low_handler,  // nullable
+    rawrtc_data_channel_error_handler const error_handler,  // nullable
+    rawrtc_data_channel_close_handler const close_handler,  // nullable
+    rawrtc_data_channel_message_handler const message_handler,  // nullable
+    void* const arg,  // nullable
+    bool const call_handler);
+
+void rawrtc_data_channel_call_channel_handler(
+    struct rawrtc_data_channel* const channel,  // not checked
+    rawrtc_data_channel_handler const channel_handler,  // nullable
+    void* const arg);
diff --git a/src/data_channel/meson.build b/src/data_channel/meson.build
new file mode 100644
index 0000000..9ab3083
--- /dev/null
+++ b/src/data_channel/meson.build
@@ -0,0 +1,5 @@
+sources += files([
+    'attributes.c',
+    'channel.c',
+    'utils.c',
+])
diff --git a/src/data_channel/utils.c b/src/data_channel/utils.c
new file mode 100644
index 0000000..31ab581
--- /dev/null
+++ b/src/data_channel/utils.c
@@ -0,0 +1,19 @@
+#include <rawrtcdc/data_channel.h>
+
+/*
+ * Get the corresponding name for a data channel state.
+ */
+char const* rawrtc_data_channel_state_to_name(enum rawrtc_data_channel_state const state) {
+    switch (state) {
+        case RAWRTC_DATA_CHANNEL_STATE_CONNECTING:
+            return "connecting";
+        case RAWRTC_DATA_CHANNEL_STATE_OPEN:
+            return "open";
+        case RAWRTC_DATA_CHANNEL_STATE_CLOSING:
+            return "closing";
+        case RAWRTC_DATA_CHANNEL_STATE_CLOSED:
+            return "closed";
+        default:
+            return "???";
+    }
+}