[alsa-devel] [PATCH 08/13] libhinawa: add 'fw_fcp' object as a helper of FCP transaction

Takashi Sakamoto o-takashi at sakamocchi.jp
Sun Jan 25 12:34:29 CET 2015


There are a standard way to transport function control data to units;
Function Control Protocol (FCP) in IEC 61883-1. This way uses a pair
of request transaction and response transaction. All of AV/C commands
which 1394TA defines is an application of FCP.

This commit adds HINAWA_TYPE_FW_FCP object for this purpose. This
object uses 'fw_resp' object and 'fw_req' object to support FCP.

I note that 'AV/C Digital Interface Command Set General
Specification Version 4.2' (September 1, 2004 1394TA) defines
'deferred transactions' in its clause 6.1.2. This type of AV/C
command requres FCP backend to wait 'unspecified interval' for next
response when receiving INTERIM type of response. To perform it,
this object uses 200 mili-seconds to wait again.

Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
---
 libhinawa/doc/reference/hinawa-docs.sgml |   1 +
 libhinawa/src/Makefile.am                |   7 +-
 libhinawa/src/fw_fcp.c                   | 282 +++++++++++++++++++++++++++++++
 libhinawa/src/fw_fcp.h                   |  58 +++++++
 4 files changed, 346 insertions(+), 2 deletions(-)
 create mode 100644 libhinawa/src/fw_fcp.c
 create mode 100644 libhinawa/src/fw_fcp.h

diff --git a/libhinawa/doc/reference/hinawa-docs.sgml b/libhinawa/doc/reference/hinawa-docs.sgml
index ff00ecc..d5773a5 100644
--- a/libhinawa/doc/reference/hinawa-docs.sgml
+++ b/libhinawa/doc/reference/hinawa-docs.sgml
@@ -33,6 +33,7 @@
             <xi:include href="xml/fw_unit.xml"/>
             <xi:include href="xml/fw_resp.xml"/>
             <xi:include href="xml/fw_req.xml"/>
+            <xi:include href="xml/fw_fcp.xml"/>
         </chapter>
     </part>
 
diff --git a/libhinawa/src/Makefile.am b/libhinawa/src/Makefile.am
index 91726b8..0a64658 100644
--- a/libhinawa/src/Makefile.am
+++ b/libhinawa/src/Makefile.am
@@ -29,13 +29,16 @@ libhinawa_la_SOURCES =				\
 	fw_resp.h				\
 	fw_resp.c				\
 	fw_req.h				\
-	fw_req.c
+	fw_req.c				\
+	fw_fcp.h				\
+	fw_fcp.c
 
 pkginclude_HEADERS =				\
 	hinawa_sigs_marshal.h			\
 	fw_unit.h				\
 	fw_resp.h				\
-	fw_req.h
+	fw_req.h				\
+	fw_fcp.h
 
 hinawa_sigs_marshal.list:
 	$(AM_V_GEN)( find | grep \.c$$ | xargs cat | 			\
diff --git a/libhinawa/src/fw_fcp.c b/libhinawa/src/fw_fcp.c
new file mode 100644
index 0000000..44c6d3b
--- /dev/null
+++ b/libhinawa/src/fw_fcp.c
@@ -0,0 +1,282 @@
+#include "fw_fcp.h"
+#include "fw_resp.h"
+#include "fw_req.h"
+
+/**
+ * SECTION:fw_fcp
+ * @Title: HinawaFwFcp
+ * @Short_description: A FCP transaction executor to a FireWire unit
+ *
+ * A HinawaFwFcp supports Function Control Protocol (FCP) in IEC 61883-1.
+ * Some types of transaction in 'AV/C Digital Interface Command Set General
+ * Specification Version 4.2' (Sep 1 2004, 1394TA) requires low layer support,
+ * thus this class has a code for them.
+ *
+ * Any of transaction frames should be aligned to 8bit (byte).
+ * This class is an application of #HinawaFwReq / #HinawaFwResp.
+ */
+
+#define FCP_MAXIMUM_FRAME_BYTES	0x200U
+#define FCP_REQUEST_ADDR	0xfffff0000b00
+#define FCP_RESPOND_ADDR	0xfffff0000d00
+
+/* For your information. */
+enum avc_type {
+	AVC_TYPE_CONTROL		= 0x00,
+	AVC_TYPE_STATUS			= 0x01,
+	AVC_TYPE_SPECIFIC_INQUIRY	= 0x02,
+	AVC_TYPE_NOTIFY			= 0x03,
+	AVC_TYPE_GENERAL_INQUIRY	= 0x04,
+	/* 0x05-0x07 are reserved. */
+};
+/* continue */
+enum avc_status {
+	AVC_STATUS_NOT_IMPLEMENTED	= 0x08,
+	AVC_STATUS_ACCEPTED		= 0x09,
+	AVC_STATUS_REJECTED		= 0x0a,
+	AVC_STATUS_IN_TRANSITION	= 0x0b,
+	AVC_STATUS_IMPLEMENTED_STABLE	= 0x0c,
+	AVC_STATUS_CHANGED		= 0x0d,
+	/* reserved */
+	AVC_STATUS_INTERIM		= 0x0f,
+};
+
+struct fcp_transaction {
+	GArray *req_frame;	/* Request frame */
+	GArray *resp_frame;	/* Response frame */
+	GCond cond;
+};
+
+struct _HinawaFwFcpPrivate {
+	HinawaFwUnit *unit;
+	HinawaFwResp *resp;
+
+	GList *transactions;
+	GMutex lock;
+};
+G_DEFINE_TYPE_WITH_PRIVATE(HinawaFwFcp, hinawa_fw_fcp, G_TYPE_OBJECT)
+#define FW_FCP_GET_PRIVATE(obj)						\
+	(G_TYPE_INSTANCE_GET_PRIVATE((obj),				\
+				     HINAWA_TYPE_FW_FCP, HinawaFwFcpPrivate))
+
+static void hinawa_fw_fcp_dispose(GObject *obj)
+{
+	HinawaFwFcp *self = HINAWA_FW_FCP(obj);
+
+	hinawa_fw_fcp_unlisten(self);
+
+	G_OBJECT_CLASS(hinawa_fw_fcp_parent_class)->dispose(obj);
+}
+
+static void hinawa_fw_fcp_finalize(GObject *gobject)
+{
+	G_OBJECT_CLASS(hinawa_fw_fcp_parent_class)->finalize(gobject);
+}
+
+static void hinawa_fw_fcp_class_init(HinawaFwFcpClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+
+	gobject_class->dispose = hinawa_fw_fcp_dispose;
+	gobject_class->finalize = hinawa_fw_fcp_finalize;
+}
+
+static void hinawa_fw_fcp_init(HinawaFwFcp *self)
+{
+	self->priv = hinawa_fw_fcp_get_instance_private(self);
+}
+
+/**
+ * hinawa_fw_fcp_transact:
+ * @self: A #HinawaFwFcp
+ * @req_frame:  (element-type guint8) (array) (in): a byte frame for request
+ * @resp_frame: (element-type guint8) (array) (out caller-allocates): a byte
+ *		frame for response
+ * @exception: A #GError
+ */
+void hinawa_fw_fcp_transact(HinawaFwFcp *self,
+			    GArray *req_frame, GArray *resp_frame,
+			    GError **exception)
+{
+	HinawaFwFcpPrivate *priv;
+	HinawaFwReq *req;
+	struct fcp_transaction trans = {0};
+	GMutex local_lock;
+	gint64 expiration;
+	guint32 *buf;
+	guint i, quads, bytes;
+
+	g_return_if_fail(HINAWA_IS_FW_FCP(self));
+	priv = FW_FCP_GET_PRIVATE(self);
+
+	if (req_frame  == NULL || g_array_get_element_size(req_frame)  != 1 ||
+	    resp_frame == NULL || g_array_get_element_size(resp_frame) != 1 ||
+	    req_frame->len > FCP_MAXIMUM_FRAME_BYTES) {
+		g_set_error(exception, g_quark_from_static_string(__func__),
+			    EINVAL, "%s", strerror(EINVAL));
+		return;
+	}
+
+	req = g_object_new(HINAWA_TYPE_FW_REQ, NULL);
+
+	/* Copy guint8 array to guint32 array. */
+	quads = (req_frame->len + sizeof(guint32) - 1) / sizeof(guint32);
+	trans.req_frame = g_array_sized_new(FALSE, TRUE,
+					    sizeof(guint32), quads);
+	g_array_set_size(trans.req_frame, quads);
+	memcpy(trans.req_frame->data, req_frame->data, req_frame->len);
+	buf = (guint32 *)trans.req_frame->data;
+	for (i = 0; i < trans.req_frame->len; i++)
+		buf[i] = htobe32(buf[i]);
+
+	/* Prepare response buffer. */
+	trans.resp_frame = g_array_sized_new(FALSE, TRUE,
+					     sizeof(guint32), quads);
+
+	/* Insert this entry. */
+	g_mutex_lock(&priv->lock);
+	priv->transactions = g_list_prepend(priv->transactions, &trans);
+	g_mutex_unlock(&priv->lock);
+
+	/* NOTE: Timeout is 200 milli-seconds. */
+	expiration = g_get_monotonic_time() + 200 * G_TIME_SPAN_MILLISECOND;
+	g_cond_init(&trans.cond);
+	g_mutex_init(&local_lock);
+
+	/* Send this request frame. */
+	hinawa_fw_req_write(req, priv->unit, FCP_REQUEST_ADDR, trans.req_frame,
+			    exception);
+	if (*exception != NULL)
+		goto end;
+deferred:
+	/*
+	 * Wait corresponding response till timeout.
+	 * NOTE: Timeout at bus-reset, illegally.
+	 */
+	g_mutex_lock(&local_lock);
+	if (!g_cond_wait_until(&trans.cond, &local_lock, expiration))
+		g_set_error(exception, g_quark_from_static_string(__func__),
+			    ETIMEDOUT, "%s", strerror(ETIMEDOUT));
+	g_mutex_unlock(&local_lock);
+
+	/* Error happened. */
+	if (*exception != NULL)
+		goto end;
+
+	/* It's a deffered transaction, wait 200 milli-seconds again. */
+	if (trans.resp_frame->data[0] >> 24 == AVC_STATUS_INTERIM) {
+		expiration = g_get_monotonic_time() +
+			     200 * G_TIME_SPAN_MILLISECOND;
+		goto deferred;
+	}
+
+	/* Convert guint32 array to guint8 array. */
+	buf = (guint32 *)trans.resp_frame->data;
+	for (i = 0; i < trans.resp_frame->len; i++)
+		buf[i] = htobe32(buf[i]);
+	bytes = trans.resp_frame->len * sizeof(guint32);
+	g_array_set_size(resp_frame, bytes);
+	memcpy(resp_frame->data, trans.resp_frame->data, bytes);
+end:
+	/* Remove this entry. */
+	g_mutex_lock(&priv->lock);
+	priv->transactions =
+			g_list_remove(priv->transactions, (gpointer *)&trans);
+	g_mutex_unlock(&priv->lock);
+
+	g_array_free(trans.req_frame, TRUE);
+	g_mutex_clear(&local_lock);
+	g_clear_object(&req);
+}
+
+static GArray *handle_response(HinawaFwResp *self, gint tcode,
+			       GArray *req_frame, gpointer user_data)
+{
+	HinawaFwFcp *fcp = (HinawaFwFcp *)user_data;
+	HinawaFwFcpPrivate *priv = FW_FCP_GET_PRIVATE(fcp);
+	struct fcp_transaction *trans;
+	GList *entry;
+
+	g_mutex_lock(&priv->lock);
+
+	/* Seek correcponding request. */
+	for (entry = priv->transactions; entry != NULL; entry = entry->next) {
+		trans = (struct fcp_transaction *)entry->data;
+
+		if ((trans->req_frame->data[1] == req_frame->data[1]) &&
+		    (trans->req_frame->data[2] == req_frame->data[2]))
+			break;
+	}
+
+	/* No requests corresponding to this response. */
+	if (entry == NULL)
+		goto end;
+
+	g_array_insert_vals(trans->resp_frame, 0,
+			    req_frame->data, req_frame->len);
+	g_cond_signal(&trans->cond);
+end:
+	g_mutex_unlock(&priv->lock);
+
+	/* Transfer no data in the response frame. */
+	return NULL;
+}
+
+/**
+ * hinawa_fw_fcp_listen:
+ * @self: A #HinawaFwFcp
+ * @unit: A #HinawaFwUnit
+ * @exception: A #GError
+ *
+ * Start to listen to FCP responses.
+ */
+void hinawa_fw_fcp_listen(HinawaFwFcp *self, HinawaFwUnit *unit,
+			  GError **exception)
+{
+	HinawaFwFcpPrivate *priv;
+
+	g_return_if_fail(HINAWA_IS_FW_FCP(self));
+	priv = FW_FCP_GET_PRIVATE(self);
+
+	priv->resp = g_object_new(HINAWA_TYPE_FW_RESP, NULL);
+	priv->unit = g_object_ref(unit);
+
+	hinawa_fw_resp_register(priv->resp, priv->unit,
+				FCP_RESPOND_ADDR, FCP_MAXIMUM_FRAME_BYTES,
+				exception);
+	if (*exception != NULL) {
+		g_clear_object(&priv->resp);
+		priv->resp = NULL;
+		g_object_unref(priv->unit);
+		priv->unit = NULL;
+		return;
+	}
+
+	g_signal_connect(priv->resp, "requested",
+			 G_CALLBACK(handle_response), self);
+
+	g_mutex_init(&priv->lock);
+	priv->transactions = NULL;
+}
+
+/**
+ * hinawa_fw_fcp_unlisten:
+ * @self: A #HinawaFwFcp
+ *
+ * Stop to listen to FCP responses.
+ */
+void hinawa_fw_fcp_unlisten(HinawaFwFcp *self)
+{
+	HinawaFwFcpPrivate *priv;
+
+	g_return_if_fail(HINAWA_IS_FW_FCP(self));
+	priv = FW_FCP_GET_PRIVATE(self);
+
+	if (priv->resp == NULL)
+		return;
+
+	hinawa_fw_resp_unregister(priv->resp);
+	priv->resp = NULL;
+	g_object_unref(priv->unit);
+	priv->unit = NULL;
+}
diff --git a/libhinawa/src/fw_fcp.h b/libhinawa/src/fw_fcp.h
new file mode 100644
index 0000000..206ebee
--- /dev/null
+++ b/libhinawa/src/fw_fcp.h
@@ -0,0 +1,58 @@
+#ifndef __ALSA_TOOLS_HINAWA_FW_FCP_H__
+#define __ALSA_TOOLS_HINAWA_FW_FCP_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include "internal.h"
+#include "fw_unit.h"
+
+G_BEGIN_DECLS
+
+#define HINAWA_TYPE_FW_FCP	(hinawa_fw_fcp_get_type())
+
+#define HINAWA_FW_FCP(obj)					\
+	(G_TYPE_CHECK_INSTANCE_CAST((obj),			\
+				    HINAWA_TYPE_FW_FCP,		\
+				    HinawaFwFcp))
+#define HINAWA_IS_FW_FCP(obj)					\
+	(G_TYPE_CHECK_INSTANCE_TYPE((obj),			\
+				    HINAWA_TYPE_FW_FCP))
+
+#define HINAWA_FW_FCP_CLASS(klass)				\
+	(G_TYPE_CHECK_CLASS_CAST((klass),			\
+				 HINAWA_TYPE_FW_FCP,		\
+				 HinawaFwFcpClass))
+#define HINAWA_IS_FW_FCP_CLASS(klass)				\
+	(G_TYPE_CHECK_CLASS_TYPE((klass),			\
+				 HINAWA_TYPE_FW_FCP))
+#define HINAWA_FW_FCP_GET_CLASS(obj)				\
+	(G_TYPE_INSTANCE_GET_CLASS((obj),			\
+				   HINAWA_TYPE_FW_FCP,		\
+				   HinawaFwFcpClass))
+
+typedef struct _HinawaFwFcp		HinawaFwFcp;
+typedef struct _HinawaFwFcpClass	HinawaFwFcpClass;
+typedef struct _HinawaFwFcpPrivate	HinawaFwFcpPrivate;
+
+struct _HinawaFwFcp {
+	GObject parent_instance;
+
+	HinawaFwFcpPrivate *priv;
+};
+
+struct _HinawaFwFcpClass {
+	GObjectClass parent_class;
+};
+
+GType hinawa_fw_fcp_get_type(void) G_GNUC_CONST;
+
+HinawaFwFcp *hinawa_fw_fcp_new(GError **exception);
+
+void hinawa_fw_fcp_listen(HinawaFwFcp *self, HinawaFwUnit *unit,
+			  GError **exception);
+void hinawa_fw_fcp_transact(HinawaFwFcp *self,
+			    GArray *req_frame, GArray *resp_frame,
+			    GError **exception);
+void hinawa_fw_fcp_unlisten(HinawaFwFcp *self);
+
+#endif
-- 
2.1.0



More information about the Alsa-devel mailing list