summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorInaky Perez-Gonzalez <inaky@linux.intel.com>2009-04-01 09:59:25 -0700
committerInaky Perez-Gonzalez <inaky@linux.intel.com>2009-04-01 14:35:55 -0700
commit2b554f22f0f433120752f89b77fc87c1a3d48220 (patch)
tree77fa237de156bf017e66a67d3b3312e5efa3498e
parent565f1807594a9106ed2d43ca4ff9cb5fa0a47bcc (diff)
libwimaxll-i2400m: introduce i2400m helpers
Simple helpers to ease up sending commands and receiving acks from the device, as well as processing reports and buffers of TLVs. Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
-rw-r--r--include/wimaxll/Makefile.am1
-rw-r--r--include/wimaxll/Makefile.in1
-rw-r--r--include/wimaxll/i2400m.h96
-rw-r--r--lib/Makefile.am18
-rw-r--r--lib/Makefile.in57
-rw-r--r--lib/i2400m.c502
6 files changed, 665 insertions, 10 deletions
diff --git a/include/wimaxll/Makefile.am b/include/wimaxll/Makefile.am
index fcef043..56fa615 100644
--- a/include/wimaxll/Makefile.am
+++ b/include/wimaxll/Makefile.am
@@ -1,5 +1,6 @@
wimaxllincludedir = @includedir@/wimaxll
wimaxllinclude_HEADERS = \
cmd.h \
+ i2400m.h \
log.h \
version.h
diff --git a/include/wimaxll/Makefile.in b/include/wimaxll/Makefile.in
index 72f35e2..1fef19a 100644
--- a/include/wimaxll/Makefile.in
+++ b/include/wimaxll/Makefile.in
@@ -176,6 +176,7 @@ top_srcdir = @top_srcdir@
wimaxllincludedir = @includedir@/wimaxll
wimaxllinclude_HEADERS = \
cmd.h \
+ i2400m.h \
log.h \
version.h
diff --git a/include/wimaxll/i2400m.h b/include/wimaxll/i2400m.h
new file mode 100644
index 0000000..04c9cc9
--- /dev/null
+++ b/include/wimaxll/i2400m.h
@@ -0,0 +1,96 @@
+/*
+ * Linux WiMax
+ * i2400m specific helpers
+ *
+ *
+ * Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __wimaxll__i2400m_h__
+#define __wimaxll__i2400m_h__
+
+#include <sys/types.h>
+#include <linux/wimax/i2400m.h>
+
+struct i2400m;
+
+/*
+ * Callback called by i2400m_msg_to_dev() when a reply to the executed
+ * command arrives.
+ *
+ * In struct i2400m, the fields mt_cb_priv, mt_orig, and mt_orig_size are
+ * set for reference.
+ *
+ * The callback is passed the reply and to only return some error
+ * value that i2400m_msg_to_dev() will return to the caller.
+ *
+ * You CANNOT execute other commands with i2400m_msg_to_dev() inside
+ * this function neither wait for reports to arrive. You'd deadlock.
+ */
+typedef int (*i2400m_reply_cb)(
+ struct i2400m *, void *priv,
+ const struct i2400m_l3l4_hdr *reply, size_t reply_size);
+
+/**
+ * Callback for handling i2400m reports.
+ *
+ * This function is called when the i2400m sends a report/indication.
+ *
+ * You cannot execute commands or wait for other reports from this
+ * callback or it woul deadlock. You need to spawn off a thread or do
+ * some other arrangement for it.
+ *
+ * @param i2400m i2400m device descriptor; use i2400m_priv() to obtain
+ * the private pointer for it
+ * @param l3l4 Pointer to the report data in L3L4 message format; note
+ * this buffer is only valid in this execution context. Once the
+ * callback returns, it will be destroyed.
+ * @param l3l4_size Size of the buffer pointed to by l3l4.
+ */
+typedef void (*i2400m_report_cb)(
+ struct i2400m *i2400m,
+ const struct i2400m_l3l4_hdr *l3l4, size_t l3l4_size);
+
+int i2400m_create(struct i2400m **, const char *, void *, i2400m_report_cb);
+void i2400m_destroy(struct i2400m *);
+int i2400m_msg_to_dev(struct i2400m *, const struct i2400m_l3l4_hdr *, size_t,
+ i2400m_reply_cb, void *);
+void *i2400m_priv(struct i2400m *);
+struct wimaxll_handle *i2400m_wmx(struct i2400m *);
+
+ssize_t i2400m_tlv_match(
+ const struct i2400m_tlv_hdr *, enum i2400m_tlv, ssize_t);
+
+const struct i2400m_tlv_hdr *i2400m_tlv_buffer_walk(
+ const void *, size_t, const struct i2400m_tlv_hdr *);
+
+const struct i2400m_tlv_hdr *i2400m_tlv_find(
+ const struct i2400m_tlv_hdr *, size_t, enum i2400m_tlv, ssize_t);
+
+#endif /* #define __wimaxll__i2400m_h__ */
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 272c8d5..022ac81 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -28,8 +28,22 @@ libwimaxll_la_CFLAGS = $(AM_CFLAGS)
libwimaxll_la_LDFLAGS = -version-info 1:0:1 $(LIBNL1_LIBS)
libwimaxll_a_SOURCES = $(libwimaxll_sources)
-lib_LTLIBRARIES = libwimaxll.la
-lib_LIBRARIES = libwimaxll.a
+libwimaxll_i2400m_sources = \
+ i2400m.c
+
+libwimaxll_i2400m_la_SOURCES = $(libwimaxll_i2400m_sources)
+# Trick automake
+libwimaxll_i2400m_la_CFLAGS = $(AM_CFLAGS)
+# -version-info is CURRENT:REVISION:AGE
+# CURRENT: inc for added, removed/changed interfaces
+# REVISION: inc for changes that do not affect the external interface
+# AGE: inc for added interfaces
+# set to zero if removed existing interfaces
+libwimaxll_i2400m_la_LDFLAGS = -lpthread -version-info 1:0:0 $(LIBNL1_LIBS)
+libwimaxll_i2400m_a_SOURCES = $(libwimaxll_sources)
+
+lib_LTLIBRARIES = libwimaxll.la libwimaxll-i2400m.la
+lib_LIBRARIES = libwimaxll.a libwimaxll-i2400m.a
BUILT_SOURCES = names-vals.h
diff --git a/lib/Makefile.in b/lib/Makefile.in
index e238c6c..4f70a2c 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -55,22 +55,34 @@ am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libdir)"
libLIBRARIES_INSTALL = $(INSTALL_DATA)
LIBRARIES = $(lib_LIBRARIES)
ARFLAGS = cru
-libwimaxll_a_AR = $(AR) $(ARFLAGS)
-libwimaxll_a_LIBADD =
+libwimaxll_i2400m_a_AR = $(AR) $(ARFLAGS)
+libwimaxll_i2400m_a_LIBADD =
am__objects_1 = genl.$(OBJEXT) log.$(OBJEXT) misc.$(OBJEXT) \
op-open.$(OBJEXT) op-msg.$(OBJEXT) op-reset.$(OBJEXT) \
op-rfkill.$(OBJEXT) re-state-change.$(OBJEXT) wimax.$(OBJEXT)
+am_libwimaxll_i2400m_a_OBJECTS = $(am__objects_1)
+libwimaxll_i2400m_a_OBJECTS = $(am_libwimaxll_i2400m_a_OBJECTS)
+libwimaxll_a_AR = $(AR) $(ARFLAGS)
+libwimaxll_a_LIBADD =
am_libwimaxll_a_OBJECTS = $(am__objects_1)
libwimaxll_a_OBJECTS = $(am_libwimaxll_a_OBJECTS)
libLTLIBRARIES_INSTALL = $(INSTALL)
LTLIBRARIES = $(lib_LTLIBRARIES)
+libwimaxll_i2400m_la_LIBADD =
+am__objects_2 = libwimaxll_i2400m_la-i2400m.lo
+am_libwimaxll_i2400m_la_OBJECTS = $(am__objects_2)
+libwimaxll_i2400m_la_OBJECTS = $(am_libwimaxll_i2400m_la_OBJECTS)
+libwimaxll_i2400m_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(libwimaxll_i2400m_la_CFLAGS) $(CFLAGS) \
+ $(libwimaxll_i2400m_la_LDFLAGS) $(LDFLAGS) -o $@
libwimaxll_la_LIBADD =
-am__objects_2 = libwimaxll_la-genl.lo libwimaxll_la-log.lo \
+am__objects_3 = libwimaxll_la-genl.lo libwimaxll_la-log.lo \
libwimaxll_la-misc.lo libwimaxll_la-op-open.lo \
libwimaxll_la-op-msg.lo libwimaxll_la-op-reset.lo \
libwimaxll_la-op-rfkill.lo libwimaxll_la-re-state-change.lo \
libwimaxll_la-wimax.lo
-am_libwimaxll_la_OBJECTS = $(am__objects_2)
+am_libwimaxll_la_OBJECTS = $(am__objects_3)
libwimaxll_la_OBJECTS = $(am_libwimaxll_la_OBJECTS)
libwimaxll_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(libwimaxll_la_CFLAGS) \
@@ -88,8 +100,10 @@ CCLD = $(CC)
LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
$(LDFLAGS) -o $@
-SOURCES = $(libwimaxll_a_SOURCES) $(libwimaxll_la_SOURCES)
-DIST_SOURCES = $(libwimaxll_a_SOURCES) $(libwimaxll_la_SOURCES)
+SOURCES = $(libwimaxll_i2400m_a_SOURCES) $(libwimaxll_a_SOURCES) \
+ $(libwimaxll_i2400m_la_SOURCES) $(libwimaxll_la_SOURCES)
+DIST_SOURCES = $(libwimaxll_i2400m_a_SOURCES) $(libwimaxll_a_SOURCES) \
+ $(libwimaxll_i2400m_la_SOURCES) $(libwimaxll_la_SOURCES)
HEADERS = $(noinst_HEADERS)
ETAGS = etags
CTAGS = ctags
@@ -238,8 +252,21 @@ libwimaxll_la_CFLAGS = $(AM_CFLAGS)
# set to zero if removed existing interfaces
libwimaxll_la_LDFLAGS = -version-info 1:0:1 $(LIBNL1_LIBS)
libwimaxll_a_SOURCES = $(libwimaxll_sources)
-lib_LTLIBRARIES = libwimaxll.la
-lib_LIBRARIES = libwimaxll.a
+libwimaxll_i2400m_sources = \
+ i2400m.c
+
+libwimaxll_i2400m_la_SOURCES = $(libwimaxll_i2400m_sources)
+# Trick automake
+libwimaxll_i2400m_la_CFLAGS = $(AM_CFLAGS)
+# -version-info is CURRENT:REVISION:AGE
+# CURRENT: inc for added, removed/changed interfaces
+# REVISION: inc for changes that do not affect the external interface
+# AGE: inc for added interfaces
+# set to zero if removed existing interfaces
+libwimaxll_i2400m_la_LDFLAGS = -lpthread -version-info 1:0:0 $(LIBNL1_LIBS)
+libwimaxll_i2400m_a_SOURCES = $(libwimaxll_sources)
+lib_LTLIBRARIES = libwimaxll.la libwimaxll-i2400m.la
+lib_LIBRARIES = libwimaxll.a libwimaxll-i2400m.a
BUILT_SOURCES = names-vals.h
enum_names = \
wimax_st
@@ -310,6 +337,10 @@ uninstall-libLIBRARIES:
clean-libLIBRARIES:
-test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES)
+libwimaxll-i2400m.a: $(libwimaxll_i2400m_a_OBJECTS) $(libwimaxll_i2400m_a_DEPENDENCIES)
+ -rm -f libwimaxll-i2400m.a
+ $(libwimaxll_i2400m_a_AR) libwimaxll-i2400m.a $(libwimaxll_i2400m_a_OBJECTS) $(libwimaxll_i2400m_a_LIBADD)
+ $(RANLIB) libwimaxll-i2400m.a
libwimaxll.a: $(libwimaxll_a_OBJECTS) $(libwimaxll_a_DEPENDENCIES)
-rm -f libwimaxll.a
$(libwimaxll_a_AR) libwimaxll.a $(libwimaxll_a_OBJECTS) $(libwimaxll_a_LIBADD)
@@ -341,6 +372,8 @@ clean-libLTLIBRARIES:
echo "rm -f \"$${dir}/so_locations\""; \
rm -f "$${dir}/so_locations"; \
done
+libwimaxll-i2400m.la: $(libwimaxll_i2400m_la_OBJECTS) $(libwimaxll_i2400m_la_DEPENDENCIES)
+ $(libwimaxll_i2400m_la_LINK) -rpath $(libdir) $(libwimaxll_i2400m_la_OBJECTS) $(libwimaxll_i2400m_la_LIBADD) $(LIBS)
libwimaxll.la: $(libwimaxll_la_OBJECTS) $(libwimaxll_la_DEPENDENCIES)
$(libwimaxll_la_LINK) -rpath $(libdir) $(libwimaxll_la_OBJECTS) $(libwimaxll_la_LIBADD) $(LIBS)
@@ -351,6 +384,7 @@ distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/genl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwimaxll_i2400m_la-i2400m.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwimaxll_la-genl.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwimaxll_la-log.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwimaxll_la-misc.Plo@am__quote@
@@ -390,6 +424,13 @@ distclean-compile:
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+libwimaxll_i2400m_la-i2400m.lo: i2400m.c
+@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwimaxll_i2400m_la_CFLAGS) $(CFLAGS) -MT libwimaxll_i2400m_la-i2400m.lo -MD -MP -MF $(DEPDIR)/libwimaxll_i2400m_la-i2400m.Tpo -c -o libwimaxll_i2400m_la-i2400m.lo `test -f 'i2400m.c' || echo '$(srcdir)/'`i2400m.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libwimaxll_i2400m_la-i2400m.Tpo $(DEPDIR)/libwimaxll_i2400m_la-i2400m.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='i2400m.c' object='libwimaxll_i2400m_la-i2400m.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwimaxll_i2400m_la_CFLAGS) $(CFLAGS) -c -o libwimaxll_i2400m_la-i2400m.lo `test -f 'i2400m.c' || echo '$(srcdir)/'`i2400m.c
+
libwimaxll_la-genl.lo: genl.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwimaxll_la_CFLAGS) $(CFLAGS) -MT libwimaxll_la-genl.lo -MD -MP -MF $(DEPDIR)/libwimaxll_la-genl.Tpo -c -o libwimaxll_la-genl.lo `test -f 'genl.c' || echo '$(srcdir)/'`genl.c
@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libwimaxll_la-genl.Tpo $(DEPDIR)/libwimaxll_la-genl.Plo
diff --git a/lib/i2400m.c b/lib/i2400m.c
new file mode 100644
index 0000000..a3ec11c
--- /dev/null
+++ b/lib/i2400m.c
@@ -0,0 +1,502 @@
+/*
+ * Linux WiMax
+ * i2400m specific helpers
+ *
+ *
+ * Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * @defgroup i2400m_group Helpers to control an Intel 2400m based device
+ *
+ * This set of helpers simplify the task of sending commands / waiting
+ * for the acks and receiving reports/indications from the i2400m.
+ *
+ * It boils down to a framework to support that only one thread can
+ * send a command at the same time; this is because the commands don't
+ * have a cookie to identify the issuer -- so a place is needed where
+ * to store the "I am waiting for a response for command X".
+ *
+ * When the callback from libwimaxll comes back with the response, if
+ * it was a reply to said message, then the waiter for that is woken
+ * up (using pthread mutexes and conditional variables).
+ *
+ * When a report is received, the report callback is called; care has
+ * to be taken not to deadlock. See i2400m_report_cb().
+ *
+ * For usage, create a handle:
+ *
+ * @code
+ * {
+ * int r;
+ * struct i2400m *i2400m;
+ * ...
+ * r = i2400m_create(&i2400m, "wmx0", my_priv_pointer, my_report_cb);
+ * if (r < 0)
+ * error;
+ * ...
+ * // create a message
+ * ...
+ * r = i2400m_msg_to_dev(i2400m, &message, message_size,
+ * message_cb, message_cb_priv);
+ * if (r < 0)
+ * error;
+ * // message_cb has been called
+ * i2400m_destroy(i2400m);
+ * }
+ * @endcode
+ *
+ * Remember there are limited things that can be done in the callback;
+ * calling i2400m_msg_to_dev() will deadlock, as well as waiting for a
+ * report.
+ *
+ * A report callback with some TLV processing example would be:
+ *
+ * @code
+ * static
+ * void my_report_cb(struct i2400m *i2400m,
+ * const struct i2400m_l3l4_hdr *l3l4, size_t l3l4_size)
+ * {
+ * struct my_priv *my_priv = i2400m_priv(i2400m);
+ * struct wimaxll_handle *wmx = i2400m_wmx(i2400m);
+ *
+ * // do something with the report...;
+ *
+ * struct i2400m_tlv *tlv = NULL;
+ *
+ * while ((tlv = i2400m_tlv_buffer_walk(l3l4->pl, l3l4_size, tlv))) {
+ * tlv_type = wimaxll_le16_to_cpu(tlv->type);
+ * tlv_length = wimaxll_le16_to_cpu(tlv->length);
+ * // do whatever with the tlv
+ * }
+ *
+ * // or find a tlv in a buffer
+ * tlv = i2400m_tlv_find(l3l4->pl, l3l4_size - sizeof(*l3l4),
+ * I2400M_TLV_SOMETHING, -1);
+ *
+ * }
+ * @endcode
+ */
+#include <wimaxll/i2400m.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wimaxll.h>
+#include <internal.h>
+
+
+/**
+ * Descriptor for a Intel 2400m
+ *
+ * @param wmx libwimaxll handle
+ * @param priv Private storage as set by the owner
+ *
+ * @param mutex Mutex for command execution (protects mt_*)
+ * @param cond Conditional variable for command execution (protected
+ * by \e mutex). Threads wait on this conditional variable waiting
+ * for commands to finish executing (at which point the mt_cb is
+ * called back).
+ *
+ * @param mt_pending Message type of the reply that a thread is
+ * waiting for.
+ * @param mt_cb Callback to execute when the \e mt_pending reply
+ * arrives.
+ * @param mt_cb_priv Private data to pass to the \e mt_cb
+ * @param mt_result Updated with the result of executing the \e mt_cb
+ * callback. If cancelled, it will be -%EINTR.
+ *
+ * @param report_cb Callback to execute when a report/indication is
+ * received.
+ * @param report_cb_priv Private data passed to the report callback.
+ *
+ * @internal
+ * @ingroup i2400m_group
+ */
+struct i2400m {
+ struct wimaxll_handle *wmx;
+ void *priv;
+
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+
+ enum i2400m_mt mt_pending;
+ i2400m_reply_cb mt_cb;
+ void *mt_cb_priv;
+ int mt_result;
+
+ i2400m_report_cb report_cb;
+ void *report_cb_priv;
+};
+
+
+/*
+ * When a message comes with an ack or report, chew it
+ *
+ * Only takes messages on the default pipe, as that's where the device
+ * passes them. Executes the callback for a command ack if it's
+ * message type is the one that was waited for, otherwise they are
+ * ignored.
+ *
+ * The driver takes care of coordinating so that only one command is
+ * executed at the same time.
+ *
+ * If it is a report, just run the callback.
+ */
+static
+int i2400m_msg_to_user_cb(struct wimaxll_handle *wmx, void *_i2400m,
+ const char *pipe_name,
+ const void *data, size_t size)
+{
+ struct i2400m *i2400m = _i2400m;
+ const struct i2400m_l3l4_hdr *hdr = data;
+ enum i2400m_mt mt;
+
+ if (pipe_name != NULL)
+ goto out;
+
+ mt = wimaxll_le16_to_cpu(hdr->type);
+ pthread_mutex_lock(&i2400m->mutex);
+ if (mt == i2400m->mt_pending) {
+ i2400m->mt_pending = I2400M_MT_INVALID;
+ if (i2400m->mt_cb != NULL)
+ i2400m->mt_result = i2400m->mt_cb(
+ i2400m, i2400m->mt_cb_priv, data, size);
+ else
+ i2400m->mt_result = 0;
+ pthread_cond_signal(&i2400m->cond);
+ }
+ pthread_mutex_unlock(&i2400m->mutex);
+ /* this is ran outside of the lock because it doesn't need
+ * much tracking info. */
+ if (mt & I2400M_MT_REPORT_MASK && i2400m->report_cb)
+ i2400m->report_cb(i2400m, data, size);
+out:
+ return 0;
+}
+
+
+/**
+ * Create a i2400m handle
+ *
+ * Creates a handle usable to execute commands and use the i2400m
+ * helpers.
+ *
+ * @param _i2400m where to store the handler value (pointer to
+ * the descriptor).
+ *
+ * @param ifname name of the network interface where the i2400m is
+ *
+ * @param priv Pointer that the callbacks can recover from the
+ * handle with i2400m_priv()
+ *
+ * @param report_cb Callback function called when a report arrives
+ *
+ * @ingroup i2400m_group
+ */
+int i2400m_create(struct i2400m **_i2400m, const char *ifname,
+ void *priv, i2400m_report_cb report_cb)
+{
+ int result;
+ struct i2400m *i2400m;
+
+ i2400m = calloc(sizeof(*i2400m), 1);
+ if (i2400m == NULL)
+ goto error_calloc;
+ result = -ENODEV;
+ i2400m->wmx = wimaxll_open(ifname);
+ if (i2400m->wmx == NULL) {
+ result = -errno;
+ goto error_open;
+ }
+ pthread_mutex_init(&i2400m->mutex, NULL);
+ pthread_cond_init(&i2400m->cond, NULL);
+ i2400m->priv = priv;
+ i2400m->report_cb = report_cb;
+ i2400m->mt_pending = I2400M_MT_INVALID;
+
+ wimaxll_set_cb_msg_to_user(
+ i2400m->wmx, i2400m_msg_to_user_cb, i2400m);
+
+ *_i2400m = i2400m;
+ return 0;
+
+error_open:
+ free(i2400m);
+error_calloc:
+ return result;
+}
+
+
+/**
+ * Destroy a descriptor created with i2400m_create()
+ *
+ * @param i2400m Handle for an i2400m as returned by
+ * i2400m_create().
+ *
+ * @ingroup i2400m_group
+ */
+void i2400m_destroy(struct i2400m *i2400m)
+{
+ pthread_mutex_lock(&i2400m->mutex);
+ i2400m->mt_result = -EINTR;
+ pthread_cond_broadcast(&i2400m->cond);
+ pthread_mutex_unlock(&i2400m->mutex);
+ wimaxll_close(i2400m->wmx);
+ free(i2400m);
+}
+
+
+/**
+ * Return the private data associated to a \e i2400m
+ *
+ * @param i2400m i2400m handle
+ * @returns pointer to priv data as set at i2400m_create() time
+ *
+ * @ingroup i2400m_group
+ */
+void * i2400m_priv(struct i2400m *i2400m)
+{
+ return i2400m->priv;
+}
+
+
+/**
+ * Return the libwimaxll handle associated to a \e i2400m
+ *
+ * @param i2400m i2400m handle
+ * @returns wimaxll handle
+ *
+ * @ingroup i2400m_group
+ */
+struct wimaxll_handle * i2400m_wmx(struct i2400m *i2400m)
+{
+ return i2400m->wmx;
+}
+
+
+/**
+ * Execute an i2400m command and wait for a response
+ *
+ * @param i2400m i2400m handle
+ *
+ * @param l3l4 Pointer to buffer containing a L3L4 message to send to
+ * the device.
+ *
+ * @param l3l4_size size of the buffer pointed to by \e l3l4 (this
+ * includes the message header and the TLV payloads, if any)
+ *
+ * @param cb Callback function to execute when the reply is received.
+ *
+ * @param cb_priv Private pointer to pass to the callback function.
+ *
+ * If the message execution fails in the device, the return value from
+ * wimaxll_msg_write() will tell it. It can also be taken (with more
+ * detail) by setting a callback function and parsing the reply.
+ *
+ * This call can be executed from multiple threads on the same \e
+ * i2400m handle at the same time, as it will be mutexed properly and
+ * only one will execute at the same time (likewise, the driver will
+ * make sure only one command from different threads is ran at the
+ * same time).
+ *
+ * @note
+ *
+ * This call blocks waiting for the reply to the message; from the
+ * callback context no calls to i2400m_msg_dev() or waits for reports
+ * on the same handle as the callback can be done, as it would
+ * deadlock.
+ *
+ * @ingroup i2400m_group
+ */
+int i2400m_msg_to_dev(struct i2400m *i2400m,
+ const struct i2400m_l3l4_hdr *l3l4, size_t l3l4_size,
+ i2400m_reply_cb cb, void *cb_priv)
+{
+ int result;
+ enum i2400m_mt msg_type;
+
+ msg_type = wimaxll_le16_to_cpu(l3l4->type);
+ /* No need to check msg & payload consistency, the kernel will do for us */
+ /* Setup the completion, ack_skb ("we are waiting") and send
+ * the message to the device */
+ pthread_mutex_lock(&i2400m->mutex);
+ i2400m->mt_pending = msg_type;
+ i2400m->mt_cb = cb;
+ i2400m->mt_cb_priv = cb_priv;
+ result = wimaxll_msg_write(i2400m->wmx, NULL, l3l4, l3l4_size);
+ if (result < 0)
+ goto error_msg_write;
+ /* The driver guarantees that either we get the response to
+ * the command or only a notification, so we just need to wait
+ * for the reply to come */
+#warning FIXME: _timeout?
+ pthread_cond_wait(&i2400m->cond, &i2400m->mutex);
+ result = i2400m->mt_result;
+error_msg_write:
+ i2400m->mt_pending = I2400M_MT_INVALID;
+ pthread_mutex_unlock(&i2400m->mutex);
+ return result;
+}
+
+
+/**
+ * Return if a TLV is of a give type and size
+ *
+ * @param tlv pointer to the TLV
+ * @param tlv_type type of the TLV we are looking for
+ * @param tlv_size expected size of the TLV we are looking for (if -1,
+ * don't check the size). Size includes the TLV header.
+ * @returns 0 if the TLV matches, < 0 if it doesn't match at all, > 0
+ * total TLV + payload size, if the type matches, but not the size
+ *
+ * @ingroup i2400m_group
+ */
+ssize_t i2400m_tlv_match(const struct i2400m_tlv_hdr *tlv,
+ enum i2400m_tlv tlv_type, ssize_t tlv_size)
+{
+ if (wimaxll_le16_to_cpu(tlv->type) != tlv_type) /* Not our type? skip */
+ return -1;
+ if (tlv_size != -1
+ && wimaxll_le16_to_cpu(tlv->length) + sizeof(*tlv) != tlv_size)
+ return wimaxll_le16_to_cpu(tlv->length) + sizeof(*tlv);
+ return 0;
+}
+
+
+/**
+ * Iterate over a buffer of TLVs
+ *
+ * Allows to safely iterate over a buffer of TLVs, making sure bounds
+ * are properly checked. Usage:
+ *
+ * @code
+ * tlv_itr = NULL;
+ * while (tlv_itr = i2400m_tlv_buffer_walk(i2400m, buf, size, tlv_itr)) {
+ * ...
+ * // Do stuff with tlv_itr, DON'T MODIFY IT
+ * ...
+ * }
+ * @endcode
+ *
+ * @param tlv_buf pointer to the beginning of the TLV buffer
+ *
+ * @param buf_size buffer size in bytes
+ *
+ * @param tlv_pos seek position; this is assumed to be a pointer returned
+ * by i2400m_tlv_buffer_walk() [and thus, validated]. The TLV
+ * returned will be the one following this one.
+ *
+ * @returns pointer to the next TLV from the seek position or NULL if
+ * the end of the buffer was reached.
+ *
+ * @ingroup i2400m_group
+ */
+const struct i2400m_tlv_hdr *i2400m_tlv_buffer_walk(
+ const void *tlv_buf, size_t buf_size,
+ const struct i2400m_tlv_hdr *tlv_pos)
+{
+ const struct i2400m_tlv_hdr *tlv_top = tlv_buf + buf_size;
+ size_t offset, length, avail_size;
+ unsigned type;
+
+ if (tlv_pos == NULL) /* Take the first one? */
+ tlv_pos = tlv_buf;
+ else /* Nope, the next one */
+ tlv_pos = (void *) tlv_pos
+ + wimaxll_le16_to_cpu(tlv_pos->length) + sizeof(*tlv_pos);
+ if (tlv_pos == tlv_top) {
+ tlv_pos = NULL; /* buffer done */
+ goto error_beyond_end;
+ }
+ if (tlv_pos > tlv_top) {
+ tlv_pos = NULL;
+ goto error_beyond_end;
+ }
+ offset = (void *) tlv_pos - (void *) tlv_buf;
+ avail_size = buf_size - offset;
+ if (avail_size < sizeof(*tlv_pos)) {
+ wimaxll_msg(NULL,
+ "HW BUG? tlv_buf %p [%zu bytes], tlv @%zu: "
+ "short header\n", tlv_buf, buf_size, offset);
+ goto error_short_header;
+ }
+ type = wimaxll_le16_to_cpu(tlv_pos->type);
+ length = wimaxll_le16_to_cpu(tlv_pos->length);
+ if (avail_size < sizeof(*tlv_pos) + length) {
+ wimaxll_msg(NULL,
+ "HW BUG? tlv_buf %p [%zu bytes], "
+ "tlv type 0x%04x @%zu: "
+ "short data (%zu bytes vs %zu needed)\n",
+ tlv_buf, buf_size, type, offset, avail_size,
+ sizeof(*tlv_pos) + length);
+ goto error_short_header;
+ }
+error_short_header:
+error_beyond_end:
+ return tlv_pos;
+}
+
+
+/**
+ * Find a TLV by type (and maybe length) in a buffer of TLVs
+ *
+ * @param tlv_hdr pointer to the first TLV in the sequence
+ *
+ * @param size size of the buffer in bytes; all TLVs are assumed to fit
+ * fully in the buffer (otherwise we'll complain).
+ *
+ * @param tlv_type type of the TLV we are looking for
+ *
+ * @param tlv_size expected size of the TLV we are looking for (if -1,
+ * don't check the size). This includes the header
+ *
+ * @returns NULL if the TLV is not found, otherwise a pointer to
+ * it. If the sizes don't match, an error is printed and NULL
+ * returned.
+ *
+ * @ingroup i2400m_group
+ */
+const struct i2400m_tlv_hdr *i2400m_tlv_find(
+ const struct i2400m_tlv_hdr *tlv_hdr, size_t size,
+ enum i2400m_tlv tlv_type, ssize_t tlv_size)
+{
+ ssize_t match;
+ const struct i2400m_tlv_hdr *tlv = NULL;
+ while ((tlv = i2400m_tlv_buffer_walk(tlv_hdr, size, tlv))) {
+ match = i2400m_tlv_match(tlv, tlv_type, tlv_size);
+ if (match == 0) /* found it :) */
+ break;
+ if (match > 0)
+ wimaxll_msg(NULL,
+ "TLV type 0x%04x found with size "
+ "mismatch (%zu vs %zu needed)\n",
+ tlv_type, match, tlv_size);
+ }
+ return tlv;
+}