diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 37 | ||||
-rw-r--r-- | lib/Makefile.in | 647 | ||||
-rw-r--r-- | lib/config.h.in | 58 | ||||
-rw-r--r-- | lib/debug.h | 114 | ||||
-rw-r--r-- | lib/internal.h | 187 | ||||
-rw-r--r-- | lib/libwimax.so.in | 3 | ||||
-rw-r--r-- | lib/mc_rx.c | 535 | ||||
-rw-r--r-- | lib/op-msg.c | 565 | ||||
-rw-r--r-- | lib/op-open.c | 548 | ||||
-rw-r--r-- | lib/op-reset.c | 122 | ||||
-rw-r--r-- | lib/op-rfkill.c | 129 | ||||
-rw-r--r-- | lib/pipe.c | 196 | ||||
-rw-r--r-- | lib/re-state-change.c | 317 | ||||
-rw-r--r-- | lib/wimax.c | 443 |
14 files changed, 3901 insertions, 0 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..2b9d879 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,37 @@ + +INCLUDES = \ + -I$(top_builddir)/include -I$(top_srcdir)/include \ + $(LIBNL1_CFLAGS) \ + $(I2400M_INCLUDES) + +noinst_HEADERS = debug.h internal.h + +lib_LTLIBRARIES = libwimaxll.la +lib_LIBRARIES = libwimaxll.a +libwimaxll_sources = \ + op-open.c \ + op-msg.c \ + op-reset.c \ + op-rfkill.c \ + mc_rx.c \ + pipe.c \ + re-state-change.c \ + wimax.c + +libwimaxll_la_SOURCES = $(libwimaxll_sources) +# Trick automake +libwimaxll_la_CFLAGS = $(AM_CFLAGS) +# -version-info is CURRENT:REVISION:AGE +# REVISION: inc for changes that do not affect the external interface +# CURRENT: inc for added interfaces +# AGE: inc for removed/changed existing interfaces +libwimaxll_la_LDFLAGS = -version-info 0:0:0 $(LIBNL1_LIBS) +libwimaxll_a_SOURCES = $(libwimaxll_sources) + +# Workaround renaming of libwimax to libwimaxll +# +# Dirty, but works the best -- will be removed soon +install-exec-local: + $(mkdir_p) $(DESTDIR)/$(libdir) + (cd $(DESTDIR)/$(libdir) && ln -sf libwimaxll.so.0 libwimax.so.0) + (cd $(DESTDIR)/$(libdir) && ln -sf libwimaxll.so libwimax.so) diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 0000000..e6328f0 --- /dev/null +++ b/lib/Makefile.in @@ -0,0 +1,647 @@ +# Makefile.in generated by automake 1.10.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = lib +DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/config.h.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libdir)" +libLIBRARIES_INSTALL = $(INSTALL_DATA) +LIBRARIES = $(lib_LIBRARIES) +ARFLAGS = cru +libwimaxll_a_AR = $(AR) $(ARFLAGS) +libwimaxll_a_LIBADD = +am__objects_1 = op-open.$(OBJEXT) op-msg.$(OBJEXT) op-reset.$(OBJEXT) \ + op-rfkill.$(OBJEXT) mc_rx.$(OBJEXT) pipe.$(OBJEXT) \ + re-state-change.$(OBJEXT) wimax.$(OBJEXT) +am_libwimaxll_a_OBJECTS = $(am__objects_1) +libwimaxll_a_OBJECTS = $(am_libwimaxll_a_OBJECTS) +libLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(lib_LTLIBRARIES) +libwimaxll_la_LIBADD = +am__objects_2 = libwimaxll_la-op-open.lo libwimaxll_la-op-msg.lo \ + libwimaxll_la-op-reset.lo libwimaxll_la-op-rfkill.lo \ + libwimaxll_la-mc_rx.lo libwimaxll_la-pipe.lo \ + libwimaxll_la-re-state-change.lo libwimaxll_la-wimax.lo +am_libwimaxll_la_OBJECTS = $(am__objects_2) +libwimaxll_la_OBJECTS = $(am_libwimaxll_la_OBJECTS) +libwimaxll_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libwimaxll_la_CFLAGS) \ + $(CFLAGS) $(libwimaxll_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +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) +HEADERS = $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GREP = @GREP@ +I2400M_INCLUDES = @I2400M_INCLUDES@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBNL1_CFLAGS = @LIBNL1_CFLAGS@ +LIBNL1_LIBS = @LIBNL1_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NMEDIT = @NMEDIT@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I$(top_builddir)/include -I$(top_srcdir)/include \ + $(LIBNL1_CFLAGS) \ + $(I2400M_INCLUDES) + +noinst_HEADERS = debug.h internal.h +lib_LTLIBRARIES = libwimaxll.la +lib_LIBRARIES = libwimaxll.a +libwimaxll_sources = \ + op-open.c \ + op-msg.c \ + op-reset.c \ + op-rfkill.c \ + mc_rx.c \ + pipe.c \ + re-state-change.c \ + wimax.c + +libwimaxll_la_SOURCES = $(libwimaxll_sources) +# Trick automake +libwimaxll_la_CFLAGS = $(AM_CFLAGS) +# -version-info is CURRENT:REVISION:AGE +# REVISION: inc for changes that do not affect the external interface +# CURRENT: inc for added interfaces +# AGE: inc for removed/changed existing interfaces +libwimaxll_la_LDFLAGS = -version-info 0:0:0 $(LIBNL1_LIBS) +libwimaxll_a_SOURCES = $(libwimaxll_sources) +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign lib/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +config.h: stamp-h1 + @if test ! -f $@; then \ + rm -f stamp-h1; \ + $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \ + else :; fi + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status lib/config.h +$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_srcdir) && $(AUTOHEADER) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 +install-libLIBRARIES: $(lib_LIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(libLIBRARIES_INSTALL) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ + $(libLIBRARIES_INSTALL) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + else :; fi; \ + done + @$(POST_INSTALL) + @list='$(lib_LIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + p=$(am__strip_dir) \ + echo " $(RANLIB) '$(DESTDIR)$(libdir)/$$p'"; \ + $(RANLIB) "$(DESTDIR)$(libdir)/$$p"; \ + else :; fi; \ + done + +uninstall-libLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(libdir)/$$p'"; \ + rm -f "$(DESTDIR)$(libdir)/$$p"; \ + done + +clean-libLIBRARIES: + -test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES) +libwimaxll.a: $(libwimaxll_a_OBJECTS) $(libwimaxll_a_DEPENDENCIES) + -rm -f libwimaxll.a + $(libwimaxll_a_AR) libwimaxll.a $(libwimaxll_a_OBJECTS) $(libwimaxll_a_LIBADD) + $(RANLIB) libwimaxll.a +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libwimaxll.la: $(libwimaxll_la_OBJECTS) $(libwimaxll_la_DEPENDENCIES) + $(libwimaxll_la_LINK) -rpath $(libdir) $(libwimaxll_la_OBJECTS) $(libwimaxll_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwimaxll_la-mc_rx.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwimaxll_la-op-msg.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwimaxll_la-op-open.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwimaxll_la-op-reset.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwimaxll_la-op-rfkill.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwimaxll_la-pipe.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwimaxll_la-re-state-change.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwimaxll_la-wimax.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mc_rx.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/op-msg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/op-open.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/op-reset.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/op-rfkill.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pipe.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/re-state-change.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wimax.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libwimaxll_la-op-open.lo: op-open.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-op-open.lo -MD -MP -MF $(DEPDIR)/libwimaxll_la-op-open.Tpo -c -o libwimaxll_la-op-open.lo `test -f 'op-open.c' || echo '$(srcdir)/'`op-open.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libwimaxll_la-op-open.Tpo $(DEPDIR)/libwimaxll_la-op-open.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='op-open.c' object='libwimaxll_la-op-open.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_la_CFLAGS) $(CFLAGS) -c -o libwimaxll_la-op-open.lo `test -f 'op-open.c' || echo '$(srcdir)/'`op-open.c + +libwimaxll_la-op-msg.lo: op-msg.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-op-msg.lo -MD -MP -MF $(DEPDIR)/libwimaxll_la-op-msg.Tpo -c -o libwimaxll_la-op-msg.lo `test -f 'op-msg.c' || echo '$(srcdir)/'`op-msg.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libwimaxll_la-op-msg.Tpo $(DEPDIR)/libwimaxll_la-op-msg.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='op-msg.c' object='libwimaxll_la-op-msg.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_la_CFLAGS) $(CFLAGS) -c -o libwimaxll_la-op-msg.lo `test -f 'op-msg.c' || echo '$(srcdir)/'`op-msg.c + +libwimaxll_la-op-reset.lo: op-reset.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-op-reset.lo -MD -MP -MF $(DEPDIR)/libwimaxll_la-op-reset.Tpo -c -o libwimaxll_la-op-reset.lo `test -f 'op-reset.c' || echo '$(srcdir)/'`op-reset.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libwimaxll_la-op-reset.Tpo $(DEPDIR)/libwimaxll_la-op-reset.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='op-reset.c' object='libwimaxll_la-op-reset.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_la_CFLAGS) $(CFLAGS) -c -o libwimaxll_la-op-reset.lo `test -f 'op-reset.c' || echo '$(srcdir)/'`op-reset.c + +libwimaxll_la-op-rfkill.lo: op-rfkill.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-op-rfkill.lo -MD -MP -MF $(DEPDIR)/libwimaxll_la-op-rfkill.Tpo -c -o libwimaxll_la-op-rfkill.lo `test -f 'op-rfkill.c' || echo '$(srcdir)/'`op-rfkill.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libwimaxll_la-op-rfkill.Tpo $(DEPDIR)/libwimaxll_la-op-rfkill.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='op-rfkill.c' object='libwimaxll_la-op-rfkill.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_la_CFLAGS) $(CFLAGS) -c -o libwimaxll_la-op-rfkill.lo `test -f 'op-rfkill.c' || echo '$(srcdir)/'`op-rfkill.c + +libwimaxll_la-mc_rx.lo: mc_rx.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-mc_rx.lo -MD -MP -MF $(DEPDIR)/libwimaxll_la-mc_rx.Tpo -c -o libwimaxll_la-mc_rx.lo `test -f 'mc_rx.c' || echo '$(srcdir)/'`mc_rx.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libwimaxll_la-mc_rx.Tpo $(DEPDIR)/libwimaxll_la-mc_rx.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mc_rx.c' object='libwimaxll_la-mc_rx.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_la_CFLAGS) $(CFLAGS) -c -o libwimaxll_la-mc_rx.lo `test -f 'mc_rx.c' || echo '$(srcdir)/'`mc_rx.c + +libwimaxll_la-pipe.lo: pipe.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-pipe.lo -MD -MP -MF $(DEPDIR)/libwimaxll_la-pipe.Tpo -c -o libwimaxll_la-pipe.lo `test -f 'pipe.c' || echo '$(srcdir)/'`pipe.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libwimaxll_la-pipe.Tpo $(DEPDIR)/libwimaxll_la-pipe.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pipe.c' object='libwimaxll_la-pipe.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_la_CFLAGS) $(CFLAGS) -c -o libwimaxll_la-pipe.lo `test -f 'pipe.c' || echo '$(srcdir)/'`pipe.c + +libwimaxll_la-re-state-change.lo: re-state-change.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-re-state-change.lo -MD -MP -MF $(DEPDIR)/libwimaxll_la-re-state-change.Tpo -c -o libwimaxll_la-re-state-change.lo `test -f 're-state-change.c' || echo '$(srcdir)/'`re-state-change.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libwimaxll_la-re-state-change.Tpo $(DEPDIR)/libwimaxll_la-re-state-change.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='re-state-change.c' object='libwimaxll_la-re-state-change.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_la_CFLAGS) $(CFLAGS) -c -o libwimaxll_la-re-state-change.lo `test -f 're-state-change.c' || echo '$(srcdir)/'`re-state-change.c + +libwimaxll_la-wimax.lo: wimax.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-wimax.lo -MD -MP -MF $(DEPDIR)/libwimaxll_la-wimax.Tpo -c -o libwimaxll_la-wimax.lo `test -f 'wimax.c' || echo '$(srcdir)/'`wimax.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libwimaxll_la-wimax.Tpo $(DEPDIR)/libwimaxll_la-wimax.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='wimax.c' object='libwimaxll_la-wimax.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_la_CFLAGS) $(CFLAGS) -c -o libwimaxll_la-wimax.lo `test -f 'wimax.c' || echo '$(srcdir)/'`wimax.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) $(HEADERS) config.h +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLIBRARIES clean-libLTLIBRARIES \ + clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-hdr distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-exec-am: install-exec-local install-libLIBRARIES \ + install-libLTLIBRARIES + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLIBRARIES uninstall-libLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLIBRARIES clean-libLTLIBRARIES clean-libtool ctags \ + distclean distclean-compile distclean-generic distclean-hdr \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-exec-local install-html \ + install-html-am install-info install-info-am \ + install-libLIBRARIES install-libLTLIBRARIES install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am \ + uninstall-libLIBRARIES uninstall-libLTLIBRARIES + + +# Workaround renaming of libwimax to libwimaxll +# +# Dirty, but works the best -- will be removed soon +install-exec-local: + $(mkdir_p) $(DESTDIR)/$(libdir) + (cd $(DESTDIR)/$(libdir) && ln -sf libwimaxll.so.0 libwimax.so.0) + (cd $(DESTDIR)/$(libdir) && ln -sf libwimaxll.so libwimax.so) +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/config.h.in b/lib/config.h.in new file mode 100644 index 0000000..b324b59 --- /dev/null +++ b/lib/config.h.in @@ -0,0 +1,58 @@ +/* lib/config.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have <linux/wimax.h>. */ +#undef HAVE_WIMAX_H + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION diff --git a/lib/debug.h b/lib/debug.h new file mode 100644 index 0000000..5d9fed7 --- /dev/null +++ b/lib/debug.h @@ -0,0 +1,114 @@ +/* + * Linux WiMax + * User Space API Debug Support + * + * + * 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. + * + * + * Simple debug printing macros + * + * FIXME: doc + * Invoke like: + * + * #define D_LOCAL 4 + * #include "debug.h" + * + * At the end of your include files. + */ +#include <wimaxll.h> + +/* Master debug switch; !0 enables, 0 disables */ +#define D_MASTER (!0) + +/* Local (per-file) debug switch; #define before #including */ +#ifndef D_LOCAL +#define D_LOCAL 0 +#endif + +#undef __d_printf +#undef d_fnstart +#undef d_fnend +#undef d_printf +#undef d_dump + +static inline +void __d_dev_head(char *head, size_t size, const struct wimaxll_handle *_dev) +{ + if (_dev == NULL) + snprintf(head, size, "libwimax: "); + else if ((unsigned long)_dev < 4096) { + fprintf(stderr, "libwimax: E: Corrupt " + "device handle %p\n", _dev); + snprintf(head, size, "libwimax[dev_n/a]: "); + } else + snprintf(head, size, + "libwimax[%s]: ", _dev->name); +} + + +#define __d_printf(l, _tag, _dev, f, a...) \ +do { \ + const struct wimaxll_handle *__dev = (_dev); \ + if (D_MASTER && D_LOCAL >= (l)) { \ + char __head[64] = ""; \ + __d_dev_head(__head, sizeof(__head), __dev); \ + fprintf(stderr, "%s%s" _tag ": " f, __head, \ + __func__, ## a); \ + } \ +} while (0 && _dev) + +#define d_fnstart(l, _dev, f, a...) __d_printf(l, " FNSTART", _dev, f, ## a) +#define d_fnend(l, _dev, f, a...) __d_printf(l, " FNEND", _dev, f, ## a) +#define d_printf(l, _dev, f, a...) __d_printf(l, "", _dev, f, ## a) +#define d_test(l) (D_MASTER && D_LOCAL >= (l)) + +static inline +void __d_dump(const struct wimaxll_handle *dev, + const void *_ptr, size_t size) +{ + const unsigned char *ptr = _ptr; + char str[64]; + size_t cnt, itr; + for (itr = cnt = 0; cnt < size; cnt++) { + itr += snprintf(str + itr, sizeof(str) - itr, + "%02x ", ptr[cnt]); + if ((cnt > 0 && (cnt + 1) % 8 == 0) || (cnt == size - 1)) { + __d_printf(D_LOCAL, "", dev, "%s\n", str); + itr = 0; + } + } +} + +#define d_dump(l, dev, ptr, size) \ +do { \ + if (d_test(l)) \ + __d_dump(dev, ptr, size); \ +} while (0) diff --git a/lib/internal.h b/lib/internal.h new file mode 100644 index 0000000..57dbd32 --- /dev/null +++ b/lib/internal.h @@ -0,0 +1,187 @@ +/* + * Linux WiMax + * Internal API and declarations + * + * + * 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 __lib_internal_h__ +#define __lib_internal_h__ + +enum { +#define __WIMAXLL_IFNAME_LEN 32 + /** + * WIMAXLL_IFNAME_LEN - Maximum size of a wimax interface + * name. + */ + WIMAXLL_IFNAME_LEN = __WIMAXLL_IFNAME_LEN, + /** + * WMAX_MC_MAX - Maximum number of multicast groups that a + * WiMAX interface can offer (this doesn't count the + * reports group, which is separate). + */ + WIMAXLL_MC_MAX = 5, +}; + + +struct wimaxll_mc_handle; + + +/** + * A description of a generic netlink multicast group + * + * \param name Name of the group + * \param id ID of the group + */ +struct wimaxll_mc_group { + char name[GENL_NAMSIZ]; + int id; + struct wimaxll_mc_handle *mch; +}; + + +/** + * A WiMax control pipe handle + * + * This type is opaque to the user + * + * \internal + * + * In order to simplify multithread support, we use to different \a + * libnl handles, one for sending to the kernel, one (for each pipe + * open to a multicast group) for reading from the kernel. This allows + * us to parallelize \c wimaxll_msg_write() and \c wimaxll_msg_read() at + * the same time in a multithreaded environment. + * + * FIXME: this needs some rewriting + * + * \param nlh_tx handle for writing to the kernel. + * Internal note: You \b have \b to set the handlers for + * %NL_CB_VALID and nl_cb_err() callbacks, as each callsite will + * do it to suit their needs. See wimaxll_rfkill() for an + * example. Any other callback you are supposed to restore to what + * it was before. + * \param gnl_family_id Generic Netlink Family ID assigned to the device + * \param mc_msg Index in the \a gnl_mc array of the "msg" + * multicast group. + * \param name name of the wimax interface + * \param gnl_mc Array of information about the different multicast + * groups supported by the device. At least the "msg" group is + * always supported. The rest are optional and depend on what the + * driver implements. + */ +struct wimaxll_handle { + struct nl_handle *nlh_tx; + int gnl_family_id; + unsigned mc_msg; + char name[__WIMAXLL_IFNAME_LEN]; + struct wimaxll_mc_group gnl_mc[WIMAXLL_MC_MAX]; +}; + + +/** + * Multicast group handle + * + * \internal + * + * This structure encapsulates all that we need to read from a single + * multicast group. We could have a single handle for doing all, but + * by definition of the interface, different multicast groups carry + * different traffic (with different needs). Rather than multiplex it + * here, we multiplex at the kernel by sending it via an specific pipe + * that knows how to handle it already. + * + * This way the driver can define it's own private pipes (if needed) + * for high bandwidth traffic (for example, tracing information) + * without affecting the rest of the groups (channels). + */ +struct wimaxll_mc_handle { + int idx; + struct wimaxll_handle *wmx; + struct nl_handle *nlh_rx; + struct nl_cb *nl_cb; + ssize_t result; + + wimaxll_msg_to_user_cb_f msg_to_user_cb; + struct wimaxll_gnl_cb_context *msg_to_user_context; + + wimaxll_state_change_cb_f state_change_cb; + struct wimaxll_gnl_cb_context *state_change_context; +}; + + +static inline +void wimaxll_mch_maybe_set_result(struct wimaxll_mc_handle *mch, int val) +{ + if (mch->result == -EINPROGRESS) + mch->result = val; +} + + +/* Utilities */ +ssize_t wimaxll_wait_for_rp_result(struct wimaxll_handle *); +int wimaxll_wait_for_ack(struct wimaxll_handle *); +int wimaxll_gnl_handle_msg_to_user(struct wimaxll_handle *, + struct wimaxll_mc_handle *, + struct nl_msg *); +int wimaxll_gnl_handle_state_change(struct wimaxll_handle *, + struct wimaxll_mc_handle *, + struct nl_msg *); +int wimaxll_gnl_error_cb(struct sockaddr_nl *, struct nlmsgerr *, void *); +struct wimaxll_mc_handle *__wimaxll_get_mc_handle(struct wimaxll_handle *, + int pipe_id); + + +#define wimaxll_container_of(pointer, type, member) \ +({ \ + type *object = NULL; \ + size_t offset = (void *) &object->member - (void *) object; \ + (type *) ((void *) pointer - offset); \ +}) + + +/* + * wimaxll_family_id - Return the associated Generic Netlink family ID + * + * @wmx: WiMax interface for which to provide the ID. + */ +static inline +int wimaxll_family_id(struct wimaxll_handle *wmx) +{ + return wmx->gnl_family_id; +} + + +void wimaxll_msg(struct wimaxll_handle *, const char *fmt, ...) + __attribute__ ((format(printf, 2, 3))); + +#endif /* #ifndef __lib_internal_h__ */ diff --git a/lib/libwimax.so.in b/lib/libwimax.so.in new file mode 100644 index 0000000..8f3f8ba --- /dev/null +++ b/lib/libwimax.so.in @@ -0,0 +1,3 @@ +/* This library is deprecated!!!! + In the meantime, do binary compat using aliases. */ +GROUP ( @libdir@/libwimaxll.so ) diff --git a/lib/mc_rx.c b/lib/mc_rx.c new file mode 100644 index 0000000..c34d687 --- /dev/null +++ b/lib/mc_rx.c @@ -0,0 +1,535 @@ +/* + * Linux WiMax + * Framework for reading from multicast groups + * + * + * 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 mc_rx Reading form Generic Netlink multicast groups + * + * The WiMAX stack sends asynchronous traffic (notifications and + * messages) to user space through Generic Netlink multicast groups; + * thus, when reading that traffic from the kernel, libwimaxll + * actually reads from a generic netlink multicast group. + * + * This allows the kernel to send a single notification that can be + * received by an undetermined (and unbound) number of listeners. As + * well, this also allows a very flexible way to multiplex different + * channels without affecting all the listeners. + * + * What is called a \e pipe is mapped over one of these multicast + * groups. + * + * \b Example: + * + * If a driver wants to send tracing information to an application in + * user space for analysis, it can create a new \e pipe (Generic + * Netlink multicast group) and send it over there. + * + * The application can listen to it and its traffic volume won't + * affect other applications listening to other events coming from the + * same device. Some of these other applications could not be ready + * ready to cope with such a high traffic. + * + * If the same model were implemented just using different netlink + * messages, all applications listening to events from the driver + * would be awakened every time any kind of message were sent, even if + * they do not need to listen to some of those messages. + * + * \warning This is a \b very \b low level interface that is for + * internal use. + * + * \warning If you have to use it in an application, it probably means + * something is wrong. + * + * \warning You might want to use higher level messaging interfaces, + * such as the \ref the_pipe_interface_group "the pipe interface" + * or the \ref the_messaging_interface "the messaging interface". + * + * \section usage Usage + * + * The functions provided by this interface are almost identical than + * those of the \ref the_pipe_interface_group "pipe interface". The + * main difference is that wimaxll_mc_rx_read() operates at a lower + * level. + * + * \code + * int mc_handle; + * ssize_t bytes; + * ... + * mc_handle = wimaxll_mc_rx_open(wimaxll_handle, "name"); + * ... + * bytes = wimaxll_mc_rx_read(wimaxll_handle, mc_handle); + * ... + * wimaxll_mc_rx_close(wimaxll_handle, mc_handle); + * \endcode + * + * \a my_callback is a function that will be called for every valid + * message received from the kernel on a single call to + * wimaxll_mc_rx_read(). + * + * Internally, each \e open pipe/multicast-group contains the list of + * callbacks for each known message. This is used a look up table for + * executing them on reception. + * + * \section roadmap Roadmap + * + * \code + * + * wimaxll_mc_rx_open() + * wimaxll_mc_idx_by_name() + * + * wimaxll_mc_rx_read() + * nl_recvmsgs() + * wimaxll_seq_check_cb() + * wimaxll_gnl_error_cb() + * wimaxll_gnl_cb() + * wimaxll_gnl_handle_state_change() + * wimaxll_gnl_handle_msg_to_user() + * wimaxll_mch_maybe_set_result() + * + * wimaxll_mc_rx_fd() + * __wimaxll_mc_handle() + * wimaxll_mc_rx_close() + * \endcode + */ +#define _GNU_SOURCE +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <assert.h> +#include <linux/types.h> +#include <netlink/msg.h> +#include <netlink/genl/genl.h> +#include <wimaxll.h> +#include "internal.h" +#define D_LOCAL 0 +#include "debug.h" + + +static +/** + * Lookup the index for a named multicast group + * + * \param wmx WiMAX device handle + * \param name Name of the multicast group to lookup. + * \return On success, non-zero positive index for the multicast + * group; on error, negative errno code. + * + * Look up the index of the named multicast group in the cache + * obtained at wimaxll_open() time. + * + * \internal + * \ingroup mc_rx + * \fn int wimaxll_mc_idx_by_name(struct wimaxll_handle *wmx, const char *name) + */ +int wimaxll_mc_idx_by_name(struct wimaxll_handle *wmx, const char *name) +{ + unsigned cnt; + for (cnt = 0; cnt < WIMAXLL_MC_MAX; cnt++) + if (!strcmp(wmx->gnl_mc[cnt].name, name)) + return cnt; + return -EPROTONOSUPPORT; +} + + +/* + * Netlink callback for (disabled) sequence check + * + * When reading from multicast groups ignore the sequence check, as + * they are events (as indicated by the netlink documentation; see the + * documentation on nl_disable_sequence_check(), for example here: + * http://people.suug.ch/~tgr/libnl/doc-1.1/ + * group__socket.html#g0ff2f43147e3a4547f7109578b3ca422). + * + * We need to do this \e manually, as we are using a new callback set + * group and thus the libnl defaults set by + * nl_disable_sequence_check() don't apply. + */ +static +int wimaxll_seq_check_cb(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + + +static +/** + * Callback to process a (succesful) message coming from generic + * netlink + * + * \internal + * + * Called by nl_recvmsgs() when a valid message is received. We + * multiplex and handle messages that are known to the library. If the + * message is unknown, do nothing other than setting -ENODATA. + * + * When reading from a pipe with wimaxll_pipe_read(), -ENODATA is + * considered a retryable error -- effectively, the message is + * skipped. + * + * \fn int wimaxll_gnl_cb(struct nl_msg *msg, void *_mch) + */ +int wimaxll_gnl_cb(struct nl_msg *msg, void *_mch) +{ + ssize_t result; + struct wimaxll_mc_handle *mch = _mch; + struct wimaxll_handle *wmx = mch->wmx; + struct nlmsghdr *nl_hdr; + struct genlmsghdr *gnl_hdr; + + d_fnstart(7, wmx, "(msg %p mch %p)\n", msg, mch); + nl_hdr = nlmsg_hdr(msg); + gnl_hdr = nlmsg_data(nl_hdr); + + if (gnl_hdr->cmd >= WIMAX_GNL_OP_MAX) + goto error_unknown_msg; + + switch (gnl_hdr->cmd) { + case WIMAX_GNL_OP_MSG_TO_USER: + if (mch->msg_to_user_cb) + result = wimaxll_gnl_handle_msg_to_user(wmx, mch, msg); + else + goto out_no_handler; + break; + case WIMAX_GNL_RE_STATE_CHANGE: + if (mch->state_change_cb) + result = wimaxll_gnl_handle_state_change(wmx, mch, msg); + else + goto out_no_handler; + break; + default: + goto error_unknown_msg; + } + wimaxll_mch_maybe_set_result(mch, 0); + d_fnend(7, wmx, "(msg %p mch %p) = %zd\n", msg, mch, result); + return result; + +error_unknown_msg: + d_printf(1, wmx, "E: %s: received unknown gnl message %d\n", + __func__, gnl_hdr->cmd); +out_no_handler: + wimaxll_mch_maybe_set_result(mch, -ENODATA); + result = NL_SKIP; + d_fnend(7, wmx, "(msg %p mch %p) = %zd\n", msg, mch, result); + return result; +} + + +/** + * Open a handle for reception from a multicast group + * + * \param wmx WiMAX device handle + * \param mc_name Name of the multicast group that has to be opened + * + * \return If successful, a non-negative handle number (\e { the + * multicast group descriptor}), to be given to other functions + * for actual operation. In case of error, a negative errno code. + * + * Allocates a handle to use for reception of data on from a single + * multicast group. + * + * Only one handle may be opened at the same time to each multicast + * group. + * + * \ingroup mc_rx + */ +int wimaxll_mc_rx_open(struct wimaxll_handle *wmx, + const char *mc_name) +{ + int result, idx; + struct wimaxll_mc_handle *mch; + + d_fnstart(3, wmx, "(wmx %p mc_name %s)\n", wmx, mc_name); + idx = wimaxll_mc_idx_by_name(wmx, mc_name); + if (idx < 0) { + result = idx; + wimaxll_msg(wmx, "E: mc group \"%s\" " + "not supported: %d\n", mc_name, result); + goto error_mc_idx_by_name; + } + d_printf(2, wmx, "D: idx is %d\n", idx); + result = -EBUSY; + if (wmx->gnl_mc[idx].mch) { + wimaxll_msg(wmx, "E: BUG! trying to open handle to multicast " + "group \"%s\", which is already open\n", mc_name); + goto error_reopen; + } + + /* Alloc a new multicast group handle */ + result = -ENOMEM; + mch = malloc(sizeof(*mch)); + if (mch == NULL) { + wimaxll_msg(wmx, "E: mc group %s: cannot allocate handle\n", + mc_name); + goto error_alloc; + } + memset(mch, 0, sizeof(*mch)); + mch->wmx = wmx; + mch->nlh_rx = nl_handle_alloc(); + if (mch->nlh_rx == NULL) { + result = nl_get_errno(); + wimaxll_msg(wmx, "E: mc group %s: cannot allocate RX netlink " + "handle: %d\n", mc_name, result); + goto error_nl_handle_alloc_rx; + } + result = nl_connect(mch->nlh_rx, NETLINK_GENERIC); + if (result < 0) { + wimaxll_msg(wmx, "E: mc group %s: cannot connect RX netlink: " + "%d\n", mc_name, result); + goto error_nl_connect_rx; + } + + result = nl_socket_add_membership(mch->nlh_rx, wmx->gnl_mc[idx].id); + if (result < 0) { + wimaxll_msg(wmx, "E: mc group %s: cannot join multicast group " + "%u: %d\n", mc_name, wmx->gnl_mc[idx].id, result); + goto error_nl_add_membership; + } + + mch->nl_cb = nl_cb_alloc(NL_CB_VERBOSE); + if (mch->nl_cb == NULL) { + result = -ENOMEM; + wimaxll_msg(wmx, "E: mc group %s: cannot allocate callback\n", + mc_name); + goto error_cb_alloc; + } + + nl_cb_set(mch->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + wimaxll_seq_check_cb, NULL); + nl_cb_set(mch->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, wimaxll_gnl_cb, mch); + nl_cb_err(mch->nl_cb, NL_CB_CUSTOM, wimaxll_gnl_error_cb, mch); + wmx->gnl_mc[idx].mch = mch; + d_fnend(3, wmx, "(wmx %p mc_name %s) = %d\n", wmx, mc_name, idx); + return idx; + +error_cb_alloc: + /* No need to drop membership, it is removed when we close the + * handle */ +error_nl_add_membership: + nl_close(mch->nlh_rx); +error_nl_connect_rx: + nl_handle_destroy(mch->nlh_rx); +error_nl_handle_alloc_rx: + free(mch); +error_alloc: +error_reopen: +error_mc_idx_by_name: + d_fnend(3, wmx, "(wmx %p mc_name %s) = %d\n", wmx, mc_name, result); + return result; +} + + +/** + * Close a multicast group handle + * + * \param wmx WiMAX handle + * \param idx Multicast group handle (as returned by wimaxll_mc_rx_open()). + * + * Releases resources associated to open multicast group handle. + * + * \ingroup mc_rx + */ +void wimaxll_mc_rx_close(struct wimaxll_handle *wmx, unsigned idx) +{ + struct wimaxll_mc_handle *mch; + d_fnstart(3, wmx, "(wmx %p idx %u)\n", wmx, idx); + if (idx >= WIMAXLL_MC_MAX) { + wimaxll_msg(wmx, "E: BUG! multicast group index %u " + "higher than allowed maximum %u\n", + idx, WIMAXLL_MC_MAX); + goto out; + } + mch = wmx->gnl_mc[idx].mch; + wmx->gnl_mc[idx].mch = NULL; + nl_cb_put(mch->nl_cb); + /* No need to drop handle membership to the msg group, closing + * it does it */ + nl_close(mch->nlh_rx); + nl_handle_destroy(mch->nlh_rx); + free(mch); +out: + d_fnend(3, wmx, "(wmx %p idx %u) = void\n", wmx, idx); +} + + +/** + * Return the multicast group handle associated to a Pipe ID + * + * \internal + * + * \param wmx WiMAX device handle + * \param pipe_id Multicast group ID, as returned by + * wimaxll_mc_rx_open(). + * \return file descriptor associated to the multicast group, that can + * be fed to functions like select(). + * + * \ingroup mc_rx + */ +struct wimaxll_mc_handle *__wimaxll_get_mc_handle(struct wimaxll_handle *wmx, + int pipe_id) +{ + struct wimaxll_mc_handle *mch = NULL; + + if (pipe_id >= WIMAXLL_MC_MAX) { + wimaxll_msg(wmx, "E: BUG! mc group #%u does not exist!\n", + pipe_id); + goto error; + } + mch = wmx->gnl_mc[pipe_id].mch; + if (mch == NULL) { + wimaxll_msg(wmx, "E: BUG! trying to read from non-opened " + "mc group #%u\n", pipe_id); + goto error; + } +error: + return mch; +} + + +/** + * Return the file descriptor associated to a multicast group + * + * \param wmx WiMAX handle + * \param pipe_id Multicast group handle, as returned by + * wimaxll_mc_rx_open(). + * \return file descriptor associated to the multicast group, that can + * be fed to functions like select(). + * + * This allows to select() on the file descriptor, which will block + * until a message is available, that then can be read with + * wimaxll_mc_rx_read(). + * + * \ingroup mc_rx + */ +int wimaxll_mc_rx_fd(struct wimaxll_handle *wmx, unsigned pipe_id) +{ + int result = -EBADFD; + struct wimaxll_mc_handle *mch; + + d_fnstart(3, wmx, "(wmx %p pipe_id %u)\n", wmx, pipe_id); + mch = __wimaxll_get_mc_handle(wmx, pipe_id); + if (mch != NULL) + result = nl_socket_get_fd(mch->nlh_rx); + d_fnend(3, wmx, "(wmx %p pipe_id %u) = %zd\n", wmx, pipe_id, result); + return result; +} + + +/** + * Read from a multicast group + * + * \param wmx WiMAX device handle + * \param index Multicast group handle, as returned by + * wimaxll_mc_rx_open(). + * \return Value returned by the callback functions (depending on the + * implementation of the callback). On error, a negative errno + * code: + * + * -%EINPROGRESS: the message was not received. + * + * -%ENODATA: messages were received, but none of the known types. + * + * Read one or more messages from a multicast group and for each valid + * one, execute the callbacks set in the multi cast handle. + * + * The callbacks are expected to handle the messages and set + * information in the context specific to the mc handle + * (mch->cb_ctx). In case of any type of errors (cb_ctx.result < 0), + * it is expected that no resources will be tied to the context. + * + * \remarks This is a blocking call. + * + * \ingroup mc_rx + * + * \internal + * + * This calls nl_recvmsgs() on the handle specific to a multi-cast + * group; wimaxll_gnl_cb() will be called for succesfully received + * generic netlink messages from the kernel and execute the callbacks + * for each. + */ +ssize_t wimaxll_mc_rx_read(struct wimaxll_handle *wmx, unsigned index) +{ + ssize_t result; + struct wimaxll_mc_handle *mch; + + d_fnstart(3, wmx, "(wmx %p index %u)\n", wmx, index); + if (index >= WIMAXLL_MC_MAX) { + wimaxll_msg(wmx, "E: BUG! mc group #%u does not exist!\n", + index); + result = -EINVAL; + goto error_bad_index; + } + mch = wmx->gnl_mc[index].mch; + if (mch == NULL) { + wimaxll_msg(wmx, "E: BUG! trying to read from non-opened " + "mc group #%u\n", index); + result = -EBADF; + goto error_not_open; + } + + /* + * The reading and processing happens here + * + * libnl's nl_recvmsgs() will read and call the different + * callbacks we specified at wimaxll_mc_rx_open() time. That's + * where the processing of the message content is done. + */ + mch->result = -EINPROGRESS; + result = nl_recvmsgs(mch->nlh_rx, mch->nl_cb); + if (result < 0) { + wimaxll_msg(wmx, "E: %s: nl_recvmgsgs failed: %d\n", + __func__, result); + goto error_nl_recvmsgs; + } + result = mch->result; + if (result == -EINPROGRESS) { + wimaxll_msg(wmx, "E: %s: no messages parsed\n", __func__); + goto error_data; + } + /* No complains on error; the kernel might just be sending an + * error out; pass it through. */ +error_data: +error_nl_recvmsgs: +error_not_open: +error_bad_index: + d_fnend(3, wmx, "(wmx %p index %u) = %zd\n", wmx, index, result); + return result; +} + +void wimax_mc_rx_open() __attribute__ ((weak, alias("wimaxll_mc_rx_open"))); +void wimax_mc_rx_fd() __attribute__ ((weak, alias("wimaxll_mc_rx_fd"))); +void wimax_mc_rx_close() __attribute__ ((weak, alias("wimaxll_mc_rx_close"))); +void wimax_mc_rx_read() __attribute__ ((weak, alias("wimaxll_mc_rx_read"))); diff --git a/lib/op-msg.c b/lib/op-msg.c new file mode 100644 index 0000000..73a2daa --- /dev/null +++ b/lib/op-msg.c @@ -0,0 +1,565 @@ +/* + * Linux WiMax + * Messaging interface implementation + * + * + * 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 the_messaging_interface The message interface + * + * This is a payload agnostic message interface for communication + * between the WiMAX kernel drivers and user space applications. + * + * This interfaces builds on the \e kernel-to-user unidirectional \ref + * the_pipe_interface_group "pipe interface". It writes data to the default + * \e message pipe by sending it to the WiMAX kernel stack, which + * passes it to the driver using the \e wimax_dev->op_msg_from_user() + * call. + * + * Only the default \e message pipe is bidirectional; WiMAX kernel + * drivers receive messages sent with wimax_msg_write(). + * + * \note The wimaxll_msg_fd() and wimaxll_msg_read() functions operate + * on the default \e message pipe, being convenience functions for + * wimaxll_pipe_fd() and wimaxll_pipe_msg_read(). + * + * To wait for a message from the driver: + * + * @code + * void *msg; + * ... + * size = wimaxll_msg_read(wmx, &msg); + * @endcode + * + * Note this call is synchronous and blocking, and won't timeout. You + * can put it on a thread to emulate asynchrony (see \ref + * multithreading), but still it is quite difficult to integrate it in + * an event loop. Read on for mainloop integration options. + * + * In \e msg you get a pointer to a dynamically allocated (by \e + * libwimaxll) area with the message payload. When the application is + * done processing the message, call: + * + * @code + * wimaxll_msg_free(msg); + * @endcode + * + * To write messages to the driver: + * + * @code + * wimaxll_msg_write(wmx, buf, buf_size); + * @endcode + * + * where \a buf points to where the message is stored. + * + * \note Messages can be written to the driver \e only over the + * default \e message pipe. Thus, no wimax_pipe_msg_write() + * function is available. + * + * All functions return negative \a errno codes on error. + * + * To integrate message reception into a mainloop, \ref callbacks + * "callbacks" and select() should be used. The file descriptor + * associated to the default \e message \e pipe can be obtained with + * wimaxll_msg_fd(). When there is activity on the file descriptor, + * wimaxll_pipe_read() should be called on the default pipe: + * + * \code + * wimax_pipe_read(wmx, wimax_msg_pipe_id(wmx)); + * \endcode + * + * this will, as explained in \ref receiving, for each received + * notification, execute its callback. + * + * The callback for reception of messages from the WiMAX kernel stack + * can be set with wimaxll_pipe_set_cb_msg_to_user() (using as \e + * pipe_id the value returned by wimax_msg_pipe_id()). For detailed + * information on the message reception callback, see the definition + * of \ref wimaxll_msg_to_user_cb_f. + * + * The kernel WiMAX stack allows drivers to create any number of pipes + * on which to send information (messages) to user space. This + * interface provides means to read those messages, which are mostly + * device specific. + * + * This is a lower level interface than \ref the_messaging_interface + * "the messaging interface"; however, it operates similarly. + * + * @code + * void *msg; + * ... + * handle = wimaxll_pipe_open(wmx, "PIPENAME"); + * ... + * wimaxll_pipe_msg_read(wmx, handle, &msg); + * ... + * wimaxll_msg_free(msg); + * ... + * wimaxll_pipe_close(wmx, handle); + * @endcode + * + * More information about the details of this interface can be found + * \ref the_pipe_interface_group "here". + * + * \note These pipes are not bidirectional. + */ +#define _GNU_SOURCE +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <assert.h> +#include <linux/types.h> +#include <netlink/msg.h> +#include <netlink/genl/genl.h> +#include <wimaxll.h> +#include "internal.h" +#define D_LOCAL 0 +#include "debug.h" + + + +/** + * WIMAX_GNL_MSG_FROM_USER: policy specification + * + * \ingroup the_messaging_interface + * \internal + * + * Authoritative reference for this is at the kernel code, + * drivers/net/wimax/op-msg.c. + * + */ +static +struct nla_policy wimaxll_gnl_msg_from_user_policy[WIMAX_GNL_ATTR_MAX + 1] = { + [WIMAX_GNL_MSG_DATA] = { + .type = NLA_UNSPEC, + }, +}; + + +/** + * Callback to process an WIMAX_GNL_OP_MSG_TO_USER from the kernel + * + * \internal + * \ingroup the_messaging_interface + * + * \param wmx WiMAX device handle + * \param mch Pointer to \c struct wimaxll_mc_handle + * \param msg Pointer to netlink message + * \return \c enum nl_cb_action + * + * wimaxll_mc_rx_read() calls libnl's nl_recvmsgs() to receive messages; + * when a valid message is received, it goes into a loop that selects + * a callback to run for each type of message and it will call this + * function. + * + * This just expects a _MSG_TO_USER message, whose payload is what + * has to be passed to the caller. Because nl_recvmsgs() will free the + * message data, a new buffer has to be allocated and copied (a patch + * has been merged already to future versions of libnl that helps in + * this). + * + * It stores the buffer and size (or result in case of error) in the + * context passed in \e mch->msg_to_user_context. + */ +int wimaxll_gnl_handle_msg_to_user(struct wimaxll_handle *wmx, + struct wimaxll_mc_handle *mch, + struct nl_msg *msg) +{ + size_t size; + ssize_t result; + struct nlmsghdr *nl_hdr; + struct genlmsghdr *gnl_hdr; + struct nlattr *tb[WIMAX_GNL_ATTR_MAX+1]; + struct wimaxll_gnl_cb_context *ctx = mch->msg_to_user_context; + void *data; + + d_fnstart(7, wmx, "(wmx %p mch %p msg %p)\n", wmx, mch, msg); + nl_hdr = nlmsg_hdr(msg); + gnl_hdr = nlmsg_data(nl_hdr); + + assert(gnl_hdr->cmd == WIMAX_GNL_OP_MSG_TO_USER); + + /* Parse the attributes */ + result = genlmsg_parse(nl_hdr, 0, tb, WIMAX_GNL_ATTR_MAX, + wimaxll_gnl_msg_from_user_policy); + if (result < 0) { + wimaxll_msg(wmx, "E: %s: genlmsg_parse() failed: %d\n", + __func__, result); + wimaxll_cb_context_set_result(ctx, result); + result = NL_SKIP; + goto error_parse; + } + if (tb[WIMAX_GNL_MSG_DATA] == NULL) { + wimaxll_msg(wmx, "E: %s: cannot find MSG_DATA attribute\n", + __func__); + wimaxll_cb_context_set_result(ctx, -ENXIO); + result = NL_SKIP; + goto error_no_attrs; + + } + result = (ssize_t) nla_get_u64(tb[WIMAX_GNL_RESULT_CODE]); + wimaxll_cb_context_set_result(ctx, result); + + size = nla_len(tb[WIMAX_GNL_MSG_DATA]); + data = nla_data(tb[WIMAX_GNL_MSG_DATA]); + + d_printf(1, wmx, "D: CRX genlmsghdr cmd %u version %u\n", + gnl_hdr->cmd, gnl_hdr->version); + d_printf(1, wmx, "D: CRX msg from kernel %u bytes\n", size); + d_dump(2, wmx, data, size); + + /* This was set by whoever called nl_recmvsgs (or + * wimaxll_mc_rx_read() or wimaxll_pipe_read()) */ + if (mch->msg_to_user_cb(wmx, ctx, data, size) == -EBUSY) + result = NL_STOP; + else + result = NL_OK; +error_no_attrs: +error_parse: + d_fnend(7, wmx, "(wmx %p mch %p msg %p) = %d\n", wmx, mch, msg, result); + return result; +} + + +struct wimaxll_cb_msg_to_user_context { + struct wimaxll_gnl_cb_context ctx; + void *data; +}; + + +/* + * Default handling of messages + * + * When someone calls wimaxll_msg_read() or wimaxll_pipe_msg_read(), those + * functions set this default callback, which will just copy the data + * to a buffer and pass that pointer to the caller along with the size. + */ +static +int wimaxll_cb_msg_to_user(struct wimaxll_handle *wmx, + struct wimaxll_gnl_cb_context *ctx, + const char *data, size_t data_size) +{ + struct wimaxll_cb_msg_to_user_context *mtu_ctx = + wimaxll_container_of( + ctx, struct wimaxll_cb_msg_to_user_context, ctx); + + if (mtu_ctx->data) + return -EBUSY; + mtu_ctx->data = malloc(data_size); + if (mtu_ctx->data) { + memcpy(mtu_ctx->data, data, data_size); + ctx->result = data_size; + } else + ctx->result = -ENOMEM; + return 0; +} + + +/** + * Read a message from any WiMAX kernel-user pipe + * + * \param wmx WiMAX device handle + * \param pipe_id Pipe to read from (as returned by + * wimaxll_pipe_open()). To use the default pipe, indicate + * use wimax_msg_pipe_id(). + * \param buf Somewhere where to store the pointer to the message data. + * \return If successful, a positive (and \c *buf set) or zero size of + * the message; on error, a negative \a errno code (\c buf + * n/a). + * + * Returns a message allocated in \c *buf as sent by the kernel via + * the indicated pipe. The message is allocated by the + * library and owned by the caller. When done, it has to be freed with + * wimaxll_msg_free() to release the space allocated to it. + * + * \note This is a blocking call. + * + * \ingroup the_messaging_interface + */ +ssize_t wimaxll_pipe_msg_read(struct wimaxll_handle *wmx, unsigned pipe_id, + void **buf) +{ + ssize_t result; + struct wimaxll_cb_msg_to_user_context mtu_ctx = { + .ctx = WIMAXLL_GNL_CB_CONTEXT_INIT(wmx), + .data = NULL, + }; + wimaxll_msg_to_user_cb_f prev_cb = NULL; + struct wimaxll_gnl_cb_context *prev_priv = NULL; + + d_fnstart(3, wmx, "(wmx %p buf %p)\n", wmx, buf); + wimaxll_pipe_get_cb_msg_to_user(wmx, pipe_id, &prev_cb, &prev_priv); + wimaxll_pipe_set_cb_msg_to_user(wmx, pipe_id, + wimaxll_cb_msg_to_user, &mtu_ctx.ctx); + result = wimaxll_pipe_read(wmx, pipe_id); + if (result >= 0) { + *buf = mtu_ctx.data; + result = mtu_ctx.ctx.result; + } + wimaxll_pipe_set_cb_msg_to_user(wmx, pipe_id, prev_cb, prev_priv); + d_fnend(3, wmx, "(wmx %p buf %p) = %zd\n", wmx, buf, result); + return result; +} + + +/** + * Free a message received with wimaxll_pipe_msg_read() or + * wimaxll_msg_read() + * + * \param msg message pointer returned by wimaxll_pipe_msg_read() or + * wimaxll_msg_read(). + * + * \note this function is the same as wimaxll_msg_free() + * + * \ingroup the_messaging_interface + */ +void wimaxll_pipe_msg_free(void *msg) +{ + d_fnstart(3, NULL, "(msg %p)\n", msg); + free(msg); + d_fnend(3, NULL, "(msg %p) = void\n", msg); +} + + +/** + * Return the file descriptor associated to the default \e message pipe + * + * \param wmx WiMAX device handle + * \return file descriptor associated to the messaging group, that can + * be fed to functions like select(). + * + * This allows to select() on the file descriptor, which will block + * until a message is available, that then can be read with + * wimaxll_pipe_read(). + * + * \ingroup the_messaging_interface + */ +int wimaxll_msg_fd(struct wimaxll_handle *wmx) +{ + return wimaxll_mc_rx_fd(wmx, wmx->mc_msg); +} + + +/** + * Read a message from the WiMAX default \e message pipe. + * + * \param wmx WiMAX device handle + * \param buf Somewhere where to store the pointer to the message data. + * \return If successful, a positive (and \c *buf set) or zero size of + * the message; on error, a negative \a errno code (\c buf + * n/a). + * + * Returns a message allocated in \c *buf as sent by the kernel via + * the default \e message pipe. The message is allocated by the + * library and owned by the caller. When done, it has to be freed with + * wimaxll_msg_free() to release the space allocated to it. + * + * \note This is a blocking call. + * + * \ingroup the_messaging_interface + */ +ssize_t wimaxll_msg_read(struct wimaxll_handle *wmx, void **buf) +{ + return wimaxll_pipe_msg_read(wmx, wmx->mc_msg, buf); +} + + +/** + * Free a message received with wimaxll_pipe_msg_read() or + * wimaxll_msg_read() + * + * \param msg message pointer returned by wimaxll_pipe_msg_read() or + * wimaxll_msg_read(). + * + * \note this function is the same as wimaxll_pipe_msg_free() + * + * \ingroup the_messaging_interface + */ +void wimaxll_msg_free(void *msg) +{ + wimaxll_pipe_msg_free(msg); +} + + +/** + * Send a driver-specific message to a WiMAX device + * + * \param wmx wimax device descriptor + * \param buf Pointer to the wimax message. + * \param size size of the message. + * + * \return 0 if ok < 0 errno code on error. On error it is assumed + * the message wasn't delivered. + * + * Sends a data buffer down to the kernel driver. The format of the + * message is driver specific. + * + * \note This is a blocking call + * + * \ingroup the_messaging_interface + */ +ssize_t wimaxll_msg_write(struct wimaxll_handle *wmx, + const void *buf, size_t size) +{ + ssize_t result; + struct nl_msg *nl_msg; + void *msg; + + d_fnstart(3, wmx, "(wmx %p buf %p size %zu)\n", wmx, buf, size); + nl_msg = nlmsg_new(); + if (nl_msg == NULL) { + result = nl_get_errno(); + wimaxll_msg(wmx, "E: cannot allocate generic netlink " + "message: %m\n"); + goto error_msg_alloc; + } + msg = genlmsg_put(nl_msg, NL_AUTO_PID, NL_AUTO_SEQ, + wimaxll_family_id(wmx), 0, 0, + WIMAX_GNL_OP_MSG_FROM_USER, WIMAX_GNL_VERSION); + if (msg == NULL) { + result = nl_get_errno(); + wimaxll_msg(wmx, "E: %s: error preparing message: %d\n", + __func__, result); + goto error_msg_prep; + } + + nla_put(nl_msg, WIMAX_GNL_MSG_DATA, size, buf); + + d_printf(5, wmx, "D: CTX nl + genl header:\n"); + d_dump(5, wmx, nlmsg_hdr(nl_msg), + sizeof(struct nlmsghdr) + sizeof(struct genlmsghdr)); + d_printf(5, wmx, "D: CTX wimax message:\n"); + d_dump(5, wmx, buf, size); + + result = nl_send_auto_complete(wmx->nlh_tx, nl_msg); + if (result < 0) { + wimaxll_msg(wmx, "E: error sending message: %d\n", result); + goto error_msg_send; + } + + result = wimaxll_wait_for_ack(wmx); /* Get the ACK from netlink */ + if (result < 0) + wimaxll_msg(wmx, "E: %s: generic netlink ack failed: %d\n", + __func__, result); +error_msg_send: +error_msg_prep: + nlmsg_free(nl_msg); +error_msg_alloc: + d_fnend(3, wmx, "(wmx %p buf %p size %zu) = %zd\n", + wmx, buf, size, result); + return result; +} + + +/** + * Return the pipe ID for the messaging interface + * + * @param wmx WiMAX device descriptor + * @return Pipe id of the messaging interface, that can be used with + * the wimaxll_pipe_*() functions. + * + * \ingroup the_messaging_interface_group + */ +unsigned wimaxll_msg_pipe_id(struct wimaxll_handle *wmx) +{ + return wmx->mc_msg; +} + + +/** + * Get the callback and priv pointer for a MSG_TO_USER message + * + * \param wmx WiMAX handle. + * \param pipe_id Pipe on which to listen for the message [as returned + * by wimaxll_pipe_open()]. + * \param cb Where to store the current callback function. + * \param context Where to store the private data pointer passed to the + * callback. + * + * \ingroup the_messaging_interface_group + */ +void wimaxll_pipe_get_cb_msg_to_user( + struct wimaxll_handle *wmx, unsigned pipe_id, + wimaxll_msg_to_user_cb_f *cb, struct wimaxll_gnl_cb_context **context) +{ + struct wimaxll_mc_handle *mch; + + mch = __wimaxll_get_mc_handle(wmx, pipe_id); + if (mch != NULL) { + *cb = mch->msg_to_user_cb; + *context = mch->msg_to_user_context; + } +} + + +/** + * Set the callback and priv pointer for a MSG_TO_USER message + * + * \param wmx WiMAX handle. + * \param pipe_id Pipe on which to listen for the message [as returned + * by wimaxll_pipe_open()]. + * \param cb Callback function to set + * \param context Private data pointer to pass to the callback + * function (wrap a \a struct wimaxll_gnl_cb_context in your context + * struct and pass a pointer to it; then use wimaxll_container_of() + * to extract it back). + * + * \ingroup the_messaging_interface_group + */ +void wimaxll_pipe_set_cb_msg_to_user( + struct wimaxll_handle *wmx, unsigned pipe_id, + wimaxll_msg_to_user_cb_f cb, struct wimaxll_gnl_cb_context *context) +{ + struct wimaxll_mc_handle *mch; + + mch = __wimaxll_get_mc_handle(wmx, pipe_id); + if (mch != NULL) { + mch->msg_to_user_cb = cb; + mch->msg_to_user_context = context; + } +} + + +void wimax_msg_fd() __attribute__ ((weak, alias("wimaxll_msg_fd"))); +void wimax_msg_read() __attribute__ ((weak, alias("wimaxll_msg_read"))); +void wimax_msg_write() __attribute__ ((weak, alias("wimaxll_msg_write"))); +void wimax_msg_free() __attribute__ ((weak, alias("wimaxll_msg_free"))); +void wimax_msg_pipe_id() __attribute__ ((weak, alias("wimaxll_msg_pipe_id"))); +void wimax_pipe_msg_read() + __attribute__ ((weak, alias("wimaxll_pipe_msg_read"))); +void wimax_pipe_msg_free() + __attribute__ ((weak, alias("wimaxll_pipe_msg_free"))); +void wimax_pipe_get_cb_msg_to_user() + __attribute__ ((weak, alias("wimaxll_pipe_get_cb_msg_to_user"))); +void wimax_pipe_set_cb_msg_to_user() + __attribute__ ((weak, alias("wimaxll_pipe_set_cb_msg_to_user"))); diff --git a/lib/op-open.c b/lib/op-open.c new file mode 100644 index 0000000..d151284 --- /dev/null +++ b/lib/op-open.c @@ -0,0 +1,548 @@ +/* + * Linux WiMAX + * Opening handles + * + * + * 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 device_management WiMAX device management + * + * The main device management operations are wimaxll_open(), + * wimaxll_close() and wimax_reset(). + * + * It is allowed to have more than one handle opened at the same + * time. + * + * Use wimaxll_ifname() to obtain the name of the WiMAX interface a + * handle is open for. + * + * \internal + * \section Roadmap Roadmap + * + * \code + * wimaxll_open() + * __wimaxll_cmd_open() + * nl_recvmsgs() + * wimaxll_gnl_rp_ifinfo_cb() + * __wimaxll_ifinfo_parse_groups() + * wimaxll_gnl_error_cb() + * + * wimaxll_close() + * wimaxll_mc_rx_close() + * wimaxll_free() + * + * wimaxll_ifname() + * \endcode + */ +#define _GNU_SOURCE +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <assert.h> +#include <linux/types.h> +#include <netlink/msg.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <wimaxll.h> +#include "internal.h" +#define D_LOCAL 0 +#include "debug.h" + + +/** + * Generic Netlink: IFINFO message policy + * + * \internal + * \ingroup device_management + * + * Authoritative reference for this is in drivers/net/wimax/op-open.c; + * this is just a copy. + */ +static +struct nla_policy wimaxll_gnl_ifinfo_policy[WIMAX_GNL_ATTR_MAX + 1] = { + [WIMAX_GNL_IFINFO_MC_GROUPS] = { .type = NLA_NESTED }, + [WIMAX_GNL_IFINFO_MC_GROUP] = { .type = NLA_NESTED }, + [WIMAX_GNL_IFINFO_MC_NAME] = { + .type = NLA_STRING, + .maxlen = GENL_NAMSIZ + }, + [WIMAX_GNL_IFINFO_MC_ID] = { .type = NLA_U16 }, +}; + + +/** + * Callback context for processing IFINFO messages + * + * \ingroup device_management + * \internal + * + * All the callbacks store the information they parse from the + * messages here; we do it so that only once everything passes with + * flying colors, then we commit the information the kernel sent. + * + * We include a \a struct wimaxll_gnl_cb_context so we can share this + * same data structure with the error handler and not require to check + * different data in different places. + * + * gnl_mc stands for Generic Netlink MultiCast group + */ +struct wimaxll_ifinfo_context { + struct wimaxll_gnl_cb_context gnl; + struct wimaxll_mc_group gnl_mc[WIMAXLL_MC_MAX]; +}; + + +/** + * Parse the list of multicast groups sent as part of a IFINFO reply + * + * \ingroup device_management + * \internal + * + * \param ctx IFINFO context, containing the WiMAX handle + * + * \param nla_groups Netlink attribute (type + * WIMAXLL_GNL_IFINFO_MC_GROUPS) that contains the list of (nested) + * attributes (type WIMAXLL_GNL_IFINFO_MC_GROUP). + * Just pass tb[WIMAXLL_GNL_IFINFO_MC_GROUPS]; this function will + * check for it to be ok (non-NULL). + * + * \return 0 if ok, < 0 errno code on error. + * + * On success, the \a wmx->mc_group array will be cleared and + * overwritten with the list of reported groups (and their + * IDs). + * + * On error, nothing is modified. + * + * The kernel has sent a list of attributes: a nested attribute + * (_MC_GROUPS) containing a list of nested attributes (_MC_GROUP) + * each with a _MC_NAME and _MC_ID attribute]. + * + * We need to parse it and extract the multicast group data. At least + * a multicast group ('reports') has to exist; the rest are optional + * to a maximum of WIMAXLL_MC_MAX (without counting 'reports'). + * + * \note The private data for this callback is not an \a struct + * wimaxll_mc_handle as most of the other ones! + */ +static +int __wimaxll_ifinfo_parse_groups(struct wimaxll_ifinfo_context *ctx, + struct nlattr *nla_groups) +{ + int result, remaining, cnt = 0; + struct wimaxll_handle *wmx = ctx->gnl.wmx; + struct nlattr *nla_group; + + d_fnstart(7, wmx, "(ctx %p [wmx %p] nla_groups %p)\n", + ctx, wmx, nla_groups); + if (nla_groups == NULL) { + wimaxll_msg(wmx, "E: %s: the kernel didn't send a " + "WIMAXLL_GNL_IFINFO_MC_GROUPS attribute\n", + __func__); + result = -EBADR; + goto error_no_groups; + } + + memset(ctx->gnl_mc, 0, sizeof(ctx->gnl_mc)); + nla_for_each_nested(nla_group, nla_groups, remaining) { + char *name; + int id; + struct nlattr *tb[WIMAX_GNL_ATTR_MAX+1]; + + d_printf(8, wmx, "D: group %d, remaining %d\n", + cnt, remaining); + result = nla_parse_nested(tb, WIMAX_GNL_ATTR_MAX, + nla_group, + wimaxll_gnl_ifinfo_policy); + if (result < 0) { + wimaxll_msg(wmx, "E: %s: can't parse " + "WIMAX_GNL_MC_GROUP attribute: %d\n", + __func__, result); + continue; + } + + if (tb[WIMAX_GNL_IFINFO_MC_NAME] == NULL) { + wimaxll_msg(wmx, "E: %s: multicast group missing " + "WIMAX_GNL_IFINFO_MC_NAME attribute\n", + __func__); + continue; + } + name = nla_get_string(tb[WIMAX_GNL_IFINFO_MC_NAME]); + + if (tb[WIMAX_GNL_IFINFO_MC_ID] == NULL) { + wimaxll_msg(wmx, "E: %s: multicast group missing " + "WIMAX_GNL_IFINFO_MC_ID attribute\n", + __func__); + continue; + } + id = nla_get_u16(tb[WIMAX_GNL_IFINFO_MC_ID]); + + d_printf(6, wmx, "D: MC group %s:%d\n", name, id); + strncpy(ctx->gnl_mc[cnt].name, name, + sizeof(ctx->gnl_mc[cnt].name)); + ctx->gnl_mc[cnt].id = id; + cnt++; + } + result = 0; +error_no_groups: + d_fnend(7, wmx, "(ctx %p [wmx %p] nla_groups %p) = %d\n", + ctx, wmx, nla_groups, result); + return result; +} + + +/* + * Same as wimaxll_gnl_error_cb(), but takes a different type of + * context, so, need another one (fitting an mch in there made little + * sense). + */ +int wimaxll_gnl_rp_ifinfo_error_cb(struct sockaddr_nl *nla, + struct nlmsgerr *nlerr, + void *_ctx) +{ + struct wimaxll_ifinfo_context *ctx = _ctx; + struct wimaxll_handle *wmx = ctx->gnl.wmx; + + d_fnstart(7, wmx, "(nla %p nlnerr %p [%d] ctx %p)\n", + nla, nlerr, nlerr->error, _ctx); + if (ctx->gnl.result == -EINPROGRESS) + ctx->gnl.result = nlerr->error; + d_fnend(7, wmx, "(nla %p nlnerr %p [%d] ctx %p) = %d\n", + nla, nlerr, nlerr->error, _ctx, NL_STOP); + return NL_STOP; +} + + +/** + * Seek for and process a WIMAX_GNL_RP_IFINFO message from the kernel + * + * \ingroup device_management + * \internal + * + * \param msg Netlink message containing the reply + * \param _ctx Pointer to a \a struct wimaxll_gnl_cb_context where the + * context will be returned. + * + * \return 'enum nl_cb_action', NL_OK if there is no error, NL_STOP on + * error and _ctx possibly updated. + * + * This will take a received netlink message, check it is a \a + * WIMAX_GNL_RP_IFINFO, parse the contents and store them in an + * struct wimaxll_ifinfo_context (for the caller to use later on). + */ +static +int wimaxll_gnl_rp_ifinfo_cb(struct nl_msg *msg, void *_ctx) +{ + int result; + struct wimaxll_ifinfo_context *ctx = _ctx; + struct wimaxll_handle *wmx = ctx->gnl.wmx; + struct nlmsghdr *nl_hdr; + struct genlmsghdr *genl_hdr; + struct nlattr *tb[WIMAX_GNL_ATTR_MAX + 1]; + unsigned major, minor; + + d_fnstart(7, wmx, "(msg %p ctx %p)\n", msg, ctx); + nl_hdr = nlmsg_hdr(msg); + genl_hdr = nlmsg_data(nl_hdr); + + if (genl_hdr->cmd != WIMAX_GNL_RP_IFINFO) { + ctx->gnl.result = -ENXIO; + result = NL_SKIP; + d_printf(1, wmx, "D: ignoring unknown reply %d\n", + genl_hdr->cmd); + goto error_parse; + } + + /* Check version compatibility -- check include/linux/wimax.h + * for a complete description. The idea is to allow for good + * expandability of the interface without causing breakage. */ + major = genl_hdr->version / 10; + minor = genl_hdr->version % 10; + if (major != WIMAX_GNL_VERSION / 10) { + ctx->gnl.result = -EBADR; + result = NL_SKIP; + wimaxll_msg(wmx, "E: kernel's major WiMAX GNL interface " + "version (%d) is different that supported %d; " + "aborting\n", major, WIMAX_GNL_VERSION / 10); + goto error_bad_major; + } + if (minor < WIMAX_GNL_VERSION % 10) + wimaxll_msg(wmx, "W: kernel's minor WiMAX GNL interface " + "version (%d) is lower that supported %d; things " + "might not work\n", minor, WIMAX_GNL_VERSION % 10); + + /* Parse the attributes */ + result = genlmsg_parse(nl_hdr, 0, tb, WIMAX_GNL_IFINFO_MAX, + wimaxll_gnl_ifinfo_policy); + if (result < 0) { + wimaxll_msg(wmx, "E: %s: genlmsg_parse() failed: %d\n", + __func__, result); + ctx->gnl.result = result; + result = NL_SKIP; + goto error_parse; + } + result = __wimaxll_ifinfo_parse_groups(ctx, + tb[WIMAX_GNL_IFINFO_MC_GROUPS]); + if (result < 0) { + ctx->gnl.result = result; + result = NL_SKIP; + } else { + ctx->gnl.result = 0; + result = NL_OK; + } +error_parse: +error_bad_major: + d_fnend(7, wmx, "(msg %p ctx %p) = %d\n", msg, ctx, result); + return result; +} + + +static +int __wimaxll_cmd_open(struct wimaxll_handle *wmx) +{ + int result; + struct nl_msg *msg; + struct nl_cb *cb; + struct wimaxll_ifinfo_context ctx; + + d_fnstart(5, wmx, "(wmx %p)\n", wmx); + msg = nlmsg_new(); + if (msg == NULL) { + result = -errno; + wimaxll_msg(wmx, "E: %s: cannot allocate generic netlink " + "message: %m\n", __func__); + goto error_msg_alloc; + } + if (genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, + wimaxll_family_id(wmx), 0, 0, + WIMAX_GNL_OP_OPEN, WIMAX_GNL_VERSION) == NULL) { + result = -errno; + wimaxll_msg(wmx, "E: %s: error preparing message: %m\n", + __func__); + goto error_msg_prep; + } + result = nl_send_auto_complete(wmx->nlh_tx, msg); + if (result < 0) { + wimaxll_msg(wmx, "E: %s: error sending message: %d\n", + __func__, result); + goto error_msg_send; + } + + /* Read the reply, that includes a WIMAX_GNL_RP_IFINFO message + * + * We need to set the call back error handler, so we get the + * default cb handler and modify it. + */ + ctx.gnl.wmx = wmx; + ctx.gnl.result = -EINPROGRESS; + cb = nl_socket_get_cb(wmx->nlh_tx); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + wimaxll_gnl_rp_ifinfo_cb, &ctx); + nl_cb_err(cb, NL_CB_CUSTOM, wimaxll_gnl_rp_ifinfo_error_cb, &ctx.gnl); + nl_recvmsgs_default(wmx->nlh_tx); + nl_cb_put(cb); + result = ctx.gnl.result; + if (result >= 0) + nl_wait_for_ack(wmx->nlh_tx); + if (result == -EINPROGRESS) + wimaxll_msg(wmx, "E: %s: the kernel didn't reply with a " + "WIMAX_GNL_RP_IFINFO message\n", __func__); + d_printf(1, wmx, "D: processing result is %d\n", result); + + /* All fine and dandy, commit the data */ + memcpy(wmx->gnl_mc, ctx.gnl_mc, sizeof(ctx.gnl_mc)); +error_msg_prep: +error_msg_send: + nlmsg_free(msg); +error_msg_alloc: + d_fnend(5, wmx, "(wmx %p) = %d\n", wmx, result); + return result; +} + + +static +void wimaxll_free(struct wimaxll_handle *wmx) +{ + free(wmx); +} + + +/** + * Open a handle to the WiMAX control interface in the kernel + * + * \param device device name of the WiMAX network interface + * \return WiMAX device handle on success; on error, %NULL is returned + * and the \a errno variable is updated with a corresponding + * negative value. + * + * When opening the handle to the device, a basic check of API + * versioning will be done. If the kernel interface has a different + * major version, the \c wimaxll_open() call will fail (existing + * interfaces modified or removed). A higher kernel minor version is + * allowed (new interfaces added); a lower kernel minor version is not + * (the library needs interfaces that are not in the kernel). + * + * \ingroup device_management + * \internal + * + * Allocates the netlink handles needed to talk to the kernel. With + * that, looks up the Generic Netlink Family ID associated (if none, + * it's not a WiMAX device). This is done by querying generic netlink + * for a family called "WiMAX <DEVNAME>". + * + * An open command is issued to get and process interface information + * (like multicast group mappings, etc). This also does an interface + * versioning verification. + * + * The bulk of this code is in the parsing of the generic netlink + * reply that the \a WIMAX_GNL_OP_OPEN command returns (\a + * WIMAX_GNL_RP_IFINFO) with information about the WiMAX control + * interface. + * + * We process said reply using the \e libnl callback mechanism, + * invoked by __wimaxll_cmd_open(). All the information is stored in a + * struct wimaxll_ifinfo_context by the callbacks. When the callback + * (and thus message) processing finishes, __wimaxll_cmd_open(), if all + * successful, will commit the information from the context to the + * handle. On error, nothing is modified. + * + * \note Because events are going to ben processed, sequence checks + * have to be disabled (as indicated by the generic netlink + * documentation). + */ +struct wimaxll_handle *wimaxll_open(const char *device) +{ + int result; + struct wimaxll_handle *wmx; + char buf[64]; + + d_fnstart(3, NULL, "(device %s)\n", device); + result = ENOMEM; + wmx = malloc(sizeof(*wmx)); + if (wmx == NULL) { + wimaxll_msg(NULL, "E: cannot allocate WiMax handle: %m\n"); + goto error_gnl_handle_alloc; + } + memset(wmx, 0, sizeof(*wmx)); + strncpy(wmx->name, device, sizeof(wmx->name)); + + /* Setup the TX side */ + wmx->nlh_tx = nl_handle_alloc(); + if (wmx->nlh_tx == NULL) { + result = nl_get_errno(); + wimaxll_msg(wmx, "E: cannot open TX netlink handle: %d\n", + result); + goto error_nl_handle_alloc_tx; + } + result = nl_connect(wmx->nlh_tx, NETLINK_GENERIC); + if (result < 0) { + wimaxll_msg(wmx, "E: cannot connect TX netlink: %d\n", result); + goto error_nl_connect_tx; + } + + /* Lookup the generic netlink family */ + snprintf(buf, sizeof(buf), "WiMAX %s", wmx->name); + result = genl_ctrl_resolve(wmx->nlh_tx, buf); + if (result < 0) { + wimaxll_msg(wmx, "E: device %s presents no WiMAX interface; " + "it might not exist, not be be a WiMAX device or " + "support an interface unknown to libwimax: %d\n", + wmx->name, result); + goto error_ctrl_resolve; + } + wmx->gnl_family_id = result; + d_printf(1, wmx, "D: WiMAX device %s, genl family ID %d\n", + wmx->name, wmx->gnl_family_id); + + result = __wimaxll_cmd_open(wmx); /* Get interface information */ + if (result < 0) + goto error_cmd_open; + + result = wimaxll_mc_rx_open(wmx, "msg"); + if (result == -EPROTONOSUPPORT) /* not open? */ + wmx->mc_msg = WIMAXLL_MC_MAX; /* for wimaxll_mc_rx_read() */ + else if (result < 0) { + wimaxll_msg(wmx, "E: cannot open 'msg' multicast group: " + "%d\n", result); + goto error_msg_open; + } else + wmx->mc_msg = result; + d_fnend(3, wmx, "(device %s) = %p\n", device, wmx); + return wmx; + + wimaxll_mc_rx_close(wmx, wmx->mc_msg); +error_msg_open: +error_cmd_open: +error_ctrl_resolve: + nl_close(wmx->nlh_tx); +error_nl_connect_tx: + nl_handle_destroy(wmx->nlh_tx); +error_nl_handle_alloc_tx: + wimaxll_free(wmx); +error_gnl_handle_alloc: + errno = -result; + d_fnend(3, NULL, "(device %s) = NULL\n", device); + return NULL; +} + + +/** + * Close a device handle opened with wimaxll_open() + * + * \param wmx WiMAX device handle + * + * \ingroup device_management + * \internal + * + * Performs the natural oposite actions done in wimaxll_open(). All + * generic netlink multicast groups are destroyed, the netlink handle + * is closed and destroyed and finally, the actual handle is released. + */ +void wimaxll_close(struct wimaxll_handle *wmx) +{ + unsigned cnt; + + d_fnstart(3, NULL, "(wmx %p)\n", wmx); + for (cnt = 0; cnt < WIMAXLL_MC_MAX; cnt++) + if (wmx->gnl_mc[cnt].mch) + wimaxll_mc_rx_close(wmx, cnt); + nl_close(wmx->nlh_tx); + nl_handle_destroy(wmx->nlh_tx); + wimaxll_free(wmx); + d_fnend(3, NULL, "(wmx %p) = void\n", wmx); +} + +void wimax_open() __attribute__ ((weak, alias("wimaxll_open"))); +void wimax_close() __attribute__ ((weak, alias("wimaxll_close"))); diff --git a/lib/op-reset.c b/lib/op-reset.c new file mode 100644 index 0000000..7f402fd --- /dev/null +++ b/lib/op-reset.c @@ -0,0 +1,122 @@ +/* + * Linux WiMax + * Export the kernel's WiMAX stack wimaxll_reset() function + * + * + * 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. + */ +#define _GNU_SOURCE +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <assert.h> +#include <linux/types.h> +#include <netlink/msg.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <wimaxll.h> +#include "internal.h" +#define D_LOCAL 0 +#include "debug.h" + + +/** + * Reset a WiMAX device + * + * \param wmx WiMAX device handle + * + * \return result of the operation. + * + * - 0: wam reset suceeded + * - -ENODEV: warm reset failed and had to resort to a cold/bus + * reset; the device was disconnected from the system and the + * current handle is invalid and should be closed. + * - Any other negative error code: unrecoverable error, shutdown + * and go home + * + * When there is a need to reset the device wimaxll_reset() can be used + * to issue a warm reset. That won't invalidate the existing handles, + * while still moving the device to power on state. + * + * If the device cannot be properly reset, the WiMAX kernel stack may + * fall back to a cold reset, which will most likely disconnect the + * device from the driver (and bus) and reconnect it; that means + * device handles will be invalid from there on. In those cases, + * -ENODEV is returned. + * + * \note This call is synchronous; when success is returned, the + * device has completed its internal reset. + * + * \ingroup device_management + * \internal + * + * This implementation simply marshalls the call to the kernel's + * wimax_reset() and returns it's return code. + */ +int wimaxll_reset(struct wimaxll_handle *wmx) +{ + ssize_t result; + struct nl_msg *msg; + + msg = nlmsg_new(); + if (msg == NULL) { + result = errno; + wimaxll_msg(wmx, "E: RESET: cannot allocate generic netlink " + "message: %m\n"); + goto error_msg_alloc; + } + if (genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, + wimaxll_family_id(wmx), 0, 0, + WIMAX_GNL_OP_RESET, WIMAX_GNL_VERSION) == NULL) { + result = -ENOMEM; + wimaxll_msg(wmx, "E: RESET: error preparing message: " + "%d 0x%08x\n", result, result); + goto error_msg_prep; + } + result = nl_send_auto_complete(wmx->nlh_tx, msg); + if (result < 0) { + wimaxll_msg(wmx, "E: RESET: error sending message: %zd\n", + result); + goto error_msg_send; + } + /* Read the message ACK from netlink */ + result = wimaxll_wait_for_rp_result(wmx); + nl_wait_for_ack(wmx->nlh_tx); + if (result < 0) + wimaxll_msg(wmx, "E: RESET: operation failed: %zd\n", result); +error_msg_prep: +error_msg_send: + nlmsg_free(msg); +error_msg_alloc: + return result; +} diff --git a/lib/op-rfkill.c b/lib/op-rfkill.c new file mode 100644 index 0000000..a5695c9 --- /dev/null +++ b/lib/op-rfkill.c @@ -0,0 +1,129 @@ +/* + * Linux WiMax + * Export the kernel's WiMAX stack wimaxll_rfkill() function + * + * + * 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. + */ +#define _GNU_SOURCE +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <assert.h> +#include <linux/types.h> +#include <netlink/msg.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <wimaxll.h> +#include "internal.h" +#define D_LOCAL 0 +#include "debug.h" + + +/** + * Control the software RF Kill switch and obtain switch status + * + * \param wmx WiMAX device handle + * + * \param state State to which you want to toggle the sofware RF Kill + * switch (%WIMAX_RF_ON, %WIMAX_RF_OFF or %WIMAX_RF_QUERY for just + * querying the current state of the hardware and software + * switches). + * + * \return Negative errno code on error. Otherwise, radio kill switch + * status (bit 0 \e hw switch, bit 1 \e sw switch, \e 0 OFF, \e 1 + * ON): + * - 3 @c 0b11: Both HW and SW switches are \e on, radio is \e on + * - 2 @c 0b10: HW switch is \e off, radio is \e off + * - 1 @c 0b01: SW switch is \e on, radio is \e off + * - 0 @c 0b00: Both HW and SW switches are \e off, radio is \e off + * + * Allows the caller to control the state of the software RF Kill + * switch (if present) and in return, obtain the current status of + * both the hardware and software RF Kill switches. + * + * If there is no hardware or software switch, that switch is assumed + * to be always on (radio on). + * + * Changing the radio state might cause the device to change state, + * and cause the kernel to send reports indicating so. + * + * \note The state of the radio (\e ON or \e OFF) is the inverse of + * the state of the RF-Kill switch (\e enabled/on kills the + * radio, radio \e off; \e disabled/off allows the radio to + * work, radio \e on). + * + * \ingroup device_management + * \internal + * + * This implementation simply marshalls the call to the kernel's + * wimax_rfkill() and returns it's return code. + */ +int wimaxll_rfkill(struct wimaxll_handle *wmx, enum wimax_rf_state state) +{ + ssize_t result; + struct nl_msg *msg; + + msg = nlmsg_new(); + if (msg == NULL) { + result = errno; + wimaxll_msg(wmx, "E: RFKILL: cannot allocate generic netlink " + "message: %m\n"); + goto error_msg_alloc; + } + if (genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, + wimaxll_family_id(wmx), 0, 0, + WIMAX_GNL_OP_RFKILL, WIMAX_GNL_VERSION) == NULL) { + result = -ENOMEM; + wimaxll_msg(wmx, "E: RFKILL: error preparing message: " + "%d 0x%08x\n", result, result); + goto error_msg_prep; + } + nla_put_u32(msg, WIMAX_GNL_RFKILL_STATE, (__u32) state); + result = nl_send_auto_complete(wmx->nlh_tx, msg); + if (result < 0) { + wimaxll_msg(wmx, "E: RFKILL: error sending message: %zd\n", + result); + goto error_msg_send; + } + /* Read the message ACK from netlink */ + result = wimaxll_wait_for_rp_result(wmx); + nl_wait_for_ack(wmx->nlh_tx); + if (result < 0) + wimaxll_msg(wmx, "E: RFKILL: operation failed: %zd\n", result); +error_msg_prep: +error_msg_send: + nlmsg_free(msg); +error_msg_alloc: + return result; +} diff --git a/lib/pipe.c b/lib/pipe.c new file mode 100644 index 0000000..0cc710a --- /dev/null +++ b/lib/pipe.c @@ -0,0 +1,196 @@ +/* + * Linux WiMax + * Pipe implementation + * + * + * 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 the_pipe_interface_group The pipe interface + * + * This is a collection of tools for managing access to the different + * pipes that a WiMAX kernel driver can export. + * + * There is always a default pipe, the \e message pipe, on which the + * kernel sends notifications (such as the <em> \ref state_change_group + * "state change" </em> notification) and \ref the_messaging_interface + * "driver-specific messages". + * + * The driver can create other pipes for sending messages out of band + * without clogging the default \e message pipe. This can be used, for + * example, for high bandwidth driver-specific diagnostics. + * + * This is a low level interface and other than wimaxll_pipe_read() + * for reading notifications from the kernel stack in a mainloop, a + * normal library user should not need to use much of it. + * + * It is implemented a very thin layer on top of the \ref mc_rx + * "multicast RX interface". + * + * \section usage Usage + * + * If a WiMAX driver exports a set of pipes (each one with a different + * name), a handle to it can be opened with: + * + * \code + * pipe_id = wimaxll_pipe_open(wmx, "pipename"); + * \endcode + * + * likewise, to close said pipe: + * + * \code + * wimaxll_pipe_close(wmx, pipe_id); + * \endcode + * + * To obtain the file descriptor associated to an opened pipe so that + * it can be fed to select(): + * + * \code + * fd = wimaxll_pipe_fd(wmx, pipe_id); + * \endcode + * + * and finally to read from said pipe and execute the callbacks + * associated to each different notification from the kernel + * + * \code + * wimaxll_pipe_read(wmx, pipe_id); + * \endcode + * + * The default \e message pipe is always open, and it's \e pipe_id can + * be obtained with wimaxll_msg_pipe_id(). + */ +#define _GNU_SOURCE +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <assert.h> +#include <linux/types.h> +#include <netlink/msg.h> +#include <netlink/genl/genl.h> +#include <wimaxll.h> +#include "internal.h" +#define D_LOCAL 0 +#include "debug.h" + + +/** + * Open a handle to receive messages from a WiMAX pipe + * + * \param wmx WiMAX device handle + * \param pipe_name Name of the pipe to open + * + * \return If successful, a non-negative pipe handle number (\e {the + * pipe id}). In case of error, a negative errno code. + * + * Opens a handle to receive data from the given WiMAX named + * pipe. wimaxll_pipe_msg_read() can be used to listen for data sent by + * the kernel on the named pipe. + * + * Only one handle may be opened at the same time to each pipe. + * + * \ingroup the_pipe_interface_group + */ +int wimaxll_pipe_open(struct wimaxll_handle *wmx, const char *pipe_name) +{ + return wimaxll_mc_rx_open(wmx, pipe_name); +} + + +/** + * Return the file descriptor associated to a multicast group + * + * \param wmx WiMAX device handle + * \param pipe_id Pipe ID, as returned by wimaxll_pipe_open(). + * \return file descriptor associated to the multicast group, that can + * be fed to functions like select(). + * + * This allows to select() on the file descriptor, which will block + * until a message is available, that then can be read with + * wimaxll_pipe_read() or wimaxll_pipe_msg_read(). + * + * \ingroup the_pipe_interface_group + */ +int wimaxll_pipe_fd(struct wimaxll_handle *wmx, unsigned pipe_id) +{ + return wimaxll_mc_rx_fd(wmx, pipe_id); +} + + +/** + * Read any kind of kernel messages from a pipe and execute callbacks + * + * \param wmx WiMAX device handle + * \param pipe_id Pipe to read from [as returned by wimaxll_pipe_open()]. + * \return If successful, 0 and the callbacks to known notifications will + * be called. On error, a negative errno code: + * + * -%EINPROGRESS: no messages received + * + * When reading notifications from the kernel, any unknown type will + * be ignored. + * + * \note This is a blocking call. + * + * \ingroup the_pipe_interface_group + */ +ssize_t wimaxll_pipe_read(struct wimaxll_handle *wmx, unsigned pipe_id) +{ + ssize_t result = -EINPROGRESS; + d_fnstart(3, wmx, "(wmx %p pipe_id %u)\n", wmx, pipe_id); + while (result == -EINPROGRESS || result == -ENODATA) + result = wimaxll_mc_rx_read(wmx, pipe_id); + d_fnend(3, wmx, "(wmx %p pipe_id %u) = %zd\n", wmx, pipe_id, result); + return result; +} + + +/** + * Close an a connection to a WiMAX pipe + * + * @param wmx WiMAX device handle + * \param pipe_id Pipe to close [as returned by wimaxll_pipe_open()]. + * + * Closes the connection to the given WiMAX pipe. + * + * \ingroup the_pipe_interface_group + */ +void wimaxll_pipe_close(struct wimaxll_handle *wmx, unsigned pipe_id) +{ + wimaxll_mc_rx_close(wmx, pipe_id); +} + + +void wimax_pipe_open() __attribute__ ((weak, alias("wimaxll_pipe_open"))); +void wimax_pipe_fd() __attribute__ ((weak, alias("wimaxll_pipe_fd"))); +void wimax_pipe_read() __attribute__ ((weak, alias("wimaxll_pipe_read"))); +void wimax_pipe_close() __attribute__ ((weak, alias("wimaxll_pipe_close"))); diff --git a/lib/re-state-change.c b/lib/re-state-change.c new file mode 100644 index 0000000..e0d1229 --- /dev/null +++ b/lib/re-state-change.c @@ -0,0 +1,317 @@ +/* + * Linux WiMax + * State Change Report + * + * + * 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 state_change_group Tracking state changes + * + * When the WiMAX devices change state, the kernel sends \e state \e + * change notification. + * + * An application can simply block a thread waiting for state changes + * using the following convenience function: + * + * @code + * result = wimaxll_wait_for_state_change(wmx, &old_state, &new_state); + * @endcode + * + * However, in most cases, applications will want to integrate into + * main loops and use the callback mechanism. + * + * For that, they just need to set a callback for the state change + * notification: + * + * + * @code + * wimaxll_set_cb_state_change(wmx, my_state_change_callback, context_pointer); + * @endcode + * + * and then wait for notifications to be available (see \ref receiving + * "receiving with select()"). When data is available and + * wimax_pipe_read() called to process them, the callback will be + * executed for each state change notification. + * + * Applications can query the current callback set for the state + * change notifications with wimaxll_get_cb_state_change(). + */ +#define _GNU_SOURCE +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <assert.h> +#include <linux/types.h> +#include <netlink/msg.h> +#include <netlink/genl/genl.h> +#include <wimaxll.h> +#include "internal.h" +#define D_LOCAL 0 +#include "debug.h" + + + +/** + * WIMAX_GNL_RE_STATE_CHANGE: policy specification + * + * \internal + * + * Authoritative reference for this is at the kernel code, + * drivers/net/wimax/stack.c. + */ +static +struct nla_policy wimaxll_gnl_re_state_change_policy[WIMAX_GNL_ATTR_MAX + 1] = { + [WIMAX_GNL_STCH_STATE_OLD] = { .type = NLA_U8 }, + [WIMAX_GNL_STCH_STATE_NEW] = { .type = NLA_U8 }, +}; + + +/** + * Callback to process an WIMAX_GNL_RE_STATE_CHANGE from the kernel + * + * \internal + * + * \param wmx WiMAX device handle + * \param mch WiMAX multicast group handle + * \param msg Pointer to netlink message + * \return \c enum nl_cb_action + * + * wimaxll_mc_rx_read() calls libnl's nl_recvmsgs() to receive messages; + * when a valid message is received, it goes into a loop that selects + * a callback to run for each type of message and it will call this + * function. + * + * This just expects a _RE_STATE_CHANGE message, whose payload is what + * has to be passed to the caller. We just extract the data and call + * the callback defined by the caller to wimaxll_mc_rx_read() (or + * wimaxll_pipe_read()). + */ +int wimaxll_gnl_handle_state_change(struct wimaxll_handle *wmx, + struct wimaxll_mc_handle *mch, + struct nl_msg *msg) +{ + ssize_t result; + struct nlmsghdr *nl_hdr; + struct genlmsghdr *gnl_hdr; + struct nlattr *tb[WIMAX_GNL_ATTR_MAX+1]; + struct wimaxll_gnl_cb_context *ctx = mch->state_change_context; + enum wimax_st old_state, new_state; + + d_fnstart(7, wmx, "(msg %p mch %p)\n", msg, mch); + nl_hdr = nlmsg_hdr(msg); + gnl_hdr = nlmsg_data(nl_hdr); + + assert(gnl_hdr->cmd == WIMAX_GNL_RE_STATE_CHANGE); + + /* Parse the attributes */ + result = genlmsg_parse(nl_hdr, 0, tb, WIMAX_GNL_ATTR_MAX, + wimaxll_gnl_re_state_change_policy); + if (result < 0) { + wimaxll_msg(wmx, "E: %s: genlmsg_parse() failed: %d\n", + __func__, result); + wimaxll_cb_context_set_result(ctx, result); + result = NL_SKIP; + goto error_parse; + } + if (tb[WIMAX_GNL_STCH_STATE_OLD] == NULL) { + wimaxll_msg(wmx, "E: %s: cannot find STCH_STATE_OLD " + "attribute\n", __func__); + wimaxll_cb_context_set_result(ctx, -ENXIO); + result = NL_SKIP; + goto error_no_attrs; + + } + old_state = nla_get_u8(tb[WIMAX_GNL_STCH_STATE_OLD]); + + if (tb[WIMAX_GNL_STCH_STATE_NEW] == NULL) { + wimaxll_msg(wmx, "E: %s: cannot find STCH_STATE_NEW " + "attribute\n", __func__); + wimaxll_cb_context_set_result(ctx, -ENXIO); + result = NL_SKIP; + goto error_no_attrs; + + } + new_state = nla_get_u8(tb[WIMAX_GNL_STCH_STATE_NEW]); + + wimaxll_cb_context_set_result(ctx, 0); + d_printf(1, wmx, "D: CRX re_state_change old %u new %u\n", + old_state, new_state); + if (mch->state_change_cb(wmx, ctx, old_state, new_state) == -EBUSY) + result = NL_STOP; + else + result = NL_OK; +error_no_attrs: +error_parse: + d_fnend(7, wmx, "(msg %p ctx %p) = %zd\n", msg, ctx, result); + return result; +} + + +/* + * Context for the default callback we use in + * wimaxll_wait_for_state_change() + */ +struct wimaxll_state_change_context { + struct wimaxll_gnl_cb_context ctx; + enum wimax_st *old_state, *new_state; + int set:1; +}; + + +/** + * Get the callback and priv pointer for a WIMAX_GNL_RE_STATE_CHANGE message + * + * \param wmx WiMAX handle. + * \param cb Where to store the current callback function. + * \param context Where to store the private data pointer passed to the + * callback. + * + * \ingroup state_change_group + */ +void wimaxll_get_cb_state_change(struct wimaxll_handle *wmx, + wimaxll_state_change_cb_f *cb, + struct wimaxll_gnl_cb_context **context) +{ + struct wimaxll_mc_handle *mch; + mch = __wimaxll_get_mc_handle(wmx, wmx->mc_msg); + if (mch != NULL) { + *cb = mch->state_change_cb; + *context = mch->state_change_context; + } +} + + +/** + * Set the callback and priv pointer for a WIMAX_GNL_RE_STATE_CHANGE message + * + * \param wmx WiMAX handle. + * \param cb Callback function to set + * \param context Private data pointer to pass to the callback function. + * + * \ingroup state_change_group + */ +void wimaxll_set_cb_state_change(struct wimaxll_handle *wmx, + wimaxll_state_change_cb_f cb, + struct wimaxll_gnl_cb_context *context) +{ + struct wimaxll_mc_handle *mch; + + mch = __wimaxll_get_mc_handle(wmx, wmx->mc_msg); + if (mch != NULL) { + mch->state_change_cb = cb; + mch->state_change_context = context; + } +} + + +/* + * Default callback we use in wimaxll_wait_for_state_change() + */ +static +int wimaxll_cb_state_change(struct wimaxll_handle *wmx, + struct wimaxll_gnl_cb_context *ctx, + enum wimax_st old_state, + enum wimax_st new_state) +{ + struct wimaxll_state_change_context *stch_ctx = + wimaxll_container_of(ctx, struct wimaxll_state_change_context, + ctx); + + if (stch_ctx->set) + return -EBUSY; + *stch_ctx->old_state = old_state; + *stch_ctx->new_state = new_state; + stch_ctx->set = 1; + return 0; +} + + +/** + * Wait for an state change notification from the kernel + * + * \param wmx WiMAX device handle + * \param old_state Pointer to where to store the previous state + * \param new_state Pointer to where to store the new state + * \return If successful, 0 and the values pointed to by the \a + * old_state and \a new_state arguments are valid; on error, a + * negative \a errno code and the state pointers contain no valid + * information. + * + * Waits for the WiMAX device to change state and reports said state + * change. + * + * Internally, this function uses wimax_pipe_read() on the default \e + * message pipe, which means that on reception (from the kernel) of + * notifications other than state change, any callbacks that are set + * for them will be executed. + * + * \note This is a blocking call. + * + * \note This function cannot be run in parallel with other code that + * modifies the \e state \e change callbacks for this same handle. + * + * \ingroup state_change_group + */ +ssize_t wimaxll_wait_for_state_change(struct wimaxll_handle *wmx, + enum wimax_st *old_state, + enum wimax_st *new_state) +{ + ssize_t result; + wimaxll_state_change_cb_f prev_cb = NULL; + struct wimaxll_gnl_cb_context *prev_ctx = NULL; + struct wimaxll_state_change_context ctx = { + .ctx = WIMAXLL_GNL_CB_CONTEXT_INIT(wmx), + .old_state = old_state, + .new_state = new_state, + .set = 0, + }; + + d_fnstart(3, wmx, "(wmx %p old_state %p new_state %p)\n", + wmx, old_state, new_state); + wimaxll_get_cb_state_change(wmx, &prev_cb, &prev_ctx); + wimaxll_set_cb_state_change(wmx, wimaxll_cb_state_change, &ctx.ctx); + result = wimaxll_pipe_read(wmx, wmx->mc_msg); + /* the callback filled out *old_state and *new_state if ok */ + wimaxll_set_cb_state_change(wmx, prev_cb, prev_ctx); + d_fnend(3, wmx, "(wmx %p old_state %p [%u] new_state %p [%u])\n", + wmx, old_state, *old_state, new_state, *new_state); + return result; +} + +void wimax_get_cb_state_change() + __attribute__ ((weak, alias("wimaxll_get_cb_state_change"))); +void wimax_set_cb_state_change() + __attribute__ ((weak, alias("wimaxll_set_cb_state_change"))); +void wimax_wait_for_state_change() + __attribute__ ((weak, alias("wimaxll_wait_for_state_change"))); diff --git a/lib/wimax.c b/lib/wimax.c new file mode 100644 index 0000000..0f337fb --- /dev/null +++ b/lib/wimax.c @@ -0,0 +1,443 @@ +/* + * Linux WiMax + * Shared/common routines + * + * + * 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. + * + * + * These are a set of facilities used by the implementation of the + * different ops in this library. + */ +#define _GNU_SOURCE +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <assert.h> +#include <linux/types.h> +#include <netlink/msg.h> +#include <netlink/genl/genl.h> +#include <wimaxll.h> +#include "internal.h" +#define D_LOCAL 0 +#include "debug.h" + + +/** + * Netlink callback to process netlink callback errors + * + * \internal + * + * \param nla Source netlink address + * \param nlerr Netlink error descritor + * \param _mch Pointer to (\a struct wimaxll_mc_handle) + * + * \return 'enum nl_cb_action', NL_OK if there is no error, NL_STOP on + * error and _mch->result possibly updated. + * + * While reading from netlink and processing with callbacks (using + * nl_recvmsgs()), we use this for the callback 'state machine' to + * store the result of an error message from the kernel. + */ +int wimaxll_gnl_error_cb(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, + void *_mch) +{ + struct wimaxll_mc_handle *mch = _mch; + struct wimaxll_handle *wmx = mch->wmx; + + d_fnstart(7, wmx, "(nla %p nlnerr %p [%d] mch %p)\n", + nla, nlerr, nlerr->error, _mch); + wimaxll_mch_maybe_set_result(mch, nlerr->error); + d_fnend(7, wmx, "(nla %p nlnerr %p [%d] mch %p) = %d\n", + nla, nlerr, nlerr->error, _mch, NL_STOP); + return NL_STOP; +} + + +/** + * Netlink callback to process an ack message and pass the 'error' code + * + * \internal + * + * Process a netlink ack message and extract the error code, which is + * placed in the context passed as argument for the calling function + * to use. + * + * We use this so that ACKers in the kernel can pass a simple error + * code (integer) in the ACK that netlink sends, without having to + * send an extra message. + * + * Complementary to wimaxll_gnl_error_cb(). + * + * Frontend to this is wimaxll_wait_for_ack() + */ +static +int wimaxll_gnl_ack_cb(struct nl_msg *msg, void *_mch) +{ + int result; + struct nlmsghdr *nl_hdr; + struct nlmsgerr *nl_err; + size_t size = nlmsg_len(nlmsg_hdr(msg)); + struct wimaxll_mc_handle *mch = _mch; + + nl_hdr = nlmsg_hdr(msg); + size = nlmsg_len(nl_hdr); + nl_err = nlmsg_data(nl_hdr); + + if (size < sizeof(*nl_err)) { + wimaxll_msg(NULL, "E: netlink ack: buffer too small " + "(%zu vs %zu expected)\n", + size, sizeof(*nl_hdr) + sizeof(*nl_err)); + result = -EIO; + goto error_ack_short; + } + d_printf(4, NULL, "netlink ack: nlmsghdr len %u type %u flags 0x%04x " + "seq 0x%x pid %u\n", nl_hdr->nlmsg_len, nl_hdr->nlmsg_type, + nl_hdr->nlmsg_flags, nl_hdr->nlmsg_seq, nl_hdr->nlmsg_pid); + if (nl_hdr->nlmsg_type != NLMSG_ERROR) { + wimaxll_msg(NULL, "E: netlink ack: message is not an ack but " + "type %u\n", nl_hdr->nlmsg_type); + result = -EBADE; + goto error_bad_type; + } + d_printf(4, NULL, "netlink ack: nlmsgerr error %d for " + "nlmsghdr len %u type %u flags 0x%04x seq 0x%x pid %u\n", + nl_err->error, + nl_err->msg.nlmsg_len, nl_err->msg.nlmsg_type, + nl_err->msg.nlmsg_flags, nl_err->msg.nlmsg_seq, + nl_err->msg.nlmsg_pid); + wimaxll_mch_maybe_set_result(mch, nl_err->error); + if (nl_err->error < 0) + d_printf(2, NULL, "D: netlink ack: received netlink error %d\n", + nl_err->error); +error_ack_short: +error_bad_type: + return NL_STOP; +} + + +/** + * Wait for a netlink ACK and pass on the result code it passed + * + * \internal + * + * \param wmx WiMAX device handle + * \return error code passed by the kernel in the nlmsgerr structure + * that contained the ACK. + * + * Similar to nl_wait_for_ack(), but returns the value in + * nlmsgerr->error, so it can be used by the kernel to return simple + * error codes. + */ +int wimaxll_wait_for_ack(struct wimaxll_handle *wmx) +{ + int result; + struct nl_cb *cb; + struct wimaxll_mc_handle fake_mch; + + fake_mch.wmx = wmx; + fake_mch.result = -EINPROGRESS; + + cb = nl_socket_get_cb(wmx->nlh_tx); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wimaxll_gnl_ack_cb, &fake_mch); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, NL_CB_DEFAULT, NULL); + nl_cb_err(cb, NL_CB_CUSTOM, wimaxll_gnl_error_cb, &fake_mch); + result = nl_recvmsgs_default(wmx->nlh_tx); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, NL_CB_DEFAULT, NULL); + nl_cb_put(cb); + if (result < 0) + return result; + return fake_mch.result; +} + + +/** + * WIMAX_GNL_RP_RESULT: policy specification + * + * \internal + * + * Authoritative reference for this is at the kernel code, + * drivers/net/wimax/stack.c. + */ +static +struct nla_policy wimaxll_gnl_result_policy[WIMAX_GNL_ATTR_MAX + 1] = { + /* This is really a signed-64 bit number that has to be + * casted from a u64 */ + [WIMAX_GNL_RESULT_CODE] = { .type = NLA_U64 }, +}; + + +/** + * Netlink callback to process a WIMAX_GNL_RP_RESULT message + * + * \internal + * + * \param msg Netlink message containing the reply + * \param _mch Pointer to a \a struct wimaxll_mc_handle where the + * result of the operation will be returned. + * + * \return 'enum nl_cb_action', NL_OK if there is no error, NL_STOP on + * error and _mch->result updated. + * + * This will take a received netlink message, check it is a \a + * WIMAX_GNL_RP_RESULT, parse the status code in it and store it the + * \m _mch's result data member for the caller to use later on. + */ +static +int wimaxll_gnl_rp_result_cb(struct nl_msg *msg, void *_mch) +{ + int result; + struct wimaxll_mc_handle *mch = _mch; + struct wimaxll_handle *wmx = mch->wmx; + struct nlmsghdr *nl_hdr; + struct genlmsghdr *genl_hdr; + struct nlattr *tb[WIMAX_GNL_ATTR_MAX + 1]; + + d_fnstart(7, wmx, "(msg %p mch %p)\n", msg, _mch); + nl_hdr = nlmsg_hdr(msg); + genl_hdr = nlmsg_data(nl_hdr); + + if (genl_hdr->cmd != WIMAX_GNL_RP_RESULT) { + result = NL_SKIP; + d_printf(1, wmx, "D: ignoring unknown command %d\n", + genl_hdr->cmd); + goto error_parse; + } + + /* Parse the attributes */ + result = genlmsg_parse(nl_hdr, 0, tb, WIMAX_GNL_ATTR_MAX, + wimaxll_gnl_result_policy); + if (result < 0) { + wimaxll_msg(wmx, "E: %s: genlmsg_parse() failed: %d\n", + __func__, result); + wimaxll_mch_maybe_set_result(mch, -ENXIO); + result = NL_SKIP; + goto error_parse; + } + if (tb[WIMAX_GNL_RESULT_CODE] == NULL) { + wimaxll_msg(wmx, "E: %s: No result code argument passed\n", + __func__); + wimaxll_mch_maybe_set_result(mch, -ENXIO); + result = NL_SKIP; + goto error_no_result_code; + + } + result = (ssize_t) nla_get_u64(tb[WIMAX_GNL_RESULT_CODE]); + wimaxll_mch_maybe_set_result(mch, result); + result = NL_OK; +error_no_result_code: +error_parse: + d_fnend(7, wmx, "(msg %p mch %p) = %d\n", msg, _mch, result); + return result; +} + + +/** + * Wait for a WIMAX_GNL_RP_RESULT reply (and an ack) and report its code + * + * \internal + * + * Many ops we execute in the kernel return just a signed integer for + * status using a WIMAX_GNL_RP_RESULT command for reply before sending + * the ACK. + * + * This function simplifies waiting for that and getting the reply. + * + * You still need to wait for the ack in your function calling + * nl_wait_for_ack(). + * + * \note We use the same handler used for transmission in the TX side + * and it's callback set, that we modifiy. As noted in the doc for + * \a struct wimaxll_handle, each call site to nl_recvmsgs_default() + * on \a wmx->nlh_tx must make sure the NL_CB_VALID and + * nl_cb_err() callbacks are properly set. + */ +ssize_t wimaxll_wait_for_rp_result(struct wimaxll_handle *wmx) +{ + ssize_t result; + struct nl_cb *cb; + struct wimaxll_mc_handle fake_mch; + + d_fnstart(5, wmx, "(wmx %p)\n", wmx); + fake_mch.wmx = wmx; + fake_mch.result = -EINPROGRESS; + cb = nl_socket_get_cb(wmx->nlh_tx); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + wimaxll_gnl_rp_result_cb, &fake_mch); + nl_cb_err(cb, NL_CB_CUSTOM, wimaxll_gnl_error_cb, &fake_mch); + nl_recvmsgs_default(wmx->nlh_tx); + nl_cb_put(cb); + + result = fake_mch.result; + if (result == -EINPROGRESS) + wimaxll_msg(wmx, "E: %s: the kernel didn't reply with a " + "WIMAX_GNL_RP_RESULT message\n", __func__); + else + d_printf(1, wmx, "D: WIMAX_GNL_RP_RESULT code is %d\n", result); + d_fnend(5, wmx, "(wmx %p) = %zd\n", wmx, result); + return result; +} + + +/** + * \defgroup diagnostics_group Output of diagnostics messages + * + * The \e libwimaxll library prints diagnostics by default to \a + * stderr. Said destination can be changed by the user by setting the + * wimaxll_vmsg() function pointer before calling any other \a + * libwimaxll function. + * + * To restore the default diagnostics destination, set wimaxll_vmsg() + * back to wimaxll_vmsg_stderr(). + */ + + +/** + * Deliver \e libwimaxll diagnostics messages to \e stderr + * + * \param fmt printf-like format + * \param vargs variable-argument list as created by + * stdargs.h:va_list() that will be formatted according to \e + * fmt. + * + * Default diagnostics printing function. + * + * \ingroup diagnostics_group + */ +void wimaxll_vmsg_stderr(const char *fmt, va_list vargs) +{ + vfprintf(stderr, fmt, vargs); +} + + + +/** + * Print library diagnostics messages [backend] + * + * @param fmt printf-like format + * @param vargs variable-argument list as created by + * stdargs.h:va_list() that will be formatted according to \e + * fmt. + * + * Prints/writes the \e libwimaxll's diagnostics messages to a + * destination as selected by the user of the library. + * + * \note This function pointer must be set \b before calling any other + * \e libwimaxll function. + * + * By default, diagnostics are printed with wimaxll_vmsg_stderr() to + * \a stderr. + * + * For example, to deliver diagnostics to syslog: + * + * @code + * #include <syslog.h> + * ... + * static + * void wimaxll_vmsg_syslog(const char *fmt, va_list vargs) + * { + * vsyslog(LOG_MAKEPRI(LOG_USER, LOG_INFO), fmt, vargs); + * } + * ... + * wimaxll_vmsg = wimaxll_vmsg_syslog(); + * ... + * wimaxll_open(BLAH); + * @endcode + * + * \ingroup diagnostics_group + * \internal + * + * The internal function wimaxll_msg() is used as as a frontend to + * this function. + */ +void (*wimaxll_vmsg)(const char *fmt, va_list vargs) = wimaxll_vmsg_stderr; + + +static +void __wimaxll_msg(const char *fmt, ...) +{ + va_list vargs; + va_start(vargs, fmt); + wimaxll_vmsg(fmt, vargs); + va_end(vargs); +} + + +/** + * \internal + * + * Prints library diagnostic messages with a predefined format [frontend] + * + * @param wmx WiMAX handle; if NULL, no device header will be presented. + * @param fmt printf-like format followed by any arguments + * + * Called by the library functions to print status/error messages. By + * default these are sent over to stderr. + * + * However, library users can change this default behaviour by setting + * wimaxll_vmsg() as documented in that function pointer's + * documentation. + * + * \ingroup diagnostics_group + */ +void wimaxll_msg(struct wimaxll_handle *wmx, const char *fmt, ...) +{ + va_list vargs; + if (wmx == NULL) + __wimaxll_msg("libwimax: "); + else if ((unsigned long) wmx < 4096) { + __wimaxll_msg("libwimax: E: Corrupt device handle %p\n", wmx); + __wimaxll_msg("libwimax[n/a]: "); + } else + __wimaxll_msg("libwimax[%s]: ", wmx->name); + va_start(vargs, fmt); + wimaxll_vmsg(fmt, vargs); + va_end(vargs); +} + + +/** + * Return the name of a the system's WiMAX interface associated to an + * open handle + * + * \param wmx WiMAX device handle + * \return Interface name (only valid while the handle is open) + * + * \ingroup device_management + */ +const char *wimaxll_ifname(const struct wimaxll_handle *wmx) +{ + return wmx->name; +} + +void wimax_ifname() __attribute__ ((weak, alias("wimaxll_ifname"))); |