commit 842719c84a589b45165700c9afb8aa93e834aa9c Author: Miqra Developer Date: Mon Sep 24 15:45:06 2018 +0000 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3aeea13 --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +**/.dirstamp +**/.deps + +build/ +deb_dist/ +*.pyc + +# http://www.gnu.org/software/automake + +Makefile.in +/ar-lib +/mdate-sh +/py-compile +/test-driver +/ylwrap + +# http://www.gnu.org/software/autoconf + +autom4te.cache +/autoscan.log +/autoscan-*.log +/aclocal.m4 +/compile +/config.guess +/config.h.in +/config.log +/config.status +/config.sub +/configure +/configure.scan +/depcomp +/install-sh +/missing +/stamp-h1 + +# https://www.gnu.org/software/libtool/ + +/ltmain.sh + +# http://www.gnu.org/software/texinfo + +/texinfo.tex + +# http://www.gnu.org/software/m4/ + +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5449695 --- /dev/null +++ b/Makefile @@ -0,0 +1,1151 @@ +# Makefile.in generated by automake 1.11.6 from Makefile.am. +# Makefile. Generated from Makefile.in by configure. + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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. + + + + + + +am__make_dryrun = \ + { \ + am__dry=no; \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ + | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ + *) \ + for am__flg in $$MAKEFLAGS; do \ + case $$am__flg in \ + *=*|--*) ;; \ + *n*) am__dry=yes; break;; \ + esac; \ + done;; \ + esac; \ + test $$am__dry = yes; \ + } +pkgdatadir = $(datadir)/mediacore-hid +pkgincludedir = $(includedir)/mediacore-hid +pkglibdir = $(libdir)/mediacore-hid +pkglibexecdir = $(libexecdir)/mediacore-hid +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 = : +sbin_PROGRAMS = mediacore-hid-server$(EXEEXT) +check_PROGRAMS = mcp23017-i2ctest$(EXEEXT) +TESTS = mcp23017-i2ctest$(EXEEXT) +subdir = . +DIST_COMMON = $(am__configure_deps) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/config.hpp.in \ + $(top_srcdir)/configure depcomp install-sh missing +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = config.hpp +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(init_d_dirdir)" \ + "$(DESTDIR)$(dbus_confdir)" "$(DESTDIR)$(etc_dirdir)" \ + "$(DESTDIR)$(modprobe_d_dirdir)" \ + "$(DESTDIR)$(rsyslog_confdir)" +PROGRAMS = $(sbin_PROGRAMS) +am__dirstamp = $(am__leading_dot)dirstamp +am__objects_1 = src/i2c/i2c.$(OBJEXT) +am_mcp23017_i2ctest_OBJECTS = src/test/mcp23017-i2ctest.$(OBJEXT) \ + $(am__objects_1) +mcp23017_i2ctest_OBJECTS = $(am_mcp23017_i2ctest_OBJECTS) +mcp23017_i2ctest_DEPENDENCIES = +am__objects_2 = src/gpio/gpio.$(OBJEXT) \ + src/mcp23017/mcp23017.$(OBJEXT) \ + src/exception/baseexceptions.$(OBJEXT) $(am__objects_1) +am__objects_3 = src/log/log.$(OBJEXT) src/thread/thread.$(OBJEXT) \ + src/buttontimer/buttontimer.$(OBJEXT) +am_mediacore_hid_server_OBJECTS = src/mc-hid-server.$(OBJEXT) \ + $(am__objects_2) $(am__objects_3) +mediacore_hid_server_OBJECTS = $(am_mediacore_hid_server_OBJECTS) +am__DEPENDENCIES_1 = +mediacore_hid_server_DEPENDENCIES = $(am__DEPENDENCIES_1) +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 = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +SCRIPTS = $(init_d_dir_SCRIPTS) +DEFAULT_INCLUDES = -I. +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(mcp23017_i2ctest_SOURCES) $(mediacore_hid_server_SOURCES) +DIST_SOURCES = $(mcp23017_i2ctest_SOURCES) \ + $(mediacore_hid_server_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(dbus_conf_DATA) $(etc_dir_DATA) $(modprobe_d_dir_DATA) \ + $(rsyslog_conf_DATA) +ETAGS = etags +CTAGS = ctags +am__tty_colors = \ +red=; grn=; lgn=; blu=; std= +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = ${SHELL} /opt/mediacore/mediacore_hid/missing --run aclocal-1.11 +AMTAR = $${TAR-tar} +AUTOCONF = ${SHELL} /opt/mediacore/mediacore_hid/missing --run autoconf +AUTOHEADER = ${SHELL} /opt/mediacore/mediacore_hid/missing --run autoheader +AUTOMAKE = ${SHELL} /opt/mediacore/mediacore_hid/missing --run automake-1.11 +AWK = mawk +BOOST_CPPFLAGS = -I/usr/include +BOOST_LDFLAGS = -L/usr/lib +CC = gcc +CCDEPMODE = depmode=gcc3 +CFLAGS = -g -O2 +CPPFLAGS = +CXX = g++ +CXXDEPMODE = depmode=gcc3 +CXXFLAGS = -g -O2 +CYGPATH_W = echo +DBUSXX_CHECK = yes +DEBUILD = /usr/bin/dpkg-buildpackage +DEFS = -DHAVE_CONFIG_H +DEPDIR = .deps +DEPS_CFLAGS = -DDBUS_API_SUBJECT_TO_CHANGE -I/usr/include/dbus-c++-1 -I/usr/include/dbus-1.0 -I/usr/lib/arm-linux-gnueabihf/dbus-1.0/include +DEPS_LIBS = -ldbus-c++-1 -ldbus-1 +DHMAKE = /usr/bin/dh_make +ECHO_C = +ECHO_N = -n +ECHO_T = +EXEEXT = +INSTALL = /usr/bin/install -c +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_PROGRAM = ${INSTALL} +INSTALL_SCRIPT = ${INSTALL} +INSTALL_STRIP_PROGRAM = $(install_sh) -c -s +LDFLAGS = +LIBOBJS = +LIBS = +LTLIBOBJS = +MAKEINFO = ${SHELL} /opt/mediacore/mediacore_hid/missing --run makeinfo +MKDIR_P = /bin/mkdir -p +OBJEXT = o +PACKAGE = mediacore-hid +PACKAGE_BUGREPORT = bugs@miqra.nl +PACKAGE_NAME = MediaCore HID Server +PACKAGE_STRING = MediaCore HID Server 1.0.0 +PACKAGE_TARNAME = mediacore-hid +PACKAGE_URL = http://www.miqra.nl/ +PACKAGE_VERSION = 1.0.0 +PATH_SEPARATOR = : +PKG_CONFIG = /usr/bin/pkg-config +PKG_CONFIG_LIBDIR = +PKG_CONFIG_PATH = +SET_MAKE = +SHELL = /bin/bash +STRIP = +VERSION = 1.0.0 +abs_builddir = /opt/mediacore/mediacore_hid +abs_srcdir = /opt/mediacore/mediacore_hid +abs_top_builddir = /opt/mediacore/mediacore_hid +abs_top_srcdir = /opt/mediacore/mediacore_hid +ac_ct_CC = gcc +ac_ct_CXX = g++ +am__include = include +am__leading_dot = . +am__quote = +am__tar = $${TAR-tar} chof - "$$tardir" +am__untar = $${TAR-tar} xf - +bindir = ${exec_prefix}/bin +build_alias = +builddir = . +datadir = ${datarootdir} +datarootdir = ${prefix}/share +docdir = ${datarootdir}/doc/${PACKAGE_TARNAME} +dvidir = ${docdir} +exec_prefix = ${prefix} +host_alias = +htmldir = ${docdir} +includedir = ${prefix}/include +infodir = ${datarootdir}/info +install_sh = ${SHELL} /opt/mediacore/mediacore_hid/install-sh +libdir = ${exec_prefix}/lib +libexecdir = ${exec_prefix}/libexec +localedir = ${datarootdir}/locale +localstatedir = ${prefix}/var +mandir = ${datarootdir}/man +mkdir_p = /bin/mkdir -p +oldincludedir = /usr/include +pdfdir = ${docdir} +prefix = /usr/local +program_transform_name = s,x,x, +psdir = ${docdir} +sbindir = ${exec_prefix}/sbin +sharedstatedir = ${prefix}/com +srcdir = . +sysconfdir = ${prefix}/etc +target_alias = +top_build_prefix = +top_builddir = . +top_srcdir = . +AUTOMAKE_OPTIONS = subdir-objects +ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} +AM_CPPFLAGS = $(DEPS_CFLAGS) +DISTCLEANFILES = src/mc-hid-server-glue.hpp \ + script/init.d/mediacore-hid + +I2C_SRC = src/i2c/i2c.c \ + src/i2c/i2c.h + +MCP_GPIO_SRC = src/gpio/gpio.hpp \ + src/gpio/gpio.cpp \ + src/mcp23017/mcp23017.hpp \ + src/mcp23017/mcp23017.cpp \ + src/exception/baseexceptions.hpp \ + src/exception/baseexceptions.cpp \ + $(I2C_SRC) + +SUPPORT_SRC = src/log/log.hpp \ + src/log/log.cpp \ + src/thread/thread.hpp \ + src/thread/thread.cpp \ + src/buttontimer/buttontimer.hpp \ + src/buttontimer/buttontimer.cpp + +EXTRA_DIST = \ + cfg/dbus/nl.miqra.MediaCore.Hid.conf \ + cfg/init.d/mediacore-hid.in \ + cfg/rsyslog/syslog.MediaCore.Hid.conf \ + src/mc-hid-introspect.xml \ + cfg/modules \ + cfg/modprobe.d/raspi-blacklist.conf + +init_d_dirdir = $(sysconfdir)/init.d +init_d_dir_SCRIPTS = cfg/init.d/mediacore-hid +dbus_confdir = $(sysconfdir)/dbus-1/system.d +dbus_conf_DATA = cfg/dbus/nl.miqra.MediaCore.Hid.conf +rsyslog_confdir = $(sysconfdir)/rsyslog.d +rsyslog_conf_DATA = cfg/rsyslog/syslog.MediaCore.Hid.conf +etc_dirdir = $(sysconfdir) +etc_dir_DATA = cfg/modules +modprobe_d_dirdir = $(sysconfdir)/modprobe.d +modprobe_d_dir_DATA = cfg/modprobe.d/raspi-blacklist.conf +BUILT_SOURCES = src/mc-hid-server-glue.hpp +mediacore_hid_server_SOURCES = src/mc-hid-server.cpp \ + src/mc-hid-server.hpp \ + src/mc-hid-server-glue.hpp \ + $(MCP_GPIO_SRC) \ + $(SUPPORT_SRC) + +mediacore_hid_server_LDADD = $(DEPS_LIBS) -lpthread -lrt +mcp23017_i2ctest_SOURCES = src/test/mcp23017-i2ctest.c \ + $(I2C_SRC) + +mcp23017_i2ctest_LDADD = -lpthread +all: $(BUILT_SOURCES) config.hpp + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .cpp .o .obj +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): + +config.hpp: stamp-h1 + @if test ! -f $@; then rm -f stamp-h1; else :; fi + @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) stamp-h1; else :; fi + +stamp-h1: $(srcdir)/config.hpp.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status config.hpp +$(srcdir)/config.hpp.in: $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.hpp stamp-h1 + +clean-checkPROGRAMS: + -test -z "$(check_PROGRAMS)" || rm -f $(check_PROGRAMS) +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) +src/test/$(am__dirstamp): + @$(MKDIR_P) src/test + @: > src/test/$(am__dirstamp) +src/test/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/test/$(DEPDIR) + @: > src/test/$(DEPDIR)/$(am__dirstamp) +src/test/mcp23017-i2ctest.$(OBJEXT): src/test/$(am__dirstamp) \ + src/test/$(DEPDIR)/$(am__dirstamp) +src/i2c/$(am__dirstamp): + @$(MKDIR_P) src/i2c + @: > src/i2c/$(am__dirstamp) +src/i2c/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/i2c/$(DEPDIR) + @: > src/i2c/$(DEPDIR)/$(am__dirstamp) +src/i2c/i2c.$(OBJEXT): src/i2c/$(am__dirstamp) \ + src/i2c/$(DEPDIR)/$(am__dirstamp) +mcp23017-i2ctest$(EXEEXT): $(mcp23017_i2ctest_OBJECTS) $(mcp23017_i2ctest_DEPENDENCIES) $(EXTRA_mcp23017_i2ctest_DEPENDENCIES) + @rm -f mcp23017-i2ctest$(EXEEXT) + $(LINK) $(mcp23017_i2ctest_OBJECTS) $(mcp23017_i2ctest_LDADD) $(LIBS) +src/$(am__dirstamp): + @$(MKDIR_P) src + @: > src/$(am__dirstamp) +src/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/$(DEPDIR) + @: > src/$(DEPDIR)/$(am__dirstamp) +src/mc-hid-server.$(OBJEXT): src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) +src/gpio/$(am__dirstamp): + @$(MKDIR_P) src/gpio + @: > src/gpio/$(am__dirstamp) +src/gpio/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/gpio/$(DEPDIR) + @: > src/gpio/$(DEPDIR)/$(am__dirstamp) +src/gpio/gpio.$(OBJEXT): src/gpio/$(am__dirstamp) \ + src/gpio/$(DEPDIR)/$(am__dirstamp) +src/mcp23017/$(am__dirstamp): + @$(MKDIR_P) src/mcp23017 + @: > src/mcp23017/$(am__dirstamp) +src/mcp23017/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/mcp23017/$(DEPDIR) + @: > src/mcp23017/$(DEPDIR)/$(am__dirstamp) +src/mcp23017/mcp23017.$(OBJEXT): src/mcp23017/$(am__dirstamp) \ + src/mcp23017/$(DEPDIR)/$(am__dirstamp) +src/exception/$(am__dirstamp): + @$(MKDIR_P) src/exception + @: > src/exception/$(am__dirstamp) +src/exception/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/exception/$(DEPDIR) + @: > src/exception/$(DEPDIR)/$(am__dirstamp) +src/exception/baseexceptions.$(OBJEXT): src/exception/$(am__dirstamp) \ + src/exception/$(DEPDIR)/$(am__dirstamp) +src/log/$(am__dirstamp): + @$(MKDIR_P) src/log + @: > src/log/$(am__dirstamp) +src/log/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/log/$(DEPDIR) + @: > src/log/$(DEPDIR)/$(am__dirstamp) +src/log/log.$(OBJEXT): src/log/$(am__dirstamp) \ + src/log/$(DEPDIR)/$(am__dirstamp) +src/thread/$(am__dirstamp): + @$(MKDIR_P) src/thread + @: > src/thread/$(am__dirstamp) +src/thread/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/thread/$(DEPDIR) + @: > src/thread/$(DEPDIR)/$(am__dirstamp) +src/thread/thread.$(OBJEXT): src/thread/$(am__dirstamp) \ + src/thread/$(DEPDIR)/$(am__dirstamp) +src/buttontimer/$(am__dirstamp): + @$(MKDIR_P) src/buttontimer + @: > src/buttontimer/$(am__dirstamp) +src/buttontimer/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/buttontimer/$(DEPDIR) + @: > src/buttontimer/$(DEPDIR)/$(am__dirstamp) +src/buttontimer/buttontimer.$(OBJEXT): \ + src/buttontimer/$(am__dirstamp) \ + src/buttontimer/$(DEPDIR)/$(am__dirstamp) +mediacore-hid-server$(EXEEXT): $(mediacore_hid_server_OBJECTS) $(mediacore_hid_server_DEPENDENCIES) $(EXTRA_mediacore_hid_server_DEPENDENCIES) + @rm -f mediacore-hid-server$(EXEEXT) + $(CXXLINK) $(mediacore_hid_server_OBJECTS) $(mediacore_hid_server_LDADD) $(LIBS) +install-init_d_dirSCRIPTS: $(init_d_dir_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(init_d_dir_SCRIPTS)'; test -n "$(init_d_dirdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(init_d_dirdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(init_d_dirdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(init_d_dirdir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(init_d_dirdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-init_d_dirSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(init_d_dir_SCRIPTS)'; test -n "$(init_d_dirdir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(init_d_dirdir)'; $(am__uninstall_files_from_dir) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f src/buttontimer/buttontimer.$(OBJEXT) + -rm -f src/exception/baseexceptions.$(OBJEXT) + -rm -f src/gpio/gpio.$(OBJEXT) + -rm -f src/i2c/i2c.$(OBJEXT) + -rm -f src/log/log.$(OBJEXT) + -rm -f src/mc-hid-server.$(OBJEXT) + -rm -f src/mcp23017/mcp23017.$(OBJEXT) + -rm -f src/test/mcp23017-i2ctest.$(OBJEXT) + -rm -f src/thread/thread.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +include src/$(DEPDIR)/mc-hid-server.Po +include src/buttontimer/$(DEPDIR)/buttontimer.Po +include src/exception/$(DEPDIR)/baseexceptions.Po +include src/gpio/$(DEPDIR)/gpio.Po +include src/i2c/$(DEPDIR)/i2c.Po +include src/log/$(DEPDIR)/log.Po +include src/mcp23017/$(DEPDIR)/mcp23017.Po +include src/test/$(DEPDIR)/mcp23017-i2ctest.Po +include src/thread/$(DEPDIR)/thread.Po + +.c.o: + depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ + $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ + $(am__mv) $$depbase.Tpo $$depbase.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(COMPILE) -c -o $@ $< + +.c.obj: + depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ + $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ + $(am__mv) $$depbase.Tpo $$depbase.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \ +# $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.o: + depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ + $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ + $(am__mv) $$depbase.Tpo $$depbase.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) \ +# $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: + depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ + $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ + $(am__mv) $$depbase.Tpo $$depbase.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) \ +# $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +install-dbus_confDATA: $(dbus_conf_DATA) + @$(NORMAL_INSTALL) + @list='$(dbus_conf_DATA)'; test -n "$(dbus_confdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(dbus_confdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(dbus_confdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbus_confdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(dbus_confdir)" || exit $$?; \ + done + +uninstall-dbus_confDATA: + @$(NORMAL_UNINSTALL) + @list='$(dbus_conf_DATA)'; test -n "$(dbus_confdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(dbus_confdir)'; $(am__uninstall_files_from_dir) +install-etc_dirDATA: $(etc_dir_DATA) + @$(NORMAL_INSTALL) + @list='$(etc_dir_DATA)'; test -n "$(etc_dirdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(etc_dirdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(etc_dirdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(etc_dirdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(etc_dirdir)" || exit $$?; \ + done + +uninstall-etc_dirDATA: + @$(NORMAL_UNINSTALL) + @list='$(etc_dir_DATA)'; test -n "$(etc_dirdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(etc_dirdir)'; $(am__uninstall_files_from_dir) +install-modprobe_d_dirDATA: $(modprobe_d_dir_DATA) + @$(NORMAL_INSTALL) + @list='$(modprobe_d_dir_DATA)'; test -n "$(modprobe_d_dirdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(modprobe_d_dirdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(modprobe_d_dirdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(modprobe_d_dirdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(modprobe_d_dirdir)" || exit $$?; \ + done + +uninstall-modprobe_d_dirDATA: + @$(NORMAL_UNINSTALL) + @list='$(modprobe_d_dir_DATA)'; test -n "$(modprobe_d_dirdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(modprobe_d_dirdir)'; $(am__uninstall_files_from_dir) +install-rsyslog_confDATA: $(rsyslog_conf_DATA) + @$(NORMAL_INSTALL) + @list='$(rsyslog_conf_DATA)'; test -n "$(rsyslog_confdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(rsyslog_confdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(rsyslog_confdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(rsyslog_confdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(rsyslog_confdir)" || exit $$?; \ + done + +uninstall-rsyslog_confDATA: + @$(NORMAL_UNINSTALL) + @list='$(rsyslog_conf_DATA)'; test -n "$(rsyslog_confdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(rsyslog_confdir)'; $(am__uninstall_files_from_dir) + +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; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) config.hpp.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) config.hpp.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; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) config.hpp.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) config.hpp.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)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @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 "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__remove_distdir) + +dist-lzma: distdir + tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma + $(am__remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__remove_distdir) + +dist-tarZ: distdir + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__remove_distdir) + +dist-shar: distdir + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__remove_distdir) + +dist dist-all: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lzma*) \ + lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir); chmod u+w $(distdir) + mkdir $(distdir)/_build + mkdir $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build \ + && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(DATA) config.hpp +installdirs: + for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(init_d_dirdir)" "$(DESTDIR)$(dbus_confdir)" "$(DESTDIR)$(etc_dirdir)" "$(DESTDIR)$(modprobe_d_dirdir)" "$(DESTDIR)$(rsyslog_confdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) 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: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f src/$(DEPDIR)/$(am__dirstamp) + -rm -f src/$(am__dirstamp) + -rm -f src/buttontimer/$(DEPDIR)/$(am__dirstamp) + -rm -f src/buttontimer/$(am__dirstamp) + -rm -f src/exception/$(DEPDIR)/$(am__dirstamp) + -rm -f src/exception/$(am__dirstamp) + -rm -f src/gpio/$(DEPDIR)/$(am__dirstamp) + -rm -f src/gpio/$(am__dirstamp) + -rm -f src/i2c/$(DEPDIR)/$(am__dirstamp) + -rm -f src/i2c/$(am__dirstamp) + -rm -f src/log/$(DEPDIR)/$(am__dirstamp) + -rm -f src/log/$(am__dirstamp) + -rm -f src/mcp23017/$(DEPDIR)/$(am__dirstamp) + -rm -f src/mcp23017/$(am__dirstamp) + -rm -f src/test/$(DEPDIR)/$(am__dirstamp) + -rm -f src/test/$(am__dirstamp) + -rm -f src/thread/$(DEPDIR)/$(am__dirstamp) + -rm -f src/thread/$(am__dirstamp) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-checkPROGRAMS clean-generic clean-sbinPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf src/$(DEPDIR) src/buttontimer/$(DEPDIR) src/exception/$(DEPDIR) src/gpio/$(DEPDIR) src/i2c/$(DEPDIR) src/log/$(DEPDIR) src/mcp23017/$(DEPDIR) src/test/$(DEPDIR) src/thread/$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-hdr distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-dbus_confDATA install-etc_dirDATA \ + install-init_d_dirSCRIPTS install-modprobe_d_dirDATA \ + install-rsyslog_confDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-sbinPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -rf src/$(DEPDIR) src/buttontimer/$(DEPDIR) src/exception/$(DEPDIR) src/gpio/$(DEPDIR) src/i2c/$(DEPDIR) src/log/$(DEPDIR) src/mcp23017/$(DEPDIR) src/test/$(DEPDIR) src/thread/$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-dbus_confDATA uninstall-etc_dirDATA \ + uninstall-init_d_dirSCRIPTS uninstall-modprobe_d_dirDATA \ + uninstall-rsyslog_confDATA uninstall-sbinPROGRAMS + +.MAKE: all check check-am install install-am install-strip + +.PHONY: CTAGS GTAGS all all-am am--refresh check check-TESTS check-am \ + clean clean-checkPROGRAMS clean-generic clean-sbinPROGRAMS \ + ctags dist dist-all dist-bzip2 dist-gzip dist-lzip dist-lzma \ + dist-shar dist-tarZ dist-xz dist-zip distcheck distclean \ + distclean-compile distclean-generic distclean-hdr \ + distclean-tags distcleancheck distdir distuninstallcheck dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dbus_confDATA install-dvi \ + install-dvi-am install-etc_dirDATA install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-init_d_dirSCRIPTS install-man \ + install-modprobe_d_dirDATA install-pdf install-pdf-am \ + install-ps install-ps-am install-rsyslog_confDATA \ + install-sbinPROGRAMS install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-dbus_confDATA uninstall-etc_dirDATA \ + uninstall-init_d_dirSCRIPTS uninstall-modprobe_d_dirDATA \ + uninstall-rsyslog_confDATA uninstall-sbinPROGRAMS + + +src/mc-hid-server-glue.hpp: src/mc-hid-introspect.xml + dbusxx-xml2cpp $^ --adaptor=$@ + +cfg/init.d/mediacore-hid: cfg/init.d/mediacore-hid.in + cat $^ > $@ + sed -i "s#@BIN_DIR@#$(bindir)#" $@ + sed -i "s#@ETC_DIR@#$(sysconfdir)#" $@ + sed -i "s#@SBIN_DIR@#$(sbindir)#" $@ + +version.py: version.py.in + cat $^ > $@ + sed -i "s#@V_PACKAGE@#$(PACKAGE)#" $@ + sed -i "s#@V_VERSION\@#$(VERSION)#" $@ + +# 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/Makefile.am b/Makefile.am new file mode 100755 index 0000000..963997d --- /dev/null +++ b/Makefile.am @@ -0,0 +1,83 @@ +AUTOMAKE_OPTIONS = subdir-objects +ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} + +AM_CPPFLAGS = $(DEPS_CFLAGS) + +DISTCLEANFILES = src/mc-hid-server-glue.hpp \ + script/init.d/mediacore-hid + +I2C_SRC = src/i2c/i2c.c \ + src/i2c/i2c.h + +MCP_GPIO_SRC = src/gpio/gpio.hpp \ + src/gpio/gpio.cpp \ + src/mcp23017/mcp23017.hpp \ + src/mcp23017/mcp23017.cpp \ + src/exception/baseexceptions.hpp \ + src/exception/baseexceptions.cpp \ + $(I2C_SRC) + +SUPPORT_SRC = src/log/log.hpp \ + src/log/log.cpp \ + src/thread/thread.hpp \ + src/thread/thread.cpp \ + src/buttontimer/buttontimer.hpp \ + src/buttontimer/buttontimer.cpp + +sbin_PROGRAMS = mediacore-hid-server +check_PROGRAMS = mcp23017-i2ctest +TESTS = mcp23017-i2ctest + +EXTRA_DIST = \ + cfg/dbus/nl.miqra.MediaCore.Hid.conf \ + cfg/init.d/mediacore-hid.in \ + cfg/rsyslog/syslog.MediaCore.Hid.conf \ + src/mc-hid-introspect.xml \ + cfg/modules \ + cfg/modprobe.d/raspi-blacklist.conf + +init_d_dirdir = $(sysconfdir)/init.d +init_d_dir_SCRIPTS = cfg/init.d/mediacore-hid + +dbus_confdir = $(sysconfdir)/dbus-1/system.d +dbus_conf_DATA = cfg/dbus/nl.miqra.MediaCore.Hid.conf + +rsyslog_confdir = $(sysconfdir)/rsyslog.d +rsyslog_conf_DATA = cfg/rsyslog/syslog.MediaCore.Hid.conf + +etc_dirdir = $(sysconfdir) +etc_dir_DATA = cfg/modules + +modprobe_d_dirdir = $(sysconfdir)/modprobe.d +modprobe_d_dir_DATA = cfg/modprobe.d/raspi-blacklist.conf + + +src/mc-hid-server-glue.hpp: src/mc-hid-introspect.xml + dbusxx-xml2cpp $^ --adaptor=$@ + +BUILT_SOURCES = src/mc-hid-server-glue.hpp + +mediacore_hid_server_SOURCES = src/mc-hid-server.cpp \ + src/mc-hid-server.hpp \ + src/mc-hid-server-glue.hpp \ + $(MCP_GPIO_SRC) \ + $(SUPPORT_SRC) + +mediacore_hid_server_LDADD = $(DEPS_LIBS) -lpthread -lrt + + +mcp23017_i2ctest_SOURCES = src/test/mcp23017-i2ctest.c \ + $(I2C_SRC) + +mcp23017_i2ctest_LDADD = -lpthread + +cfg/init.d/mediacore-hid: cfg/init.d/mediacore-hid.in + cat $^ > $@ + sed -i "s#@BIN_DIR@#$(bindir)#" $@ + sed -i "s#@ETC_DIR@#$(sysconfdir)#" $@ + sed -i "s#@SBIN_DIR@#$(sbindir)#" $@ + +version.py: version.py.in + cat $^ > $@ + sed -i "s#@V_PACKAGE@#$(PACKAGE)#" $@ + sed -i "s#@V_VERSION\@#$(VERSION)#" $@ \ No newline at end of file diff --git a/TESTCMDS.txt b/TESTCMDS.txt new file mode 100644 index 0000000..f344812 --- /dev/null +++ b/TESTCMDS.txt @@ -0,0 +1,8 @@ +Monitor bus: + dbus-monitor --profile "interface='nl.miqra.MediaCore.Hid'" --system + +Call method on bus: + dbus-send --system --dest=nl.miqra.MediaCore.Hid --print-reply --type=method_call /nl/miqra/MediaCore/Hid nl.miqra.MediaCore.Hid.SetColor byte:[r] byte:[g] byte:[b] + +Rebuild make system: + autoreconf -i \ No newline at end of file diff --git a/cfg/dbus/nl.miqra.MediaCore.Hid.conf b/cfg/dbus/nl.miqra.MediaCore.Hid.conf new file mode 100644 index 0000000..58c65bb --- /dev/null +++ b/cfg/dbus/nl.miqra.MediaCore.Hid.conf @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cfg/init.d/mediacore-hid.in b/cfg/init.d/mediacore-hid.in new file mode 100644 index 0000000..d97839b --- /dev/null +++ b/cfg/init.d/mediacore-hid.in @@ -0,0 +1,163 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: mediacorehid +# Required-Start: $syslog $dbus +# Required-Stop: $syslog $dbus +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Example initscript +# Description: This file should be used to construct scripts to be +# placed in /etc/init.d. +### END INIT INFO + +# Author: Miqra Engineering <@miqra.nl> +# +# Please remove the "Author" lines above and replace them +# with your own name if you copy and modify this script. + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/bin:/usr/sbin:/usr/bin +DESC="Mediacore HID interface server" +NAME=mediacore-hid-server +DAEMON=@SBIN_DIR@/$NAME +DAEMON_ARGS="" +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=@ETC_DIR@/init.d/$NAME + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +VERBOSE=yes + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --background -m --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --background -m --pidfile $PIDFILE --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. + sleep 0.3 + dbus-send --system --dest=nl.miqra.MediaCore.Hid --type=method_call /nl/miqra/MediaCore/Hid nl.miqra.MediaCore.Hid.SetColor byte:128 byte:0 byte:0 +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --exec $DAEMON + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --exec $DAEMON + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: diff --git a/cfg/modprobe.d/raspi-blacklist.conf b/cfg/modprobe.d/raspi-blacklist.conf new file mode 100644 index 0000000..d7e3a73 --- /dev/null +++ b/cfg/modprobe.d/raspi-blacklist.conf @@ -0,0 +1,4 @@ +# blacklist spi and i2c by default (many users don't need them) + +blacklist spi-bcm2708 +#blacklist i2c-bcm2708 diff --git a/cfg/modules b/cfg/modules new file mode 100644 index 0000000..7af9ed7 --- /dev/null +++ b/cfg/modules @@ -0,0 +1,8 @@ +# /etc/modules: kernel modules to load at boot time. +# +# This file contains the names of kernel modules that should be loaded +# at boot time, one per line. Lines beginning with "#" are ignored. +# Parameters can be specified after the module name. + +snd-bcm2835 +i2c-dev diff --git a/cfg/piio.conf b/cfg/piio.conf new file mode 100644 index 0000000..6dfdae6 --- /dev/null +++ b/cfg/piio.conf @@ -0,0 +1,148 @@ +# Example configuration file for PiIo +# +# Configuration is done in groups called I/O groups. +# Each IO group can only contain I/O's of one type, e.g. only Raspberry Pi GPIOs or only pins on one MCP23017 IO expander +# for each type there are parameters +# Each group has a unique name + +# Note that in this example config file, all actual code is initially commented out to avoid problems directly after install + +/* +# Io Group called GPIO +GPIO: +{ + # it is of I/O type "GPIO", indicating it uses Raspberry Pi internal GPIOS + type = "GPIO"; + + # Settings for this groups PWM generatoer + pwm-tickdelay-us = 1600; # Number of microseconds between ticks + pwm-ticks = 16; # Number of ticks in a pwm cycle + + # Settings for the individual I/O's + # Here too, each I/O has it's own type and it's own id. + + io: + { + # Input called 'intest' + intest: + { + # It is of type "MULTIBITIN", which means that it takes values from multiple + # bits and combines it into a single number value. + # (To use a multibit output, you can use the type "MULTIBITOUT", which takes the same configuration, minus the additional settings) + type: "MULTIBITIN"; + + # The 'pins' options specified the pins that are used for the multibit input. + # For the GPIO, it takes the BCM identifiers. Both revision 1 and 2 are supported, and converted to the proper pin automatically + # Bit order is important, MSB first, LSB last. + pins: [17, 18]; + + # Additional config options are: + + // pullup: True/False # Default: False - Enable/disable internal pullup (currently not functional on GPIO type) + // invert: True/False # Default: False - Invert the input pins before processing + // int-enabled: True/False # Default: True - Trigger an event on value change for this input + } + + # Button called 'btn' + btn: + { + # Input of type "BUTTON", which means a single input pin, treated as a button. + # Buttons can trigger only 'Pressed' and 'Held' events, which trigger when the button is either pressed + # for a minimum of (default) 25 ms, or held for a minimum of (default) 6000 ms + # These values can be configured in the IO Group config + type: "BUTTON"; + + # Button is connected on pin 4. + pin: 4; + + # Additional config options are: + + // pullup: True/False # Default: True - Enable/disable internal pullup (currently not functional on GPIO type) + // invert: True/False # Default: True - Invert the input pins before processing + // int-enabled: True/False # Default: True - Trigger an event on value change for this input + + # Note that defaults for buttons are different from defaults for other inputs (invert and pullup true by default for buttons) + + } + + # Output called 'led1' + led1: + { + # Output of type "OUTPUTPIN", which means a single output pin. + type: "OUTPUTPIN"; + + # Output on pin 23 + pin: 23; + }; + # Output called 'led1' + led2: + { + # Output of type "OUTPUTPIN", which means a single output pin. + type: "OUTPUTPIN"; + # Output on pin 24 + pin: 24; + }; + # PWM Output called 'led3' + led3: + { + # Output of type "PWMPIN", which means pwm on a single output pin. + # Note that using PWM takes up processor time, + # currently around 5% for mcp23017 pins, and up to 30% for GPIO pins. (This last value is being worked on) + type: "PWMPIN"; + + # Output on pin 23 + pin: 25; + }; + }; +} +*/ + +/* +# Io Group called GPIO +MCP1: +{ + # it is of I/O type "MCP23017", indicating it uses an MCP23017 I2C I/O expander chip + type = "MCP23017"; + + # The MCP23017 needs an I2C address, and optionally a GPIO I/O pin to receive interrupts on + address = 0x20; + intpin = 22; + + # Settings for the individual I/O's + # Here too, each I/O has it's own type and it's own id. + + # Note that the pin id's for an MCP23017 are counted from the GPA0 as pin 0 up to GPB7 as pin 15 + io: + { + # Input called 'sensor1' + sensor1: + { + # It is of type "INPUTPIN", which means that it takes the value from the single input pin + type: "INPUTPIN"; + + # Input on pin 7 + pin: 7; + } + leda: + { + # Output of type "PWMPIN", which means pwm on a single output pin. + # Note that using PWM takes up processor time, + # currently around 5% for mcp23017 pins, and up to 30% for GPIO pins. (This last value is being worked on) + type: "PWMPIN"; + + # Output on pin 8 + pin: 8; + } + ledb: + { + # Output of type "PWMPIN", which means pwm on a single output pin. + # Note that using PWM takes up processor time, + # currently around 5% for mcp23017 pins, and up to 30% for GPIO pins. (This last value is being worked on) + type: "PWMPIN"; + + # Output on pin 9 + pin: 9; + } + } +} +*/ \ No newline at end of file diff --git a/cfg/rsyslog/syslog.MediaCore.Hid.conf b/cfg/rsyslog/syslog.MediaCore.Hid.conf new file mode 100644 index 0000000..52d95c7 --- /dev/null +++ b/cfg/rsyslog/syslog.MediaCore.Hid.conf @@ -0,0 +1 @@ +local0.* -/var/log/mediacore.log \ No newline at end of file diff --git a/config.hpp b/config.hpp new file mode 100644 index 0000000..2ce0ed3 --- /dev/null +++ b/config.hpp @@ -0,0 +1,23 @@ +/* config.hpp. Generated from config.hpp.in by configure. */ +/* config.hpp.in. Generated from configure.ac by autoheader. */ + +/* define if the Boost library is available */ +#define HAVE_BOOST /**/ + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "bugs@miqra.nl" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "MediaCore HID Server" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "MediaCore HID Server 1.0.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "mediacore-hid" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "http://www.miqra.nl/" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.0.0" diff --git a/config.hpp.in b/config.hpp.in new file mode 100644 index 0000000..2b3fc7b --- /dev/null +++ b/config.hpp.in @@ -0,0 +1,22 @@ +/* config.hpp.in. Generated from configure.ac by autoheader. */ + +/* define if the Boost library is available */ +#undef HAVE_BOOST + +/* 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 home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION diff --git a/config.hpp.in~ b/config.hpp.in~ new file mode 100644 index 0000000..0dd1d44 --- /dev/null +++ b/config.hpp.in~ @@ -0,0 +1,25 @@ +/* config.hpp.in. Generated from configure.ac by autoheader. */ + +/* define if the Boost library is available */ +#undef HAVE_BOOST + +/* define if the Boost::Signals library is available */ +#undef HAVE_BOOST_SIGNALS + +/* 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 home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION diff --git a/configure.ac b/configure.ac new file mode 100755 index 0000000..2929f57 --- /dev/null +++ b/configure.ac @@ -0,0 +1,21 @@ +AC_INIT([MediaCore HID Server], [1.0.0], [bugs@miqra.nl], [mediacore-hid], [http://www.miqra.nl/]) +AC_PREREQ([2.59]) +AM_INIT_AUTOMAKE([1.11 no-define foreign subdir-objects]) +AC_CONFIG_HEADERS([config.hpp]) +AC_PROG_CXX +AC_PROG_CC +PKG_CHECK_MODULES([DEPS], [dbus-c++-1 >= 0.9.0]) + +AC_CHECK_PROG(DBUSXX_CHECK,dbusxx-xml2cpp,yes) +if test x"$DBUSXX_CHECK" != x"yes" ; then + AC_MSG_ERROR([Please install libdbus-c++-bin before installing.]) +fi + +AC_PATH_PROG([DEBUILD], [dpkg-buildpackage], [notfound]) +AC_PATH_PROG([DHMAKE], [dh_make], [notfound]) + +AX_BOOST_BASE([1.49.0]) + +AC_CONFIG_FILES([Makefile]) + +AC_OUTPUT diff --git a/deb-dependencies.txt b/deb-dependencies.txt new file mode 100644 index 0000000..08743ab --- /dev/null +++ b/deb-dependencies.txt @@ -0,0 +1,6 @@ +autoconf-archive +libboost-all-dev +libdbus-c++-bin +libdbus-c++-dev +libdbus-1-dev + diff --git a/debian/control b/debian/control new file mode 100755 index 0000000..420a9fe --- /dev/null +++ b/debian/control @@ -0,0 +1,15 @@ +Source: mediacore-hid +Section: misc +Suite: stable +Priority: optional +Maintainer: Miqra Engineering Packaging +Build-Depends: debhelper (>= 8.0.0), autotools-dev, autoconf-archive, libboost-all-dev, libdbus-c++-bin, libdbus-c++-dev, libdbus-1-dev, libtclap-dev +Standards-Version: 3.9.3 +Debian-Version: 1 +Homepage: + +Package: mediacore-hid +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: I/O System for Mediacore Interface + I/O server system for mediacore that exposes the i/o through DBUS for use in other applications diff --git a/debian/postinst b/debian/postinst new file mode 100644 index 0000000..297f9e4 --- /dev/null +++ b/debian/postinst @@ -0,0 +1,40 @@ +#!/bin/sh +# postinst script for piio-server +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + update-rc.d mediacore-hid defaults + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/debian/prerm b/debian/prerm new file mode 100644 index 0000000..d3cf18b --- /dev/null +++ b/debian/prerm @@ -0,0 +1,39 @@ +#!/bin/sh +# prerm script for piio-server +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `upgrade' +# * `failed-upgrade' +# * `remove' `in-favour' +# * `deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + remove|upgrade|deconfigure) + update-rc.d -f mediacore-hid remove + ;; + + failed-upgrade) + ;; + + *) + echo "prerm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/debpackager.py b/debpackager.py new file mode 100644 index 0000000..d28325b --- /dev/null +++ b/debpackager.py @@ -0,0 +1,56 @@ +#!/usr/bin/python + +import os,sys +import glob +import shutil +import errno +from subprocess import call +import version + + +# Config +DEBFULLNAME="Miqra Engineering Packaging" +DEBEMAIL="packaging@miqra.nl" + + + +# Code +os.environ['DEBFULLNAME'] = DEBFULLNAME +os.environ['DEBEMAIL'] = DEBEMAIL + +## Debian package building +DEBBUILDDIR="deb_dist" +DEBSOURCEPKG="{0}_{1}.orig.tar.gz".format(version.PACKAGE, version.VERSION) +TARBALLNAME="{0}-{1}.tar.gz".format(version.PACKAGE, version.VERSION) +DEBSOURCEDIR=os.path.join(DEBBUILDDIR,"{0}-{1}".format(version.PACKAGE, version.VERSION)) +PKGDIR="{0}-{1}".format(version.PACKAGE, version.VERSION) + +PKGPATH=os.path.join(DEBBUILDDIR,PKGDIR) + +CWD = os.getcwd() + +try: + os.makedirs(DEBSOURCEDIR) +except OSError as exc: # Python >2.5 + if exc.errno == errno.EEXIST and os.path.isdir(DEBSOURCEDIR): + pass + else: raise + +call(["make","dist"]) + +shutil.move(TARBALLNAME , os.path.join(DEBBUILDDIR,DEBSOURCEPKG)) + +os.chdir(DEBBUILDDIR) +call(["tar","-xzvf",DEBSOURCEPKG]) + +print "Entering dir " + PKGDIR +os.chdir(PKGDIR) +print "Now in ", os.getcwd() +call(["dh_make --single -yes --copyright bsd"],shell=True) + +for f in glob.glob(os.path.join(CWD,"debian","*")): + dst = os.path.join(CWD,PKGPATH,"debian",os.path.basename(f)) + shutil.copy2(f,dst) + +call("debuild",shell=True) + diff --git a/debpackager.sh b/debpackager.sh new file mode 100755 index 0000000..98fb7a8 --- /dev/null +++ b/debpackager.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +rm version.py +make version.py +python debpackager.py + diff --git a/sigtest b/sigtest new file mode 100755 index 0000000..7da8631 Binary files /dev/null and b/sigtest differ diff --git a/sigtest.cpp b/sigtest.cpp new file mode 100644 index 0000000..b6f6d15 --- /dev/null +++ b/sigtest.cpp @@ -0,0 +1,31 @@ +#include +//#include +#include + +using namespace boost; +using namespace std; + +struct ClassA +{ + boost::signals2::signal SigA; + boost::signals2::signal SigB; +}; + +struct ClassB +{ + void PrintFoo() { cout << "Foo" << endl; } + void PrintInt(int i) { cout << "Bar: " << i << endl; } +}; + +int main() +{ + ClassA a; + ClassB b, b2; + + a.SigA.connect(bind(&ClassB::PrintFoo, &b)); + a.SigB.connect(bind(&ClassB::PrintInt, &b, _1)); + a.SigB.connect(bind(&ClassB::PrintInt, &b2, _1)); + + a.SigA(); + a.SigB(4); +} \ No newline at end of file diff --git a/src/buttontimer/buttontimer.cpp b/src/buttontimer/buttontimer.cpp new file mode 100644 index 0000000..9e9e6a8 --- /dev/null +++ b/src/buttontimer/buttontimer.cpp @@ -0,0 +1,120 @@ +#include "buttontimer.hpp" + +#include +#ifndef CLOCK_MONOTIC +# define CLOCK_MONOTIC CLOCK_REALTIME +#endif + +ButtonTimer::ButtonTimer(uint32_t shortpress_min_ms, uint32_t longpress_ms) +{ + shortpressMinTime = shortpress_min_ms; + longpressTime = longpress_ms; + eventLock = false; + + ThreadStart(); +} + +ButtonTimer::~ButtonTimer() +{ + ThreadStop(); + +} + +void ButtonTimer::RegisterPress(uint16_t keycode) +{ + MutexLock(); + // Prevent trouble when calling this from within one of our event listeners + if(eventLock) { MutexUnlock(); return; } + + pressRegistry[keycode] = now_ms(); + MutexUnlock(); +} + + +void ButtonTimer::RegisterRelease(uint16_t keycode) +{ + uint64_t now = now_ms(); + uint64_t then; + MutexLock(); + // Prevent trouble when calling this from within one of our event listeners + if(eventLock) { MutexUnlock(); return; } + + + + if(pressRegistry.count(keycode)) + { + // if it was a long press, the key code would already have been erased, so we + // can safely fire the onShortPress event + then = pressRegistry[keycode]; + // remove from registry after release, if it was a long press, the event should have already been fired + pressRegistry.erase(keycode); + + if(now - then > shortpressMinTime) + { + eventLock = true; // lock out trouble + onShortPress(keycode); + eventLock = false; // risk of trouble gone + } + } + + MutexUnlock(); +} + +void ButtonTimer::CancelPress(uint16_t keycode) +{ + // remove button id from map (but only if it is in the map already) + MutexLock(); + // Prevent trouble when calling this from within one of our event listeners + if(eventLock) { MutexUnlock(); return; } + + if(pressRegistry.count(keycode)) + { + pressRegistry.erase(keycode); + } + MutexUnlock(); +} + +void ButtonTimer::ThreadLoop() +{ + uint64_t now = now_ms(); + std::list btnList; + boost::optional valid; + MutexLock(); + // list through all the items + + for( std::map::iterator ii=pressRegistry.begin(); ii!=pressRegistry.end(); ++ii) + { + if(now - (ii->second) >= longpressTime) // if it is in overtime + { + // Schedule to process the thing + btnList.push_back(ii->first); + } + } + + // Process listed items + for (std::list::iterator it=btnList.begin(); it != btnList.end(); ++it) + { + pressRegistry.erase(*it); + // If any validators are connected, they can retun false to indicate that this connection is not + // allowed + eventLock = true; // lock out trouble + + valid = onValidatePress(*it); + if(valid.get_value_or(true)) + { + onLongPress(*it); + } + eventLock = false; // risk of trouble gone + } + + MutexUnlock(); + usleep(50000); +} + +int64_t ButtonTimer::now_ms(void) +{ + struct timespec now; + clock_gettime(CLOCK_MONOTIC, &now); + + return ((int64_t)now.tv_sec)*1000LL + (now.tv_nsec/1000000); +} diff --git a/src/buttontimer/buttontimer.hpp b/src/buttontimer/buttontimer.hpp new file mode 100644 index 0000000..442df34 --- /dev/null +++ b/src/buttontimer/buttontimer.hpp @@ -0,0 +1,35 @@ +#include "../thread/thread.hpp" +#include +#include + +#include + +// combiner which perfoms a kind of and function on all signal returns, returns true if no signals connected +class ButtonTimer : protected Thread +{ + public: + ButtonTimer(uint32_t shortpress_min_ms, uint32_t longpress_ms); + ~ButtonTimer(); + + void RegisterPress(uint16_t keycode); + void RegisterRelease(uint16_t keycode); + void CancelPress(uint16_t id); + + boost::signals2::signal onShortPress; + boost::signals2::signal onLongPress; + boost::signals2::signal onValidatePress; + + protected: + virtual void ThreadLoop(void); + + private: + boost::signals2::connection onThreadErrorConnection; + uint32_t longpressTime; + uint32_t shortpressMinTime; + std::map pressRegistry; + + bool eventLock; + static int64_t now_ms(void); + +}; + diff --git a/src/exception/baseexceptions.cpp b/src/exception/baseexceptions.cpp new file mode 100644 index 0000000..4450934 --- /dev/null +++ b/src/exception/baseexceptions.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include + +#include "baseexceptions.hpp" + + + +/************************************ +* * +* MsgException * +* * +*************************************/ +MsgException::MsgException() +{ +} + +MsgException::MsgException(const std::string& message) +{ + myMsg = message; +} + +MsgException::MsgException(const std::string &format,...) +{ + va_list argptr; + va_start(argptr, format); + init(format.c_str(),argptr); + va_end(argptr); +} + +MsgException::MsgException(const char * format,...) +{ + va_list argptr; + va_start(argptr, format); + init(format,argptr); + va_end(argptr); +} + +void MsgException::init(const char * format,va_list mArgs) +{ + uint32_t BUFFER_LEN = 256; + char buffer[BUFFER_LEN]; + // force buffer to zeroes + memset(buffer, 0x00, BUFFER_LEN); + // use vsnprintf to parse + vsnprintf(buffer, BUFFER_LEN-1, format, mArgs); + myMsg = std::string(buffer); +} + +void MsgException::init(const char * s) +{ + myMsg = std::string(s); +} + + +std::string MsgException::Message() +{ + return myMsg; +} + +const char* MsgException::what() +{ + formattedMsg = type() + " - " + myMsg; + return formattedMsg.c_str(); +} + +MsgException::~MsgException() throw () +{ + +} + + + + diff --git a/src/exception/baseexceptions.hpp b/src/exception/baseexceptions.hpp new file mode 100644 index 0000000..f6cce27 --- /dev/null +++ b/src/exception/baseexceptions.hpp @@ -0,0 +1,69 @@ +#ifndef __BASEEXCEPTIONS_HPP_ +#define __BASEEXCEPTIONS_HPP_ + +#include +#include + +class MsgException : public std::exception +{ + public: + MsgException(); + MsgException(const std::string &message); + MsgException(const std::string &format, ...); + MsgException(const char * format,...); + ~MsgException() throw(); + virtual const char* what(); + std::string Message(); + + protected: + virtual std::string type() { return "MsgException"; }; + void init(const char * format,va_list mArgs); + void init(const char * s); + + std::string myMsg; + std::string formattedMsg; +}; + +#define QUOTE(name) #name +#define DefineNewMsgException(cName) \ +class cName : public MsgException \ +{ \ + public: \ + cName(const std::string &format,...) { va_list argptr; va_start(argptr, format); init(format.c_str(),argptr); va_end(argptr); } \ + cName(const char * format,...) { va_list argptr; va_start(argptr, format); init(format,argptr); va_end(argptr); } \ + protected: \ + virtual std::string type() { return QUOTE(cName); }; \ +}; + + +DefineNewMsgException(WarningException); +DefineNewMsgException(InvalidArgumentException); +DefineNewMsgException(OperationFailedException); + + +/* +class WarningException : public MsgException +{ + protected: + virtual std::string type() { return "Warning"; }; +}; + + +class InvalidArgumentException : public MsgException +{ + protected: + virtual std::string type() { return "InvalidArgumentException"; }; +}; + +class OperationFailedException : public MsgException +{ + MsgException(const std::string& message); + MsgException(const std::string &format,...); + MsgException(const char * format,...) + protected: + virtual std::string type() { return "OperationFailedException"; }; +}; + +*/ + +#endif \ No newline at end of file diff --git a/src/gpio/gpio.cpp b/src/gpio/gpio.cpp new file mode 100644 index 0000000..ef7203a --- /dev/null +++ b/src/gpio/gpio.cpp @@ -0,0 +1,458 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpio.hpp" +#include "../log/log.hpp" +#include + + +#define GPIO_0_2_R1 0 /*!< \def Gpio pin 0/2 (rev1/rev2) with rev2 board code */ +#define GPIO_1_3_R1 1 /*!< \def Gpio pin 1/3 (rev1/rev2) with rev2 board code */ +#define GPIO_21_27_R1 21 /*!< \def Gpio pin 21/27 (rev1/rev2) with rev2 board code */ + +#define RDBUF_LEN 10 // length of read buffer +#define POLL_TIMEOUT 100 // timeout for polling function in ms (also the maximum delay time before thread is stopped + + +#define FALSE 0 +#define TRUE 1 + +using namespace std; + +/**************************** +* * +* PRIVATE DEFINTIONS * +* * +*****************************/ + +static unsigned int HardwareRevision(void); + +/**************************** +* * +* IOPIN OBJECT FUNCS * +* * +*****************************/ + +//! Create new IOPin object +GpioPin::GpioPin(int gpiopin, GpioDirection direction, GpioEdge edge) +{ + int result; + int pin_id; + char fTemp[GPIO_FN_MAXLEN]; + + // Set tag to empty pointer + Tag = NULL; + + pin_id = verifyPin(gpiopin); + if(pin_id < 0) + throw OperationFailedException("Gpio pin %d is not a valid Gpio for this Raspberry Pi board revision",gpiopin); + + // ensure that the Gpio pin is exported + try + { + pinPreExported = !(exportPin(pin_id)); + } + catch(OperationFailedException x) + { + throw x; + } + + // Prepare the iopin object + pin = pin_id; + + // Prepare the file names for the different files + snprintf(fTemp, GPIO_FN_MAXLEN-1, "/sys/class/gpio/gpio%d/direction", pin_id); + fnDirection = std::string(fTemp); + snprintf(fTemp, GPIO_FN_MAXLEN-1, "/sys/class/gpio/gpio%d/edge", pin_id); + fnEdge = std::string(fTemp); + snprintf(fTemp, GPIO_FN_MAXLEN-1, "/sys/class/gpio/gpio%d/value", pin_id); + fnValue = std::string(fTemp); + + // Initialize callbacks to NULL + + try + { + // set initial direction or die trying + setDirection(direction); + // set initial edge or die trying + setEdge(edge); + } + catch(OperationFailedException x) + { + if(!pinPreExported) + unexportPin(pin); + throw x; + } +} + + +//! Close the IOPin connection +GpioPin::~GpioPin() +{ + if(!pinPreExported) + unexportPin(pin); +} + +//! Get the actual used pin number of the IO Pin +int GpioPin::getPinNr() +{ + return pin; +} + +//! Get current direction of pin +GpioDirection GpioPin::getDirection() +{ + std::string s = readFile(fnDirection); + // got enough info in the first byte + if(s[0] == 'i') + return kDirectionIn; + else + return kDirectionOut; +} + +//! Set new value of pin +void GpioPin::setDirection(GpioDirection direction) +{ + if (direction == kDirectionIn) writeFile(fnDirection,"in\n"); + else if (direction == kDirectionOut) writeFile(fnDirection,"out\n"); +} + +//! Get current edge detection type +GpioEdge GpioPin::getEdge() +{ + std::string s = readFile(fnEdge); + switch(s[0]) // as the first letters of each result are all different + { + default : + case 'n': + case 'N': + return kEdgeNone; + case 'r': + case 'R': + return kEdgeRising; + case 'f': + case 'F': + return kEdgeFalling; + case 'b': + case 'B': + return kEdgeBoth; + + } +} + +//! Set edge detection type +void GpioPin::setEdge(GpioEdge edge) +{ + if (edge == kEdgeNone) writeFile(fnEdge,"none\n"); + else if (edge == kEdgeRising) writeFile(fnEdge,"rising\n"); + else if (edge == kEdgeFalling) writeFile(fnEdge,"falling\n"); + else if (edge == kEdgeBoth) writeFile(fnEdge,"both\n"); +} + + +//! Get current value of pin +bool GpioPin::getValue() +{ + std::string s = readFile(fnValue); + // got enough info in the first byte + if(s[0] == '1') + return true; + else + return false; +} + +//! Set new value of pin +void GpioPin::setValue(bool value) +{ + if (value) writeFile(fnValue,"1\n"); + else writeFile(fnValue,"0\n"); +} + + + +/**************************** +* * +* INTERRUPT FUNCS * +* * +*****************************/ + +void GpioPin::InterruptStart() +{ + if(!ThreadRunning()) + { + this->ThreadStart(); + } +} + +void GpioPin::InterruptStop() +{ + if(ThreadRunning()) + { + this->ThreadStop(); + } +} + + +void GpioPin::ThreadFunc() +{ + int fd,ret; + struct pollfd pfd; + char rdbuf[RDBUF_LEN]; + + memset(rdbuf, 0x00, RDBUF_LEN); + + fd=open(fnValue.c_str(), O_RDONLY); + if(fd<0) + throw OperationFailedException("Could not open file %s for reading: [%d] %s",fnValue.c_str(), errno, strerror(errno)); + + pfd.fd=fd; + pfd.events=POLLPRI; + + ret=read(fd, rdbuf, RDBUF_LEN-1); + if(ret<0) + { + close(fd); + throw OperationFailedException("Could not read from %s: [%d] %s",fnValue.c_str(), errno, strerror(errno)); + } + + while(ThreadRunning()) + { + memset(rdbuf, 0x00, RDBUF_LEN); + lseek(fd, 0, SEEK_SET); + ret=poll(&pfd, 1, POLL_TIMEOUT); + if(ret<0) // negative result is error + { + close(fd); + throw OperationFailedException("Could not poll %s: [%d] %s",fnValue.c_str(), errno, strerror(errno)); + } + + if(ret==0) + continue; // 0 bytes read is timeout, we should retry read + // ok, poll succeesed, now we read the value + ret=read(fd, rdbuf, RDBUF_LEN-1); + if(ret<0) + { + close(fd); + throw OperationFailedException("Could not read from %s: [%d] %s",fnValue.c_str(), errno, strerror(errno)); + } + + // Kill the loop now if the thread stopped during our poll + if(!ThreadRunning()) + break; + // Continue with doing the callback, if we're still enabled. + // Now, rdbuf[0] contains 0 or 1 depending on the trigger + onInterrupt(this, kEdgeFalling, !(rdbuf[0] == '0')); + } + close(fd); + +} + + + + +/**************************** +* * +* SUPPORT FUNCTIONS * +* * +*****************************/ + +//! open a file for writing and write text to it +void GpioPin::writeFile(std::string &fname, std::string &value) +{ + writeFile(fname,value.c_str()); +} + +void GpioPin::writeFile(std::string &fname, const char *value) +{ + FILE *fd; + if ((fd = fopen (fname.c_str(), "w")) == NULL) + throw OperationFailedException("Could not open %s for writing",fname.c_str()); + + fprintf (fd, value); + fclose(fd); +} + + + +//! open a file for reading and read some text from it +/*! + function will throw an exception on empty string, since the files we use it on + will always return a value. If they don't we have serious problems +*/ +std::string GpioPin::readFile(std::string &fname) +{ + FILE *fd; + char rdbuf[RDBUF_LEN]; + int ret; + + if ((fd = fopen (fname.c_str(), "r")) == NULL) + throw OperationFailedException("Could not open %s for reading",fname.c_str()); + + ret = fread(rdbuf,1,RDBUF_LEN -1, fd); + fclose(fd); + + if(ret<0) + { + throw OperationFailedException("Error reading from %s: [%d] %s",fname.c_str(), errno, strerror(errno)); + } + else if(ret == 0) + { + throw OperationFailedException("Got empty string reading from %s: [%d] %s",fname.c_str(), errno, strerror(errno)); + } + + rdbuf[ret] = '\0'; // Ensure null termination + + return std::string(rdbuf); +} + + +//! Verifies gpio pin number, and translates pin numbers (REV2) to the proper REV1 or REV2 board gpio pin numbers +/*! + \param gpiopin The pin number to verify + \return verified and translated gpio pin, or -1 if invalid +*/ +int GpioPin::verifyPin(int gpiopin) +{ + // List of valid Gpio pins + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + int validpinsRev1[17] = { 0, 1, 4, 7, 8, 9,10,11,14,15,17,18,21,22,23,24,25}; + int validpinsRev2[21] = { 2, 3, 4, 7, 8, 9,10,11,14,15,17,18,22,23,24,25,27,28,29,30,31}; + unsigned int i; + + // get hardware revision + unsigned int rev = HardwareRevision(); + if (rev < 4) + { // REV 1 BOARD + + // Translate pins to rev 1 equivalent + if(gpiopin == GPIO_0_2) + gpiopin = GPIO_0_2_R1; + else if(gpiopin == GPIO_1_3) + gpiopin = GPIO_1_3_R1; + else if(gpiopin == GPIO_21_27) + gpiopin = GPIO_21_27_R1; + + // Verify that the pin number is valid, otherwise return -1 + for(i=0; i + +/*! \file Gpio interrupt capture functions. Header file. +*/ + +// P1 Header pins (both board revisions) +#define GPIO_0_2 2 /*!< \def Gpio pin 0/2 (rev1/rev2) */ +#define GPIO_1_3 3 /*!< \def Gpio pin 1/3 (rev1/rev2) */ +#define GPIO_4 4 /*!< \def Gpio pin 4 */ +#define GPIO_7 7 /*!< \def Gpio pin 7 */ +#define GPIO_8 8 /*!< \def Gpio pin 8 */ +#define GPIO_9 9 /*!< \def Gpio pin 9 */ +#define GPIO_10 10 /*!< \def Gpio pin 10 */ +#define GPIO_11 11 /*!< \def Gpio pin 11 */ +#define GPIO_14 14 /*!< \def Gpio pin 14 */ +#define GPIO_15 15 /*!< \def Gpio pin 15 */ +#define GPIO_17 17 /*!< \def Gpio pin 17 */ +#define GPIO_18 18 /*!< \def Gpio pin 18 */ +#define GPIO_21_27 27 /*!< \def Gpio pin 21/27 (rev1/rev2) */ +#define GPIO_22 22 /*!< \def Gpio pin 22 */ +#define GPIO_23 23 /*!< \def Gpio pin 23 */ +#define GPIO_24 24 /*!< \def Gpio pin 24 */ +#define GPIO_25 25 /*!< \def Gpio pin 25 */ + +// P5 Header pins (only rev 2) +#define GPIO_28 28 /*!< \def Gpio pin 28 */ +#define GPIO_29 29 /*!< \def Gpio pin 29 */ +#define GPIO_30 30 /*!< \def Gpio pin 30 */ +#define GPIO_31 31 /*!< \def Gpio pin 31 */ + +#define GPIO_FN_MAXLEN 128 // length of file name field + +class GpioException : MsgException +{ + protected: + virtual std::string type() { return "GpioException"; } +}; + +//! Enum for specifying input/output direction +enum GpioDirection +{ + kDirectionOut = 0, + kDirectionIn = 1 +}; + +//! Enum for specifying edge detection type +enum GpioEdge +{ + kEdgeNone = 0, + kEdgeRising = 1, + kEdgeFalling = 2, + kEdgeBoth = 3 +}; + +class GpioPin : public Thread +{ + public: + GpioPin(int pinnr, GpioDirection direction, GpioEdge edge); + ~GpioPin(); + + //! Get the actual used pin number of the IO Pin + int getPinNr(); + + //! Get current direction of pin + GpioDirection getDirection(); + //! Set new direction of pin + void setDirection(GpioDirection direction); + + //! Get current edge detection type + GpioEdge getEdge(); + //! Set edge detection type + void setEdge(GpioEdge edge); + + //! Get current value of pin + bool getValue(); + //! Set new value of pin + void setValue(bool value); + + //! Start interrupt listener + void InterruptStart(); + //! Stop interrupt listener + void InterruptStop(); + + //! Signal on interrupt + boost::signals2::signal onInterrupt; + + // Tag to store application-dependant data + void * Tag; + + virtual void ThreadFunc(); + + private: + int pin; // Gpio pin number + std::string fnDirection; // File name for Direction file + std::string fnEdge; // File name for Edge file + std::string fnValue; // File name for Value file + bool pinPreExported; // Bool indicates if the pin was already exported + + + //! Verifies gpio pin number, and translates pin numbers (REV2) to the proper REV1 or REV2 board gpio pin numbers + static int verifyPin(int gpiopin); + + //! Export a certain Gpio pin + static bool exportPin(int gpiopin); + + //! Unexport a certain Gpio pin + static bool unexportPin(int gpiopin); + + //! open a file for writing and write text to it. + static void writeFile(std::string &fname, const char *value); + static void writeFile(std::string &fname, std::string &value); + + //! open a file for reading and read some text from it + static std::string readFile(std::string &fname); + +}; + + +#endif \ No newline at end of file diff --git a/src/i2c/i2c.c b/src/i2c/i2c.c new file mode 100644 index 0000000..2f47c0b --- /dev/null +++ b/src/i2c/i2c.c @@ -0,0 +1,127 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c.h" + +static unsigned int HardwareRevision(void); + +static unsigned int HardwareRevision(void) +{ + FILE * filp; + unsigned rev; + char buf[512]; + char term; + + rev = 0; + + filp = fopen ("/proc/cpuinfo", "r"); + + if (filp != NULL) + { + while (fgets(buf, sizeof(buf), filp) != NULL) + { + if (!strncasecmp("revision\t", buf, 9)) + { + if (sscanf(buf+strlen(buf)-5, "%x%c", &rev, &term) == 2) + { + if (term == '\n') break; + rev = 0; + } + } + } + fclose(filp); + } + return rev; +} + +int i2cInit(unsigned char address) +{ + int fd; // File descrition + char *fileName = "/dev/i2c-1"; // Name of the port we will be using (using revision 2 board's /dev/i2c-1 by default) + + unsigned int rev = HardwareRevision(); + if (rev < 4){ // Switch to revision 1 board's /dev/i2c-0 if revision number is below 4; + strcpy(fileName,"/dev/i2c-0"); + } + + if ((fd = open(fileName, O_RDWR)) < 0) { // Open port for reading and writing + return -1; + } + + if (ioctl(fd, I2C_SLAVE, address) < 0) { // Set the port options and set the address of the device we wish to speak to + return -2; + } + + return fd; +} + +void i2cClose(int fd) +{ + close(fd); +} + +int i2cReadReg8(int fd, unsigned char reg) +{ + unsigned char buf[1]; // Buffer for data being read/ written on the i2c bus + buf[0] = reg; // This is the register we wish to read from + + if ((write(fd, buf, 1)) != 1) { // Send register to read from + return -1; + } + + if (read(fd, buf, 1) != 1) { // Read back data into buf[] + return -2; + } + + return buf[0]; +} + +int i2cWriteReg8(int fd, unsigned char reg, unsigned char value) +{ + unsigned char buf[2]; + buf[0] = reg; // Commands for performing a ranging on the SRF08 + buf[1] = value; + + if ((write(fd, buf, 2)) != 2) { // Write commands to the i2c port + return -1; + } + return 0; +} + +int i2cReadReg16(int fd, unsigned char reg) +{ + unsigned char buf[2]; // Buffer for data being read/ written on the i2c bus + buf[0] = reg; // This is the register we wish to read from + + if ((write(fd, buf, 1)) != 1) { // Send register to read from + return -1; + } + + if (read(fd, buf, 2) != 2) { // Read back data into buf[] + return -2; + } + return (int)(buf[1] << 8) | (int)buf[0]; + + +} + +int i2cWriteReg16(int fd, unsigned char reg,unsigned short value) +{ + unsigned char buf[3]; + buf[0] = reg; // Commands for performing a ranging on the SRF08 + buf[1] = (unsigned char)( ( value >> 0 ) & 0xFF ); + buf[2] = (unsigned char)( ( value >> 8 ) & 0xFF ); + + if ((write(fd, buf, 3)) != 3) { // Write commands to the i2c port + return -1; + } + return 0; +} diff --git a/src/i2c/i2c.h b/src/i2c/i2c.h new file mode 100644 index 0000000..fda7bbe --- /dev/null +++ b/src/i2c/i2c.h @@ -0,0 +1,19 @@ +#ifndef __USER_I2C_H__ +#define __USER_I2C_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +int i2cInit(unsigned char address); +void i2cClose(int fd); +int i2cReadReg8(int fd, unsigned char reg); +int i2cWriteReg8(int fd, unsigned char reg, unsigned char value); +int i2cReadReg16(int fd, unsigned char reg); +int i2cWriteReg16(int fd, unsigned char reg,unsigned short value); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/log/log.cpp b/src/log/log.cpp new file mode 100644 index 0000000..2c87099 --- /dev/null +++ b/src/log/log.cpp @@ -0,0 +1,67 @@ +#include "log.hpp" +#include +#include +#include +#include + +std::string logPrintf(std::string &format,...) +{ + uint32_t BUFFER_LEN = 256; + char buffer[BUFFER_LEN]; + + va_list argptr; + va_start(argptr, format); + + // force buffer to zeroes + memset(buffer, 0x00, BUFFER_LEN); + // use vsnprintf to parse + vsnprintf(buffer, BUFFER_LEN-1, format.c_str(), argptr); + va_end(argptr); + + return std::string(buffer); +} + +Log::Log(std::string ident, int facility) { + facility_ = facility; + priority_ = LOG_DEBUG; + strncpy(ident_, ident.c_str(), sizeof(ident_)); + ident_[sizeof(ident_)-1] = '\0'; + + openlog(ident_, LOG_PID, facility_); +} + +int Log::sync() { + if (buffer_.length()) { + syslog(priority_, buffer_.c_str()); + buffer_.erase(); + priority_ = LOG_DEBUG; // default to debug for each message + } + return 0; +} + +int Log::overflow(int c) { + if (c != EOF) { + buffer_ += static_cast(c); + } else { + sync(); + } + return c; +} + +std::ostream& operator<< (std::ostream& os, const LogPriority& log_priority) { + static_cast(os.rdbuf())->priority_ = (int)log_priority; + return os; +} + +// Static init function +void Log::Init(std::string ident, int facility) +{ + std::clog.rdbuf(new Log(ident, facility)); + +} + +// Static init function with default facility +void Log::Init(std::string ident) +{ + Log::Init(ident,LOG_LOCAL0); +} \ No newline at end of file diff --git a/src/log/log.hpp b/src/log/log.hpp new file mode 100644 index 0000000..b96e510 --- /dev/null +++ b/src/log/log.hpp @@ -0,0 +1,42 @@ +#ifndef __LOG_HPP__ +#define __LOG_HPP__ + + +#include +#include + +enum LogPriority { + kLogEmerg = LOG_EMERG, // system is unusable + kLogAlert = LOG_ALERT, // action must be taken immediately + kLogCrit = LOG_CRIT, // critical conditions + kLogCritical = LOG_CRIT, // critical conditions + kLogErr = LOG_ERR, // error conditions + kLogError = LOG_ERR, // error conditions + kLogWarning = LOG_WARNING, // warning conditions + kLogNotice = LOG_NOTICE, // normal, but significant, condition + kLogInfo = LOG_INFO, // informational message + kLogDebug = LOG_DEBUG // debug-level message +}; + +std::ostream& operator<< (std::ostream& os, const LogPriority& log_priority); + +class Log : public std::basic_streambuf > { +public: + explicit Log(std::string ident, int facility); + static void Init(std::string ident, int facility); + static void Init(std::string ident); +protected: + int sync(); + int overflow(int c); + +private: + friend std::ostream& operator<< (std::ostream& os, const LogPriority& log_priority); + std::string buffer_; + int facility_; + int priority_; + char ident_[50]; +}; + +std::string logPrintf(std::string &format,...); + +#endif \ No newline at end of file diff --git a/src/mc-hid-introspect.xml b/src/mc-hid-introspect.xml new file mode 100644 index 0000000..5a3b926 --- /dev/null +++ b/src/mc-hid-introspect.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/mc-hid-server-glue.hpp b/src/mc-hid-server-glue.hpp new file mode 100644 index 0000000..1e74d83 --- /dev/null +++ b/src/mc-hid-server-glue.hpp @@ -0,0 +1,223 @@ + +/* + * This file was automatically generated by dbusxx-xml2cpp; DO NOT EDIT! + */ + +#ifndef __dbusxx__src_mc_hid_server_glue_hpp__ADAPTOR_MARSHAL_H +#define __dbusxx__src_mc_hid_server_glue_hpp__ADAPTOR_MARSHAL_H + +#include +#include + +namespace nl { +namespace miqra { +namespace MediaCore { + +class Hid_adaptor +: public ::DBus::InterfaceAdaptor +{ +public: + + Hid_adaptor() + : ::DBus::InterfaceAdaptor("nl.miqra.MediaCore.Hid") + { + register_method(Hid_adaptor, SetColor, _SetColor_stub); + register_method(Hid_adaptor, ClearColor, _ClearColor_stub); + register_method(Hid_adaptor, PulseColor, _PulseColor_stub); + register_method(Hid_adaptor, JackState, _JackState_stub); + } + + ::DBus::IntrospectedInterface *introspect() const + { + static ::DBus::IntrospectedArgument SetColor_args[] = + { + { "r", "y", true }, + { "g", "y", true }, + { "b", "y", true }, + { 0, 0, 0 } + }; + static ::DBus::IntrospectedArgument ClearColor_args[] = + { + { 0, 0, 0 } + }; + static ::DBus::IntrospectedArgument PulseColor_args[] = + { + { "r", "y", true }, + { "g", "y", true }, + { "b", "y", true }, + { "interval", "d", true }, + { 0, 0, 0 } + }; + static ::DBus::IntrospectedArgument JackState_args[] = + { + { "jackedin", "b", false }, + { 0, 0, 0 } + }; + static ::DBus::IntrospectedArgument ButtonDown_args[] = + { + { "button", "s", false }, + { 0, 0, 0 } + }; + static ::DBus::IntrospectedArgument ButtonUp_args[] = + { + { "button", "s", false }, + { 0, 0, 0 } + }; + static ::DBus::IntrospectedArgument JackIn_args[] = + { + { 0, 0, 0 } + }; + static ::DBus::IntrospectedArgument JackOut_args[] = + { + { 0, 0, 0 } + }; + static ::DBus::IntrospectedArgument ButtonPress_args[] = + { + { "button", "s", false }, + { 0, 0, 0 } + }; + static ::DBus::IntrospectedArgument ButtonLongPress_args[] = + { + { "button", "s", false }, + { 0, 0, 0 } + }; + static ::DBus::IntrospectedMethod Hid_adaptor_methods[] = + { + { "SetColor", SetColor_args }, + { "ClearColor", ClearColor_args }, + { "PulseColor", PulseColor_args }, + { "JackState", JackState_args }, + { 0, 0 } + }; + static ::DBus::IntrospectedMethod Hid_adaptor_signals[] = + { + { "ButtonDown", ButtonDown_args }, + { "ButtonUp", ButtonUp_args }, + { "JackIn", JackIn_args }, + { "JackOut", JackOut_args }, + { "ButtonPress", ButtonPress_args }, + { "ButtonLongPress", ButtonLongPress_args }, + { 0, 0 } + }; + static ::DBus::IntrospectedProperty Hid_adaptor_properties[] = + { + { 0, 0, 0, 0 } + }; + static ::DBus::IntrospectedInterface Hid_adaptor_interface = + { + "nl.miqra.MediaCore.Hid", + Hid_adaptor_methods, + Hid_adaptor_signals, + Hid_adaptor_properties + }; + return &Hid_adaptor_interface; + } + +public: + + /* properties exposed by this interface, use + * property() and property(value) to get and set a particular property + */ + +public: + + /* methods exported by this interface, + * you will have to implement them in your ObjectAdaptor + */ + virtual void SetColor(const uint8_t& r, const uint8_t& g, const uint8_t& b) = 0; + virtual void ClearColor() = 0; + virtual void PulseColor(const uint8_t& r, const uint8_t& g, const uint8_t& b, const double& interval) = 0; + virtual bool JackState() = 0; + +public: + + /* signal emitters for this interface + */ + void ButtonDown(const std::string& arg1) + { + ::DBus::SignalMessage sig("ButtonDown"); + ::DBus::MessageIter wi = sig.writer(); + wi << arg1; + emit_signal(sig); + } + void ButtonUp(const std::string& arg1) + { + ::DBus::SignalMessage sig("ButtonUp"); + ::DBus::MessageIter wi = sig.writer(); + wi << arg1; + emit_signal(sig); + } + void JackIn() + { + ::DBus::SignalMessage sig("JackIn"); + emit_signal(sig); + } + void JackOut() + { + ::DBus::SignalMessage sig("JackOut"); + emit_signal(sig); + } + void ButtonPress(const std::string& arg1) + { + ::DBus::SignalMessage sig("ButtonPress"); + ::DBus::MessageIter wi = sig.writer(); + wi << arg1; + emit_signal(sig); + } + void ButtonLongPress(const std::string& arg1) + { + ::DBus::SignalMessage sig("ButtonLongPress"); + ::DBus::MessageIter wi = sig.writer(); + wi << arg1; + emit_signal(sig); + } + +private: + + /* unmarshalers (to unpack the DBus message before calling the actual interface method) + */ + ::DBus::Message _SetColor_stub(const ::DBus::CallMessage &call) + { + ::DBus::MessageIter ri = call.reader(); + + uint8_t argin1; ri >> argin1; + uint8_t argin2; ri >> argin2; + uint8_t argin3; ri >> argin3; + SetColor(argin1, argin2, argin3); + ::DBus::ReturnMessage reply(call); + return reply; + } + ::DBus::Message _ClearColor_stub(const ::DBus::CallMessage &call) + { + ::DBus::MessageIter ri = call.reader(); + + ClearColor(); + ::DBus::ReturnMessage reply(call); + return reply; + } + ::DBus::Message _PulseColor_stub(const ::DBus::CallMessage &call) + { + ::DBus::MessageIter ri = call.reader(); + + uint8_t argin1; ri >> argin1; + uint8_t argin2; ri >> argin2; + uint8_t argin3; ri >> argin3; + double argin4; ri >> argin4; + PulseColor(argin1, argin2, argin3, argin4); + ::DBus::ReturnMessage reply(call); + return reply; + } + ::DBus::Message _JackState_stub(const ::DBus::CallMessage &call) + { + ::DBus::MessageIter ri = call.reader(); + + bool argout1 = JackState(); + ::DBus::ReturnMessage reply(call); + ::DBus::MessageIter wi = reply.writer(); + wi << argout1; + return reply; + } +}; + +} } } +#endif //__dbusxx__src_mc_hid_server_glue_hpp__ADAPTOR_MARSHAL_H diff --git a/src/mc-hid-server.cpp b/src/mc-hid-server.cpp new file mode 100644 index 0000000..ab63d3e --- /dev/null +++ b/src/mc-hid-server.cpp @@ -0,0 +1,512 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "mc-hid-server.hpp" +#include "log/log.hpp" + +#include +#include +#include +#include +#include +#include + + +// For getting current time +#include +#ifndef CLOCK_MONOTIC +# define CLOCK_MONOTIC CLOCK_REALTIME +#endif + +#define NOISE_TIMEOUT_MS 400 +#define GPIO_INT_PIN 4 +#define MCP23017_ADR 0x20 + +#define BTN_ESCAPE 0x0001 +#define BTN_FAVORITES 0x0002 +#define BTN_RECENT 0x0004 + +#define BTN_REWIND 0x0008 +#define BTN_PLAYPAUSE 0x0010 +#define BTN_FASTFORWARD 0x0020 + +#define BTN_CANCEL 0x0040 +#define BTN_OK 0x0080 +#define BTN_UP 0x0100 +#define BTN_DOWN 0x0200 + +#define SENSE_MINIJACK 0x0400 + +#define PIN_RED 12 +#define PIN_GREEN 13 +#define PIN_BLUE 14 + +#define PULSE_STEPS 32 +#define PULSE_WAIT_US 20000 + +#define TRUE 1 +#define FALSE 0 + +using namespace std; + +static const char *HID_SERVER_NAME = "nl.miqra.MediaCore.Hid"; +static const char *HID_SERVER_PATH = "/nl/miqra/MediaCore/Hid"; + + +// function to get current time in ms +int64_t now_ms(void); +// signal handler +void niam(int sig); + +HidServer::HidServer(DBus::Connection &connection) + : DBus::ObjectAdaptor(connection, HID_SERVER_PATH) +{ + + // Initialize class variables if needed + noiseTimeout = 0; // Value of 0 means: no noise timeout + + // Initialize button timer + btnTimer = new ButtonTimer(25,6000); // Short press should take at leas 25 ms, and a Long press takes 6 seconds + onShortPressConnection = btnTimer->onShortPress.connect(boost::bind(&HidServer::onShortPress, this, _1)); + onLongPressConnection = btnTimer->onLongPress.connect(boost::bind(&HidServer::onLongPress, this, _1)); + onValidatePressConnection = btnTimer->onValidatePress.connect(boost::bind(&HidServer::onValidatePress, this, _1)); + + // **** Initialize the GPIO Interrupt pin + + clog << kLogInfo << PACKAGE_STRING << " starting..." << endl; + + try + { + initHardware(); + } + catch(std::exception x) + { + clog << kLogCrit << "Fatal error during hardware initialization: " << x.what() << endl << "Quitting now... " << endl; + niam(0); + } + + clog << kLogInfo << "Initialization complete, listening to HID requests." << endl; +} + +HidServer::~HidServer() +{ + ClearColor(); + + onInterruptErrorConnection.disconnect(); + onInterruptConnection.disconnect(); + + delete intpin; intpin = NULL; + delete mcp; mcp = NULL; + delete btnTimer; btnTimer = NULL; + + clog << kLogInfo << "Stopping normally" << endl; +} + +void HidServer::initHardware(void) +{ + HWConfig hwConfig; + unsigned short value; + + clog << kLogInfo << "Opening GPIO pin " << GPIO_INT_PIN << " for interrupt listening" << endl; + // open pin + intpin = new GpioPin( GPIO_INT_PIN, // Pin number + kDirectionIn, // Data direction + kEdgeFalling); // Interrupt edge + + // **** Initialize the MCP23017 + + // Hardware config + hwConfig.DISSLEW = false; // Leave slew rate control enabled + hwConfig.INT_MIRROR = true; // Interconnect I/O pins + hwConfig.INT_ODR = false; // Interrupt is not an open drain + hwConfig.INT_POL = false; // Interrupt is Active-Low + + clog << kLogInfo << "Opening MCP23017 IO expander on I2C address " << MCP23017_ADR << endl; + + try + { + // Initialize chip system + mcp = new Mcp23017( MCP23017_ADR, // adr + 0x0fff, // iodir + 0x0fff, // ipol + 0x0fff, // pullup + hwConfig, // Hardware Config + true); // Swap A/B + + try + { + clog << kLogInfo << "Enabling interrupts on IO Expander" << endl; + // Configure interrupt + mcp->IntConfig( 0x0000, // defval + 0x0000, // intcon + 0x07ff); // int enable + + + clog << kLogInfo << "Starting interrupt listener on GPIO pin " << GPIO_INT_PIN << endl; + + // Initialize interrupts + + onInterruptErrorConnection.disconnect(); + onInterruptConnection.disconnect(); + + onInterruptErrorConnection = intpin->onThreadError.connect(boost::bind(&HidServer::onInterruptError, this, _1, _2)); + onInterruptConnection = intpin->onInterrupt.connect(boost::bind(&HidServer::onInterrupt, this, _1, _2, _3)); + + // Start interrupt event on interrupt pin + intpin->InterruptStart(); + + clog << kLogInfo << "Performing initial read of IO Expander values to clear any pending interrupts and initialize Jack state" << endl; + // Read initial value to clear any current interrupts + value = mcp->getValue(); + + // Pass the initial mini-jack state to any listeners + if(value && SENSE_MINIJACK) + JackIn(); + else + JackOut(); + + // Prepare the R/G/B pins for possible PWM usage + mcp->setPwmState(PIN_RED,TRUE); + mcp->setPwmState(PIN_GREEN,TRUE); + mcp->setPwmState(PIN_BLUE,TRUE); + + } + catch(OperationFailedException x) + { + delete mcp; mcp = NULL; + throw x; + } + + } + catch(OperationFailedException x) + { + delete intpin; intpin = NULL; + throw x; + } +} + +void HidServer::SetColor(const uint8_t& r, const uint8_t& g, const uint8_t& b) +{ + clog << kLogDebug << "Got color request: RGB("<< (int)r << "," << (int)g << "," << (int)b << ") - "; + // Check if we can do solid colors (no need for PWM) + if( (r == 0 || r==255) && + (g == 0 || g==255) && + (b == 0 || b==255)) + { + // if so, then 0 is off and non-zero is on + clog << "Using ON/OFF to produce color" << endl; + // but first disable pwm + mcp->PwmStop(); // waits for completion + mcp->setPin(PIN_RED, (bool)r); + mcp->setPin(PIN_GREEN, (bool)g); + mcp->setPin(PIN_BLUE, (bool)b); + + } + else + { + clog << "Using PWM to produce color" << endl; + // we need PWM, so enable it. + mcp->PwmStart(); + + mcp->setPwmLedValue(PIN_RED, r); + mcp->setPwmLedValue(PIN_GREEN, g); + mcp->setPwmLedValue(PIN_BLUE, b); + } + +} + +void HidServer::PulseColor(const uint8_t& r, const uint8_t& g, const uint8_t& b, const double& interval) +{ + pulseR = r; + pulseG = g; + pulseB = b; + + pulseDirUp = true; + pulseMin = 0; + pulseI = pulseMin; + + clog << kLogDebug << "Got pulse color request: RGB("<< (int)r << "," << (int)g << "," << (int)b << ")" << endl; + + pulseTime = (int32_t)((interval * 1000000) / (2 * PULSE_STEPS)); + // Start pulse thread and PWM system + ThreadStart(); + mcp->PwmStart(); + +} + +// contains the pulse loop +void HidServer::ThreadLoop() +{ + uint8_t myR, myG, myB; + int32_t factor = pulseI*100; + + + myR = (pulseR * factor)/(100 * PULSE_STEPS); + myG = (pulseG * factor)/(100 * PULSE_STEPS); + myB = (pulseB * factor)/(100 * PULSE_STEPS); + + mcp->setPwmLedValue(PIN_RED, myR); + mcp->setPwmLedValue(PIN_GREEN, myG); + mcp->setPwmLedValue(PIN_BLUE, myB); + + + if(pulseDirUp) + { + pulseI++; + if(pulseI>= PULSE_STEPS) + pulseDirUp = false; + } + else + { + pulseI--; + if(pulseI <= pulseMin) + pulseDirUp = true; + } + + usleep(pulseTime); + +} + +void HidServer::ClearColor() +{ + clog << kLogDebug << "Got request to clear colors" << endl; + + // Stop pulse thread + ThreadStop(); + + // Disable PWM and turn all colors off + mcp->PwmStop(); // waits for completion + mcp->setPin(PIN_RED, false); + mcp->setPin(PIN_GREEN, false); + mcp->setPin(PIN_BLUE, false); +} + +bool HidServer::JackState() +{ + uint16_t value; + + try + { + value = mcp->getValue(); + } + catch(OperationFailedException x) + { + // Log the exception + clog << kLogError << "Error while retrieving Jack State: " << x.what() << endl; + + // Return false on error + return false; + } + // Pass the current mini-jack state to any listeners + if(value && SENSE_MINIJACK) + return true; + else + return false; +} + +bool HidServer::onValidatePress(uint16_t keycode) +{ + // Check if the key is still pressed before sending out a long press + + if(mcp->getValue() & keycode) + return true; + else + return false; +} + +void HidServer::onShortPress(uint16_t keycode) +{ + clog << kLogDebug << "SHORT PRESS : " << getBtnName(keycode) << endl; + ButtonPress(getBtnName(keycode)); +} + +void HidServer::onLongPress(uint16_t keycode) +{ + clog << kLogDebug << "LONG PRESS : " << getBtnName(keycode) << endl; + ButtonLongPress(getBtnName(keycode)); +} + +void HidServer::keyUp(uint16_t keycode) +{ + if(keycode == SENSE_MINIJACK) + { + JackOut(); + } + else + { + ButtonUp(getBtnName(keycode)); + btnTimer->RegisterRelease(keycode); + } +} + +void HidServer::keyDown(uint16_t keycode) +{ + if(keycode == SENSE_MINIJACK) + { + JackIn(); + } + else + { + ButtonDown(getBtnName(keycode)); + btnTimer->RegisterPress(keycode); + } +} + +// Convert binary keycode into a string for ease of use in e.g. python or other dbus bindings +std::string HidServer::getBtnName(uint16_t keycode) +{ + switch(keycode) + { + case BTN_ESCAPE : + return "BTN_ESCAPE"; + case BTN_FAVORITES : + return "BTN_FAVORITES"; + case BTN_RECENT : + return "BTN_RECENT"; + + case BTN_REWIND : + return "BTN_REWIND"; + case BTN_PLAYPAUSE : + return "BTN_PLAYPAUSE"; + case BTN_FASTFORWARD : + return "BTN_FASTFORWARD"; + + case BTN_CANCEL : + return "BTN_CANCEL"; + case BTN_OK : + return "BTN_OK"; + case BTN_UP : + return "BTN_UP"; + case BTN_DOWN : + return "BTN_DOWN"; + case SENSE_MINIJACK : + return "SENSE_MINIJACK"; + default: + return "BTN_NONE"; + } + +} + +void HidServer::onInterrupt(GpioPin * sender, GpioEdge edge, bool pinval) +{ + uint16_t intf,intcap, keycode; + uint8_t i, bitcount; + + if(edge != kEdgeFalling) // Only continue on falling edge + return; + + + intf = mcp->getIntF(); + intcap = mcp->getIntCap(); + + // Check if we are in a noise timeout, and cancel if so. + if(noiseTimeout !=0 && noiseTimeout > now_ms()) + return; + else + noiseTimeout = 0; + + + // Check if only one bit is set in the interrupt cap: This indicated normal operation. + // If more than one bit is set, we should assume noise + bitcount = 0; + for(i=0;i<16;i++) + { + if(intf & (1 << i)) // Check if this key is set + bitcount++; + } + + //printf("Interrupt!\n INTF : 0x%04x\n INTCAP : 0x%04x\n KEYCODE: 0x%04x\n\n", intf, intcap, keycode); + + if(bitcount > 0 && bitcount <= 3 ) // anything above 3 key interrupts at the same time is considered noise + { + + for(i=0; i < 16; i++) + { + if(intf & (1 << i)) // check if this keycode is set + { + keycode = (1 << i); + if(intcap & keycode) + { + keyDown(keycode); + if(keycode == SENSE_MINIJACK) + clog << kLogDebug << "Jacked IN " << endl; + else + clog << kLogDebug << "Button down : " << getBtnName(keycode) << endl; + } + else + { + keyUp(keycode); + if(keycode == SENSE_MINIJACK) + clog << kLogDebug << "Jacked OUT " << endl; + else + clog << kLogDebug << "Button up : " << getBtnName(keycode) << endl; + } + } + } + } + else + { + clog << kLogDebug << "!!! Input Noise !!! (Timeout: " << NOISE_TIMEOUT_MS << "ms)" << endl; + noiseTimeout = now_ms() + NOISE_TIMEOUT_MS; + } + + +} + +void HidServer::onInterruptError(Thread* sender, ThreadException x) +{ + // When this function is called, the interrupt thread will have stopped + clog << kLogErr << "Error in interrupt listener: " << x.what() << endl; + clog << kLogErr << "Attempting to restart interrupt listener... " << endl; + try + { + delete mcp; mcp = NULL; + delete intpin; intpin = NULL; + initHardware(); // Attempt to re-init the chip system. Quit on failure + clog << kLogInfo << "Succesfully restarted interrupt listener" << endl; + } + catch(std::exception x2) + { + clog << kLogCrit << "Fatal error while attempting to restart interrupt listener : " << x2.what() << endl << "Quitting now... " << endl; + niam(0); + } +} + +// get current time in milliseconds +int64_t now_ms(void) +{ + struct timespec now; + clock_gettime(CLOCK_MONOTIC, &now); + + return ((int64_t)now.tv_sec)*1000LL + (now.tv_nsec/1000000); +} + + +DBus::BusDispatcher dispatcher; + +void niam(int sig) +{ + dispatcher.leave(); +} + +int main() +{ + signal(SIGTERM, niam); + signal(SIGINT, niam); + + DBus::default_dispatcher = &dispatcher; + + // Initialize clog to be redirected to syslog key "mediacore.hid.server" + Log::Init("mediacore.hid"); + + DBus::Connection conn = DBus::Connection::SystemBus(); + conn.request_name(HID_SERVER_NAME); + + HidServer server(conn); + + + dispatcher.enter(); + + return 0; +} \ No newline at end of file diff --git a/src/mc-hid-server.hpp b/src/mc-hid-server.hpp new file mode 100644 index 0000000..d47e022 --- /dev/null +++ b/src/mc-hid-server.hpp @@ -0,0 +1,65 @@ +#ifndef __MC_HID_SERVER_HPP +#define __MC_HID_SERVER_HPP + +#include +#include +#include + +#include "mc-hid-server-glue.hpp" + +#include "gpio/gpio.hpp" +#include "mcp23017/mcp23017.hpp" +#include "thread/thread.hpp" +#include "buttontimer/buttontimer.hpp" + +class HidServer + : public nl::miqra::MediaCore::Hid_adaptor, // << This will be generated by the makefile using dbusxx-xml2cpp on mc-hid-introspect.xml + public DBus::IntrospectableAdaptor, + public DBus::ObjectAdaptor, + public Thread +{ +public: + + HidServer(DBus::Connection &connection); + ~HidServer(); + + virtual void SetColor(const uint8_t& r, const uint8_t& g, const uint8_t& b); + virtual void PulseColor(const uint8_t& r, const uint8_t& g, const uint8_t& b, const double& interval); + virtual void ClearColor(); + virtual bool JackState(); + + void onInterrupt(GpioPin * sender, GpioEdge edge, bool pinval); + void onInterruptError(Thread * sender, ThreadException x); + + bool onValidatePress(uint16_t keycode); + void onShortPress(uint16_t keycode); + void onLongPress(uint16_t keycode); + +protected: + virtual void ThreadLoop(); + +private: + Mcp23017 *mcp; + GpioPin *intpin; + ButtonTimer *btnTimer; + + int64_t noiseTimeout; + + uint8_t pulseR, pulseG, pulseB,pulseI,pulseMin; + int32_t pulseTime; + bool pulseDirUp; + + void initHardware(void); + + void keyUp(uint16_t keycode); + void keyDown(uint16_t keycode); + std::string getBtnName(uint16_t keycode); + + boost::signals2::connection onInterruptConnection; + boost::signals2::connection onInterruptErrorConnection; + boost::signals2::connection onShortPressConnection; + boost::signals2::connection onLongPressConnection; + boost::signals2::connection onValidatePressConnection; +}; + +#endif//__MC_HID_SERVER_HPP \ No newline at end of file diff --git a/src/mc-hid-server.o b/src/mc-hid-server.o new file mode 100644 index 0000000..c66f247 Binary files /dev/null and b/src/mc-hid-server.o differ diff --git a/src/mcp23017/mcp23017.cpp b/src/mcp23017/mcp23017.cpp new file mode 100644 index 0000000..45be7e8 --- /dev/null +++ b/src/mcp23017/mcp23017.cpp @@ -0,0 +1,968 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#include "mcp23017.hpp" +#include "../i2c/i2c.h" + +/**************************** +* * +* DEFINITIONS * +* * +*****************************/ + +//! True/False definitions +#define TRUE 1 +#define FALSE 0 + + +/************************************ +* * +* INTERNAL FUNCTION DEFINITIONS * +* * +*************************************/ + + + +/************************************ +* * +* REGISTER ADDRESS DEFINITIONS * +* * +*************************************/ + +// This interface used only BANK=0 addresses, which are defined below +// The Init function ensures that the device is started in BANK=0 mode + +#define IODIR 0x00 +#define IODIRA 0x00 +#define IODIRB 0x01 + +#define IPOL 0x02 +#define IPOLA 0x02 +#define IPOLB 0x03 + +#define GPINTEN 0x04 +#define GPINTENA 0x04 +#define GPINTENB 0x05 + +#define DEFVAL 0x06 +#define DEFVALA 0x06 +#define DEFVALB 0x07 + +#define INTCON 0x08 +#define INTCONA 0x08 +#define INTCONB 0x09 + +#define IOCON 0x0A +#define IOCONA 0x0A +#define IOCONB 0x0B + +#define GPPU 0x0C +#define GPPUA 0x0C +#define GPPUB 0x0D + +#define INTF 0x0E +#define INTFA 0x0E +#define INTFB 0x0F + +#define INTCAP 0x10 +#define INTCAPA 0x10 +#define INTCAPB 0x11 + +#define GPIO 0x12 +#define GPIOA 0x12 +#define GPIOB 0x13 + +#define OLAT 0x14 +#define OLATA 0x14 +#define OLATB 0x15 + +/************************************ +* * +* PSEUDO-GLOBAL VARIABLES * +* * +*************************************/ + +// Reverse address lookup tables for registers. +// Used mainly in error reporting + +// 8 Bit names +const char * Registers8[22] = +{ + "IODIRA", // 00 + "IODIRB", // 01 + "IPOLA", // 02 + "IPOLB", // 03 + "GPINTENA", // 04 + "GPINTENB", // 05 + "DEFVALA", // 06 + "DEFVALB", // 07 + "INTCONA", // 08 + "INTCONB", // 09 + "IOCONA", // 0A + "IOCONB", // 0B + "GPPUA", // 0C + "GPPUB", // 0D + "INTFA", // 0E + "INTFB", // 0F + "INTCAPA", // 10 + "INTCAPB", // 11 + "GPIOA", // 12 + "GPIOB", // 13 + "OLATA", // 14 + "OLATB" // 15 +}; + +// 16 bit names +const char * Registers16[22] = +{ + "IODIR", // 00 + "IODIR", // 01 + "IPOL", // 02 + "IPOL", // 03 + "GPINTEN", // 04 + "GPINTEN", // 05 + "DEFVAL", // 06 + "DEFVAL", // 07 + "INTCON", // 08 + "INTCON", // 09 + "IOCON", // 0A + "IOCON", // 0B + "GPPU", // 0C + "GPPU", // 0D + "INTF", // 0E + "INTF", // 0F + "INTCAP", // 10 + "INTCAP", // 11 + "GPIO", // 12 + "GPIO", // 13 + "OLAT", // 14 + "OLAT" // 15 +}; + + + + +/************************************ +* * +* STRUCT RELATED FUNCTIONS * +* * +*************************************/ + +HWConfig::HWConfig() +{ + DISSLEW = false; // Leave slew rate control enabled + INT_MIRROR = true; // Mirror Interrupt pins + INT_ODR = false; // Interrupt is not an open drain + INT_POL = false; // Interrupt is Active-Low +} + +uint8_t HWConfig::parse() +{ + uint8_t val = 0; + if(DISSLEW) + val |= 0x10; + if(INT_MIRROR) + val |= 0x40; + if(INT_ODR) + val |= 0x04; + if(INT_POL) + val |= 0x02; + + val |= 0x20; // Disable the address pointer + + return val; +} + + + +//! Internal helper function that translates an IOConfig structure to an unsigned int denoting the proper configuration for an MCP23017 + +/************************************ +* * +* CONNECTION SETUP * +* * +*************************************/ + +//! Open a new connection to the MCP23017 device, and initialize it. +Mcp23017::Mcp23017( uint8_t adr, + uint16_t iodir, + uint16_t ipol, + uint16_t pullup, + HWConfig hwcfg, + bool swapAB) +{ + int i; + // Try opening the port for the specific address + fp = i2cInit(adr); + if(fp <= 0) // error + { + if(fp == 0) + throw OperationFailedException("Got NULL pointer to I2C File"); + else if (fp == -1) + throw OperationFailedException("Could not open I2C device for reading and writing"); + else if (fp == -2) + throw OperationFailedException("Could not set I2C destination address"); + else + throw OperationFailedException("Got unspecified error [%d] opening I2C device",fp); + } + + // Initialize objcect variables + this->adr = adr; // set address + this->swapAB = swapAB; // set swapAB value; + + this->pwm_enabled = false; + this->pwm_mask = 0x0000; + this->pwm_prev_val = 0x0000; + + // Initialize the PWM values to null + for(i=0; i< 16; i++) + { + this->pwm_values[i] = 0; + this->pwm_v_values[i] = 0; + } + + // Initialize to default pwm speed + this->pwm_tick_delay_us = 800; // 800us * 16 steps would result in 78Hz + this->pwm_ticks = 16; + + // Copy HWConfig to key + this->hwConfig = hwcfg; + + // Use the following trick to assure we're talking to the MCP23017 in BANK=0 mode, so we can properly set the IOCON value + + try + { + // If we're in BANK1 mode, address 0x15 corresponds to the GPINTENB register, which should be inited to 0 anyway; + tryI2CWrite8(0x05, 0x00); + // Now write the proper IOCON value + tryI2CWrite8(IOCON, hwcfg.parse()); + // Finally, initialize both GPINTENA and GPINTENB to 0x00 + tryI2CWrite8(GPINTENA, 0x00); + tryI2CWrite8(GPINTENB, 0x00); + + // Further initialization + + // Set up the IO direction + tryI2CWrite16(IODIR, iodir); + // Set up the input polarity + tryI2CWrite16(IPOL, ipol); + // Set up the pullups + tryI2CWrite16(GPPU, pullup); + } + catch(OperationFailedException x) + { + i2cClose(fp); + throw x; + } +} + + +//! Destructor +Mcp23017::~Mcp23017() +{ + PwmStop(); // try to stop the PWM driver; + i2cClose(fp); +} + + +/************************************ +* * +* INTERRUPT SETTINGS * +* * +************************************/ + +//! Set interrupt config for the +void Mcp23017::IntConfig( uint16_t intcon, uint16_t defval, uint16_t int_enable) +{ + tryI2CWrite16(INTCON,intcon); + tryI2CWrite16(DEFVAL,defval); + tryI2CWrite16(GPINTEN,int_enable); +} + +//! Get Interrupt flags +uint16_t Mcp23017::getIntF() +{ + return tryI2CRead16(INTF); +} + +//! Get state of input pins on latest interrupt +uint16_t Mcp23017::getIntCap() +{ + return tryI2CRead16(INTCAP); +} + +//! Set Default value for pins +void Mcp23017::setDefault( uint16_t value) +{ + tryI2CWrite16(DEFVAL, value); +} + +//! Get Default value for pins +uint16_t Mcp23017::getDefault() +{ + return tryI2CRead16(DEFVAL); +} + +//! Set Interrupt enable +void Mcp23017::setIntEnable( uint16_t value) +{ + tryI2CWrite16(GPINTEN, value); +} + +//! Get Interrupt enable +uint16_t Mcp23017::getIntEnable() +{ + return tryI2CRead16(GPINTEN); +} + +//! Set Interrupt control value +void Mcp23017::setIntControl(uint16_t value) +{ + tryI2CWrite16(INTCON, value); +} + +//! Get Interrupt control value +uint16_t Mcp23017::getIntControl() +{ + return tryI2CRead16(INTCON); +} + +/************************************ +* * +* REGULAR I/O FUNCTIONS * +* * +************************************/ + +//! Set input polarity +void Mcp23017::setIPol(uint16_t value) +{ + tryI2CWrite16(IPOL, value); +} + +//! Get input polarity +uint16_t Mcp23017::getIPol() +{ + return tryI2CRead16(IPOL); +} + +//! Get output latch value +uint16_t Mcp23017::getOLat() +{ + return tryI2CRead16(OLAT); +} + +//! Set IO Direction +void Mcp23017::setDirection(uint16_t value) +{ + tryI2CWrite16(IODIR, value); +} + +//! Get IO Direction +uint16_t Mcp23017::getDirection() +{ + return tryI2CRead16(IODIR); +} + +//! Set new output value of the I/O pins +void Mcp23017::setValue(uint16_t value) +{ + // if pwm is enabled, use a masked write with the inverse of the pwm_mask + // to avoid overwriting pwm values + if(this->pwm_enabled) + tryI2CMaskedWrite16(GPIO,value,~this->pwm_mask); + else + tryI2CWrite16(GPIO, value); +} + +//! Get current input value of the I/O pins +uint16_t Mcp23017::getValue() +{ + return tryI2CRead16(GPIO); +} + +//! Set new output value for a subset of the I/O pins (masked by 'mask', where high bits indicat bits to set in the output) +void Mcp23017::setMaskedValue(uint16_t value, uint16_t mask) +{ + // if pwm is enabled, join the pwm_mask with this mask + // to avoid overwriting pwm values + if(this->pwm_enabled) + mask &= ~this->pwm_mask; + + tryI2CMaskedWrite16(GPIO,value,mask); +} + +//! Get the values of a specific pin +bool Mcp23017::getPin(uint8_t pin) +{ + uint16_t value; + + if(pin > 15) + throw InvalidArgumentException("Pin number exceeds maximum pin id. Ignoring request to set pin value."); + + value = tryI2CRead16(GPIO); + + if(value & (1 << pin)) + return TRUE; + else + return FALSE; +} + +//! Set the value of a specific pin +void Mcp23017::setPin(uint8_t pin, bool value) +{ + uint16_t newval, mask; + + if(pin > 15) + throw InvalidArgumentException("Pin number exceeds maximum pin id. Ignoring request to set pin value."); + + // set mask + mask = ( 1 << pin ); + + // if pwm is enabled, join the pwm_mask with this mask + // to avoid overwriting pwm values + if(this->pwm_enabled) + mask &= ~this->pwm_mask; + + if(mask == 0x00) + throw InvalidArgumentException("Pin is active in PWM. Ignoring request to set pin value."); + + if(value) + { + newval = ( 1 << pin ); + } + else + { + newval = 0; + } + + tryI2CMaskedWrite16(GPIO, newval, mask); +} + +/************************************ +* * +* PWM FUNCTIONS * +* * +************************************/ + +//! Start the PWM routine for this I/O expander +void Mcp23017::PwmStart() +{ + + if(!ThreadRunning()) + { + ThreadStart(); + } +} + +//! Stop the PWM routine for this I/O expander +void Mcp23017::PwmStop() +{ + if(ThreadRunning()) + { + ThreadStop(); + // Set the PWM pins back to low + tryI2CMaskedWrite16(OLAT, 0x0000, this->pwm_mask); + } +} + +//! Get the PWM value for a specific pin as 0-255 value (can be different from previously set value, due to rounding errors) +uint8_t Mcp23017::getPwmValue(uint8_t pin) +{ + + if(pin > 15) + throw InvalidArgumentException("Pin number exceeds maximum pin id. Ignoring request."); + + return this->pwm_v_values[pin]; // Return the cached 0-255 value of the pin +} + +//! Set the PWM value for a specific pin as 0-255 value +void Mcp23017::setPwmValue(uint8_t pin, uint8_t value) +{ + + if(pin > 15) + throw InvalidArgumentException("Pin number exceeds maximum pin id. Ignoring request."); + + this->pwm_v_values[pin] = value; // cache the provided value for returning and for updating pwm_value on change in pwm_ticks); + this->pwm_values[pin] = value / (256/this->pwm_ticks); +} + +//! Set the PWM value for a specific pin as 0-255 value and apply gamma correction for LED light levels +void Mcp23017::setPwmLedValue(uint8_t pin, uint8_t lightvalue) +{ + // Gamma correction table + const uint8_t GammaToLinear[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 4, 4, 4, + 4, 5, 5, 5, 5, 6, 6, 6, + 6, 7, 7, 7, 8, 8, 8, 9, + 9, 9, 10, 10, 11, 11, 11, 12, + 12, 13, 13, 14, 14, 14, 15, 15, + 16, 16, 17, 17, 18, 18, 19, 19, + 20, 21, 21, 22, 22, 23, 23, 24, + 25, 25, 26, 27, 27, 28, 28, 29, + 30, 31, 31, 32, 33, 33, 34, 35, + 36, 36, 37, 38, 39, 39, 40, 41, + 42, 43, 44, 44, 45, 46, 47, 48, + 49, 50, 51, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 80, 81, + 82, 83, 84, 86, 87, 88, 89, 91, + 92, 93, 94, 96, 97, 98, 100, 101, + 102, 104, 105, 106, 108, 109, 110, 112, + 113, 115, 116, 118, 119, 120, 122, 123, + 125, 126, 128, 129, 131, 132, 134, 136, + 137, 139, 140, 142, 143, 145, 147, 148, + 150, 152, 153, 155, 157, 158, 160, 162, + 164, 165, 167, 169, 171, 172, 174, 176, + 178, 179, 181, 183, 185, 187, 189, 191, + 192, 194, 196, 198, 200, 202, 204, 206, + 208, 210, 212, 214, 216, 218, 220, 222, + 224, 226, 228, 230, 232, 234, 237, 239, + 241, 243, 245, 247, 249, 252, 254, 255 + }; + + if(pin > 15) + throw InvalidArgumentException("Pin number exceeds maximum pin id. Ignoring request."); + + this->pwm_v_values[pin] = GammaToLinear[lightvalue]; // cache the (gamma-corrected) provided value for returning and for updating pwm_value on change in pwm_ticks); + this->pwm_values[pin] = GammaToLinear[lightvalue] / (256/this->pwm_ticks); +} + + +//! Get the PWM state (on/off) for a specific pin +bool Mcp23017::getPwmState(uint8_t pin) +{ + if(pin > 15) + throw InvalidArgumentException("Pin number exceeds maximum pin id. Ignoring request."); + + return ( (this->pwm_mask & (1 << pin)) > 0); +} + +//! Set the PWM state (on/off) for a specific pin +void Mcp23017::setPwmState(uint8_t pin, bool state) +{ + if(pin > 15) + throw InvalidArgumentException("Pin number exceeds maximum pin id. Ignoring request."); + + if(state) + this->pwm_mask |= (1 << pin); + else + this->pwm_mask &= ~(1 << pin); +} + +//! Set the PWM Configuration +void Mcp23017::setPwmConfig(uint32_t tick_delay_us, uint8_t ticks) +{ + int i; + + this->pwm_tick_delay_us = tick_delay_us; + this->pwm_ticks = ticks; + + // update the actual PWM values, according to the + for(i=0;i<16;i++) + { + if(this->pwm_mask & (1 << i)) // Check if pwm_mask is enabled for this pin + { + // if so, update the actually used pwm value, to reflect the new setting of ticks + this->pwm_values[i] = this->pwm_v_values[i] / (256/this->pwm_ticks); + } + } +} + +//! Get PWM Configuration +PwmConfig Mcp23017::getPwmConfig() +{ + + PwmConfig pcfg; + pcfg.tick_delay_us = pwm_tick_delay_us; + pcfg.ticks = pwm_ticks; + return pcfg; +} + +//! Driver function for PWM thread +void Mcp23017::ThreadFunc(void) +{ + uint8_t ctr = 0; + int i; + uint16_t pwm_out = 0x00; + // + + MakeRealtime(); + + while(ThreadRunning()) + { + + pwm_out = this->pwm_mask; // start with pin high for all pins that have pwm_enabled + + // Now check for each pin if it should be low in this step... + for(i=0;i<16;i++) + { + if(this->pwm_mask & (1 << i)) // Check if pwm_mask is enabled for this pin + { + if(ctr >= this->pwm_values[i] ) // check if the counter is greater than the pwm value for this pin. If so, turn it off. + { + pwm_out &= ~(1 << i); // mask this pin out to 0, for it should be stopped + } + } + } + + if(pwm_out != this->pwm_prev_val) + { + // write out the result, masked with the pwm_mask + muteI2CMaskedWrite16(OLAT, pwm_out, this->pwm_mask); + this->pwm_prev_val = pwm_out; + } + + ctr++; + if(ctr >= (this->pwm_ticks - 1)) + { + ctr = 0; + } + + usleep(this->pwm_tick_delay_us); + } + +} + + +/************************************ +* * +* INTERNAL HELPER FUNCTIONS * +* * +*************************************/ + +/*! Try to read an 8 bit value from a register + In case of an error, The IOKey error value will be, + and the function will return prematurely. +*/ +uint8_t Mcp23017::tryI2CRead8(uint8_t reg) +{ + int ret; + // lock process + MutexLock(); + + // Now start reading + ret = i2cReadReg8(this->fp,reg); + + // unlock process + MutexUnlock(); + + // And properly set any error messages + if(ret == -1) + throw OperationFailedException("Error writing register address, attempted to read 8bit value from register %s (0x%2x) ",Registers8[reg], reg); + else if(ret == -2) + throw OperationFailedException("Error reading value, attempted to read 8bit value from register %s (0x%2x)",Registers8[reg], reg); + else if(ret < 0) + throw OperationFailedException("Unknown error [%d], attempted to read 8bit value from register %s (0x%2x)",ret, Registers8[reg], reg); + + return (uint8_t)ret; +} + +/*! Try to write an 8 bit value to a register + In case of an error, The IOKey error value will be, + and the function will return prematurely. +*/ +void Mcp23017::tryI2CWrite8(uint8_t reg, uint8_t value) +{ + int ret; + + // lock process + MutexLock(); + + // Now start reading + ret = i2cWriteReg8(this->fp,reg,value); + + // unlock process + MutexUnlock(); + + // And properly set any error messages + if(ret == -1) + throw OperationFailedException("Error writing to register, attempted to write 8bit value 0x%2x to register %s (0x%2x)",value, Registers8[reg], reg); + else if(ret < 0) + throw OperationFailedException("Unknown error [%d], attempted to write 8bit value 0x%2x to register %s (0x%2x)",ret, value, Registers8[reg], reg); + + +} + +/*! Try to write specific bits in an 8 bit value to a register + In case of an error, The IOKey error value will be, + and the function will return prematurely. +*/ +void Mcp23017::tryI2CMaskedWrite8(uint8_t reg, uint8_t value, uint8_t mask) +{ + int ret; + uint8_t newval; + + // lock process + MutexLock(); + + // read current value; + if( (ret = i2cReadReg8(this->fp,reg)) < 0) + { + // unlock process + MutexUnlock(); + // And properly set any error messages + if(ret == -1) + throw OperationFailedException("Error writing register address, attempted to read 8bit value from register %s (0x%2x) for masked write", Registers8[reg], reg); + else if(ret == -2) + throw OperationFailedException("Error reading value, attempted to read 8bit value from register %s (0x%2x) for masked write", Registers8[reg], reg); + else if(ret < 0) + throw OperationFailedException("Unknown error [%d], attempted to read 8bit value from register %s (0x%2x) for masked write",ret, Registers8[reg], reg); + } + + // copy result to new variable + newval = (uint16_t)(ret); + // keep only the non-masked bits + newval &= ~mask; + // overwrite the masked bits with the new value + newval |= (value & mask); + + if( (ret = i2cWriteReg8(this->fp,reg,newval)) < 0) + { + // unlock process + MutexUnlock(); + + // And properly set any error messages + if(ret == -1) + throw OperationFailedException("Error writing to register, attempted to write 8bit value 0x%2x to register %s (0x%2x) for masked write",value, Registers8[reg], reg); + else if(ret < 0) + throw OperationFailedException("Unknown error [%d], attempted to write 8bit value 0x%2x to register %s (0x%2x) for masked write",ret, value, Registers8[reg], reg); + } + + // unlock process + MutexUnlock(); +} + +/*! Try to read a 16 bit value from a register + In case of an error, The IOKey error value will be, + and the function will return prematurely. +*/ +uint16_t Mcp23017::tryI2CRead16(uint8_t reg) +{ + uint8_t hwreg = reg; + int ret; + + // lock process + MutexLock(); + + // Ensure that we're initially reading from the right register in relation to the AB swap settings + // (With swap enabled, we should start reading from the odd register, with swap disabled, we should start reading from the even register) + if(this->swapAB) + hwreg |= 0x01; + else + hwreg &= 0xFE; + + // Now start reading + ret = i2cReadReg16(this->fp,hwreg); + + // unlock process + MutexUnlock(); + + // And properly set any error messages + if(this->swapAB) + { + if(ret == -1) + throw OperationFailedException("Error writing register address, attempted to read 16bit value from register %s (0x%2x) [AB Swap active, actual register read is %s (0x%2x)]", Registers16[reg], reg, Registers16[hwreg], hwreg); + else if(ret == -2) + throw OperationFailedException("Error reading value, attempted to read 16bit value from register %s (0x%2x) [AB Swap active, actual register read is %s (0x%2x)]", Registers16[reg], reg, Registers16[hwreg], hwreg); + else if(ret < 0) + throw OperationFailedException("Unknown error [%d], attempted to read 16bit value from register %s (0x%2x) [AB Swap active, actual register read is %s (0x%2x)]",ret, Registers16[reg], reg, Registers16[hwreg], hwreg); + } + else + { + if(ret == -1) + throw OperationFailedException("Error writing register address, attempted to read 16bit value from register %s (0x%2x)", Registers16[reg], reg); + else if(ret == -2) + throw OperationFailedException("Error reading value, attempted to read 16bit value from register %s (0x%2x)", Registers16[reg], reg); + else if(ret < 0) + throw OperationFailedException("Unknown error [%d], attempted to read 16bit value from register %s (0x%2x)",ret, Registers16[reg], reg); + } + +return (uint16_t)ret; +} + +/*! Try to write a 16 bit value to a register + In case of an error, The IOKey error value will be, + and the function will return prematurely. +*/ +void Mcp23017::tryI2CWrite16(uint8_t reg,uint16_t value) +{ + uint8_t hwreg = reg; + int ret; + + // lock process + MutexLock(); + + // Ensure that we're initially reading from the right register in relation to the AB swap settings + // (With swap enabled, we should start reading from the odd register, with swap disabled, we should start reading from the even register) + if(this->swapAB) + hwreg |= 0x01; + else + hwreg &= 0xFE; + + // Now start writing + ret = i2cWriteReg16(this->fp,hwreg, value); + + // unlock process + MutexUnlock(); + + // And properly set any error messages + if(this->swapAB) + { + if(ret == -1) + throw OperationFailedException("Error writing to register, attempted to write 16bit value 0x%4x to register %s (0x%2x) [AB Swap active, actual register read is %s (0x%2x)]",value, Registers16[reg], reg, Registers16[hwreg], hwreg); + else if(ret < 0) + throw OperationFailedException("Unknown error [%d], attempted to write 16bit value 0x%4x to register %s (0x%2x) [AB Swap active, actual register read is %s (0x%2x)]",ret, value, Registers16[reg], reg, Registers16[hwreg], hwreg); + } + else + { + if(ret == -1) + throw OperationFailedException("Error writing to register, attempted to write 16bit value 0x%4x to register %s (0x%2x)",value, Registers16[reg], reg); + else if(ret < 0) + throw OperationFailedException("Unknown error [%d], attempted to write 16bit value 0x%4x to register %s (0x%2x)",ret, value, Registers16[reg], reg); + } + + +} + +/*! Try to write specific bits in a 16 bit value to a register + In case of an error, The IOKey error value will be, + and the function will return prematurely. +*/ +void Mcp23017::tryI2CMaskedWrite16(uint8_t reg, uint16_t value, uint16_t mask) +{ + int ret; + uint16_t newval; + uint8_t hwreg = reg; + + // lock process + MutexLock(); + + // Ensure that we're initially reading from the right register in relation to the AB swap settings + // (With swap enabled, we should start reading from the odd register, with swap disabled, we should start reading from the even register) + if(this->swapAB) + hwreg |= 0x01; + else + hwreg &= 0xFE; + + // read current value; + if( (ret = i2cReadReg16(this->fp,hwreg)) < 0) + { + // unlock process + MutexUnlock(); + + // And properly set any error messages + if(this->swapAB) + { + if(ret == -1) + throw OperationFailedException("Error writing register address, attempted to read 16bit value from register %s (0x%2x) for masked write [AB Swap active, actual register read is %s (0x%2x)]", Registers16[reg], reg, Registers16[hwreg], hwreg); + else if(ret == -2) + throw OperationFailedException("Error reading value, attempted to read 16bit value from register %s (0x%2x) for masked write [AB Swap active, actual register read is %s (0x%2x)]", Registers16[reg], reg, Registers16[hwreg], hwreg); + else if(ret < 0) + throw OperationFailedException("Unknown error [%d], attempted to read 16bit value from register %s (0x%2x) for masked write [AB Swap active, actual register read is %s (0x%2x)]",ret, Registers16[reg], reg, Registers16[hwreg], hwreg); + } + else + { + if(ret == -1) + throw OperationFailedException("Error writing register address, attempted to read 16bit value from register %s (0x%2x) for masked write", Registers16[reg], reg); + else if(ret == -2) + throw OperationFailedException("Error reading value, attempted to read 16bit value from register %s (0x%2x) for masked write", Registers16[reg], reg); + else if(ret < 0) + throw OperationFailedException("Unknown error [%d], attempted to read 16bit value from register %s (0x%2x) for masked write",ret, Registers16[reg], reg); + } + } + + // copy result to new variable + newval = (uint16_t)(ret); + // keep only the non-masked bits + newval &= ~mask; + // overwrite the masked bits with the new value + newval |= (value & mask); + + if( (ret = i2cWriteReg16(this->fp,hwreg,newval)) < 0) + { + // unlock process + MutexUnlock(); + + // And properly set any error messages + if(this->swapAB) + { + if(ret == -1) + throw OperationFailedException("Error writing to register, attempted to write 16bit value 0x%4x to register %s (0x%2x) for masked write [AB Swap active, actual register read is %s (0x%2x)]",value, Registers16[reg], reg, Registers16[hwreg], hwreg); + else if(ret < 0) + throw OperationFailedException("Unknown error [%d], attempted to write 16bit value 0x%4x to register %s (0x%2x) for masked write [AB Swap active, actual register read is %s (0x%2x)]",ret, value, Registers16[reg], reg, Registers16[hwreg], hwreg); + } + else + { + if(ret == -1) + throw OperationFailedException("Error writing to register, attempted to write 16bit value 0x%4x to register %s (0x%2x) for masked write",value, Registers16[reg], reg); + else if(ret < 0) + throw OperationFailedException("Unknown error [%d], attempted to write 16bit value 0x%4x to register %s (0x%2x) for masked write",ret, value, Registers16[reg], reg); + } + } + + // unlock process + MutexUnlock(); +} + +/*! Attempt to write specific bits in a 16 bit value to a register + In case of an error, the function will return prematurely, + without setting an error. +*/ +void Mcp23017::muteI2CMaskedWrite16(uint8_t reg, uint16_t value, uint16_t mask) +{ + int result; + uint16_t newval; + uint8_t hwreg = reg; + + // lock process + MutexLock(); + // Ensure that we're initially reading from the right register in relation to the AB swap settings + // (With swap enabled, we should start reading from the odd register, with swap disabled, we should start reading from the even register) + if(this->swapAB) + hwreg |= 0x01; + else + hwreg &= 0xFE; + + // read current value; + if( (result = i2cReadReg16(this->fp,hwreg)) < 0) + { +// fprintf(stderr,"WARNING: Got error code [%d] attempting to read\r\n",result); + // unlock process + MutexUnlock(); + return; + } + + // copy result to new variable + newval = (uint16_t)(result); + // keep only the non-masked bits + newval &= ~mask; + // overwrite the masked bits with the new value + newval |= (value & mask); + + if( (result = i2cWriteReg16(this->fp,hwreg,newval)) < 0) + { +// fprintf(stderr,"WARNING: Got error code [%d] attempting to write\r\n",result); + // unlock process + MutexUnlock(); + return; + } + + // unlock process + MutexUnlock(); + +} diff --git a/src/mcp23017/mcp23017.hpp b/src/mcp23017/mcp23017.hpp new file mode 100644 index 0000000..177f4fb --- /dev/null +++ b/src/mcp23017/mcp23017.hpp @@ -0,0 +1,291 @@ +#ifndef __MCP23017_H_ +#define __MCP23017_H_ + +#include "../exception/baseexceptions.hpp" +#include "../thread/thread.hpp" +#include + +/*! \file MCP23017 interface functions. Header file. +*/ + +/**************************** +* * +* PUBLIC STRUCTS * +* * +*****************************/ + +// Structure for IO Configuration +//! \typedef HWConfig Structure containing hardware configuration for the MCP23017 +class HWConfig +{ + public: + bool DISSLEW ; /*!< Disable slew rate control (useful in noisy circuits operating at 400kHz and below), Default: false*/ + bool INT_MIRROR; /*!< Mirror INTA and INTB pins: Both pins act as a single INT pin responding to both port A and port B, Default: true */ + bool INT_ODR; /*!< Set interrupt pins as open drain output, Default: false */ + bool INT_POL; /*!< Set interrupt polatity: 1 is active-high, 0 is active-low, Default: false */ + + HWConfig(); + uint8_t parse(); /*!< parse into usable uint8_t */ +}; + +// Structure for IO Configuration +//! \struct PWMConfig Structure containing current PWM configuration (only used as return value) +struct PwmConfig +{ + uint32_t tick_delay_us; /*!< Delay between ticks in us */ + uint8_t ticks; /*!< Number of ticks in a cycle */ +}; + +/************************************ +* * +* MAIN CLASS * +* * +*************************************/ + +class Mcp23017 : public Thread +{ + private: + uint8_t adr; // I2C Address of the IO expander chip + int fp; // File pointer for the I2C connection + bool swapAB; // Option to swap ports A and B for 8bit and 16 bit operations + + HWConfig hwConfig; // Configuration of the port + + bool pwm_enabled; // Boolean used to stop the PWM thread safely + uint16_t pwm_mask; // Masks the pins on which PWM operates + uint8_t pwm_values[16]; // PWM value for each possible pin + uint8_t pwm_v_values[16]; // Cache for the PWM values provided, before downconversion + uint32_t pwm_tick_delay_us; // Interval between PWM steps in us + uint8_t pwm_ticks; // Number of PWM steps before coming full circle + uint16_t pwm_prev_val; // Keeps state of pwm output; + + uint8_t tryI2CRead8 (uint8_t reg); + void tryI2CWrite8(uint8_t reg, uint8_t value); + void tryI2CMaskedWrite8(uint8_t reg, uint8_t value, uint8_t mask); + + uint16_t tryI2CRead16(uint8_t reg); + void tryI2CWrite16(uint8_t reg, uint16_t value); + void tryI2CMaskedWrite16(uint8_t reg, uint16_t value, uint16_t mask); + void muteI2CMaskedWrite16(uint8_t reg, uint16_t value, uint16_t mask); + + + + protected: + virtual void ThreadFunc(void); // Override this if you want the entire function customized + + public: + //! Open a new connection to the MCP23017 device, and initialize it. + /*! + \param adr The I2C address of the IC to connect to + \param iodir Initial I/O direction mask (HIGH is input, LOW is output) + \param ipol Initial input polarity mask (HIGH inverts polarity, LOW keeps it the same) + \param pullup Initial pullup mask (HIGH enables pullup, LOW disables) + \param hwcfg Hardware configuration for the IC + \param swapAB Swap A and B registers in the 16 bit operations + \sa MCP23017Close + */ + Mcp23017( uint8_t adr, + uint16_t iodir, + uint16_t ipol, + uint16_t pullup, + HWConfig hwcfg, + bool swapAB + ); + + ~Mcp23017(); + + /************************************ + * * + * INTERRUPT FUNCTIONS * + * * + ************************************/ + + //! Set interrupt config for the MCP23017 + /*! + \param intcon The interrupt control mask (HIGH bit compares to default value, LOW bit to previous value) + \param defval Initial default value for the pins + \param int_enable The interrupt enable mask (HIGH bit enables, LOW bit disables) + */ + void IntConfig(uint16_t defval, uint16_t intcon, uint16_t int_enable); + + //! Get the interrupt condition of the interrupt-enabled pins + /*! + \return Current interrupt trigger state for pins + */ + uint16_t getIntF(); + + //! //! Get state of input pins on latest interrupt + /*! + \return The state of the interrupt pins on the last interrupt + */ + uint16_t getIntCap(); + + //! Set Default value for pins + /*! + \param value The new default value for the pins + */ + void setDefault(uint16_t value); + + //! Get default value for input pins + /*! + \return Current default value for the input pins + */ + uint16_t getDefault(); + + //! Set Interrupt enable mask + /*! + \param value The new interrupt enable mask (HIGH bit enables, LOW bit disables) + */ + void setIntEnable(uint16_t value); + + //! Get Interrupt enable mask + /*! + \return Current interrupt enable mask + */ + uint16_t getIntEnable(); + + //! Set Interrupt control value + /*! + \param value The new interrupt control mask (HIGH bit compares to default value, LOW bit to previous value) + */ + void setIntControl(uint16_t value); + + //! Get Interrupt control value + /*! + \return Current interrupt control state + */ + uint16_t getIntControl(); + + /************************************ + * * + * REGULAR I/O FUNCTIONS * + * * + ************************************/ + + //! Set input polarity + /*! + \param value The input polarity - HIGH inverts input polarity, LOW does not + */ + void setIPol(uint16_t value); + + //! Get input polarity + /*! + \return Current value of the input polarity + */ + uint16_t getIPol(); + + //! Get output latch value + /*! + \return Current value of the output latches + */ + uint16_t getOLat(); + + //! Set IO Direction + /*! + \param key The new I/O direction (HIGH bit is input, LOW bit is output) + */ + void setDirection(uint16_t value); + + //! Get IO Direction + /*! + \return Current value of the IO direction + */ + uint16_t getDirection(); + + //! Set new output value of the I/O pins + /*! + \param value The new output value of the pins + */ + void setValue(uint16_t value); + + //! Get current input value of the I/O pins + /*! + \return Current value of the port + */ + uint16_t getValue(); + + //! Set new output value for a subset of the I/O pins (masked by 'mask', where high bits indicat bits to set in the output) + /*! + \param value The new output value of the pins + \param mask The mask indicating which bits to apply (HIGH bit indicated bit will be applied) + */ + void setMaskedValue(uint16_t value, uint16_t mask); + //! Get the values of a specific pin; + /*! + \return The current state (1 or 0) of the pin + */ + bool getPin(uint8_t pin); + + //! Set the value of a specific pin + /*! + \param value Boolean indicating the new value of the pin + */ + void setPin(uint8_t pin, bool value); + + /************************************ + * * + * PWM FUNCTIONS * + * * + ************************************/ + + //! Start the PWM routine for this I/O expander + void PwmStart(); + + //! Stop the PWM routine for this I/O expander + void PwmStop(); + + //! Get the PWM value for a specific pin (can be different from previously set value, due to rounding errors) + /*! + \param pin The pin number of which to retrieve the value + \return The current PWM value for the pin + */ + uint8_t getPwmValue(uint8_t pin); + + //! Set the PWM value for a specific pin as 0-255 value + /*! + \param pin The pin number of which to retrieve the value + \param value The new PWM value + */ + void setPwmValue(uint8_t pin, uint8_t value); + + //! Set the PWM value for a specific pin as 0-255 value and apply gamma correction for LED light levels + /*! + \param pin The pin number of which to retrieve the value + \param value The new PWM value + */ + void setPwmLedValue(uint8_t, uint8_t lightvalue); + + //! Get the PWM state (on/off) for a specific pin + /*! + \param pin The pin number of which to retrieve the state + \return Boolean indicating the current state of the pin + */ + bool getPwmState(uint8_t pin); + + //! Set the PWM state (on/off) for a specific pin + /*! + \param pin The pin number of which to set the state + \param state Boolean indicating the new state of pwm on this pin (HIGH is enabled, LOW is disabled) + */ + void setPwmState(uint8_t pin, bool state); + + //! Set the PWM Configuration + /*! Default value results in a 4 bit PWM of around 80Hz + Note: PWM has a fairly heavy CPU load. With default settings (4 bit pwm, 800us/step) this is around 4.5% on a raspberry pi. + Doubling the resolution to 5 bit, 400us/step takes around 7% cpu load. Use at your own discretion. + + \param tick_delay_us The number of microseconds between PWM ticks (default: 313 us) + \param ticks The number of ticks in one PWM cycle (default: 32 ticks) + */ + void setPwmConfig(uint32_t tick_delay_us, uint8_t ticks); + + //! Get PWM Configuration + /*! + \return PWMConfig object containing the current settings + */ + PwmConfig getPwmConfig(); + +}; + + +#endif \ No newline at end of file diff --git a/src/test/mcp23017-i2ctest.c b/src/test/mcp23017-i2ctest.c new file mode 100644 index 0000000..21c9a16 --- /dev/null +++ b/src/test/mcp23017-i2ctest.c @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../i2c/i2c.h" + +int main(int argc, char ** argv) +{ + int fd,i; + int result; + + char * Registers8[22] = + { + "IODIRA ", // 00 + "IODIRB ", // 01 + "IPOLA ", // 02 + "IPOLB ", // 03 + "GPINTENA", // 04 + "GPINTENB", // 05 + "DEFVALA ", // 06 + "DEFVALB ", // 07 + "INTCONA ", // 08 + "INTCONB ", // 09 + "IOCON ", // 0A + "IOCON ", // 0B + "GPPUA ", // 0C + "GPPUB ", // 0D + "INTFA ", // 0E + "INTFB ", // 0F + "INTCAPA ", // 10 + "INTCAPB ", // 11 + "GPIOA ", // 12 + "GPIOB ", // 13 + "OLATA ", // 14 + "OLATB " // 15 + }; + + char * Registers16[22] = + { + "IODIR ", // 00 + "IODIR ", // 01 + "IPOL ", // 02 + "IPOL ", // 03 + "GPINTEN", // 04 + "GPINTEN", // 05 + "DEFVAL ", // 06 + "DEFVAL ", // 07 + "INTCON ", // 08 + "INTCON ", // 09 + "IOCON ", // 0A + "IOCON ", // 0B + "GPPU ", // 0C + "GPPU ", // 0D + "INTF ", // 0E + "INTF ", // 0F + "INTCAP ", // 10 + "INTCAP ", // 11 + "GPIO ", // 12 + "GPIO ", // 13 + "OLAT ", // 14 + "OLAT " // 15 + }; + + fd = i2cInit(0x20); + + if(argc > 2) + { + int reg = -1; + int val = -1; + + if(strlen(argv[1]) == 4 && strncmp(argv[1],"0x",2) == 0) + reg = strtol(argv[1],NULL,16); + + if( (strlen(argv[2]) == 4 || strlen(argv[2]) == 6) && strncmp(argv[2],"0x",2) == 0) + val = strtol(argv[2],NULL,16); + + if(reg < 0) + printf ("Please provide an 8 bit hexadecimal value for the register in the form of 0xHH\n"); + if(val < 0) + printf ("Please provide an 8 or 16 bit hexadecimal value for the value in the form of 0xHH\n"); + + if(reg >= 0 && val >= 0) + { + if(strlen(argv[2]) == 4) + { + printf("Setting register %s (0x%02x) to 8 bit value 0x%02x\n\n", Registers8[reg], reg, val); + i2cWriteReg8(fd,(unsigned char)reg, (unsigned char) val); + } + else if (strlen(argv[2]) == 6) + { + printf("Setting register %s (0x%02x) to 16 bit value 0x%04x\n\n", Registers16[reg], reg, val); + i2cWriteReg16(fd,(unsigned char)reg, (unsigned short) val); + } + } + + } + + + printf("Reading registers as 8 bit values\n"); + for(i=0x00; i < 0x16; i++) + { + result = i2cReadReg8(fd,i); + printf(" %s (0x%02x) : 0x%02x\n",Registers8[i], i,result); + } + + printf("\n"); + printf("Reading registers as 16 bit values\n"); + + for(i=0x00; i < 0x16; i+=2) + { + result = i2cReadReg16(fd,i); + printf(" %s (0x%02x) : 0x%04x\n",Registers16[i], i,result); + } + + printf("\n"); + printf("Reading registers as 16 bit values with A/B swapped\n"); + + for(i=0x01; i < 0x16; i+=2) + { + result = i2cReadReg16(fd,i); + printf(" %s (0x%02x) : 0x%04x\n",Registers16[i], i,result); + } + + return 0; +} diff --git a/src/thread/thread.cpp b/src/thread/thread.cpp new file mode 100644 index 0000000..abbba44 --- /dev/null +++ b/src/thread/thread.cpp @@ -0,0 +1,145 @@ +#include "thread.hpp" +#include +#include +#include +#include + +#include + +#include "../log/log.hpp" + +using namespace std; + +void * thread_threadStarter(void *); + +Thread::Thread() +{ + running = false; + + pthread_mutexattr_init(&mutexAttr); + pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mutex, &mutexAttr); + +} + +Thread::~Thread() +{ + // clean up the mutex + pthread_mutex_destroy(&mutex); + + // Stop the thread if needed + if(running) + ThreadStop(); +} + +bool Thread::ThreadRunning() +{ + return running; +} + + +void Thread::ThreadStart() +{ + int32_t result; + if(!running) + { + running = true; + + result = pthread_create(&wthread, NULL, thread_threadStarter, this); + if(result != 0) // success + { + running = false; + if(result == EAGAIN) + throw ThreadException("Cannot create thread: Error EAGAIN - The system lacked the neccesary resources to create another thread, or PTHREAD_THREADS_MAX is exceeded"); + else if(result == EINVAL) + throw ThreadException("Cannot create thread: Error EINVAL - The value specified by attr (in pthread_create) is invalid"); + else if(result == EPERM) + throw ThreadException("Cannot create thread: Error EPERM - The caller does not have approproate permission to set the required scheduling permissions or scheduling policy"); + } + } +} + +void Thread::ThreadStop() +{ + int32_t result; + if(running) + { + running = false; // stops the thread loop + result = pthread_join(wthread, NULL); // wait for completion + if(result != 0) + { + if(result == EINVAL) + throw ThreadException("Cannot end thread: Error EINVAL - The implementation has detected that the value specified by thread does not refer to a joinable thread."); + else if(result == ESRCH) + throw ThreadException("Cannot create thread: Error ESRCH - No thread could be found corresponding to that specified by the given thread ID"); + else if(result == EDEADLK) + throw ThreadException("Cannot create thread: Error EINVAL - A deadlock was detected or the value of thread specifies the calling thread."); + } + } +} + +void Thread::ThreadStarter() +{ + try + { + ThreadFunc(); + running = false; + } + catch(std::exception x) + { + running = false; + + // (debug) Log the exception, before calling the callback + // clog << kLogErr << x.what() << endl; + onThreadError(this, ThreadException(x.what())); + + } +} + +bool Thread::MakeRealtime() +{ + struct sched_param params; + int32_t ret; + + params.sched_priority = sched_get_priority_max(SCHED_FIFO); + ret = pthread_setschedparam(wthread, SCHED_FIFO, ¶ms); // This thread is always key->pwm_thread, and if not, then it should be + + if (ret != 0) { + // Print the error + clog << kLogWarning << "Unsuccessful in setting thread realtime priority" << endl; + return false; + } + else + return true; +} + +void Thread::ThreadLoop(void) +{ + usleep(25000); +} + +void Thread::ThreadFunc(void) +{ + while(running) + { + ThreadLoop(); + } +} + +void Thread::MutexLock() +{ + pthread_mutex_lock(&mutex); +} +void Thread::MutexUnlock() +{ + pthread_mutex_unlock(&mutex); +} + + + +// static function that calls the real function +void * thread_threadStarter(void * obj) +{ + Thread * wt = (Thread*)obj; + wt->ThreadStarter(); +} diff --git a/src/thread/thread.hpp b/src/thread/thread.hpp new file mode 100644 index 0000000..da32eab --- /dev/null +++ b/src/thread/thread.hpp @@ -0,0 +1,48 @@ +#ifndef __THREAD_HPP +#define __THREAD_HPP + +#include +#include +#include "../exception/baseexceptions.hpp" +#include + +DefineNewMsgException(ThreadException); + +/* +class ThreadException : public MsgException +{ + protected: + virtual std::string type() { return "ThreadException"; } +}; +*/ + +class Thread +{ + friend void * thread_threadStarter(void * obj); + public: + ~Thread(); + boost::signals2::signal onThreadError; + + protected: + Thread(); + + void ThreadStart(); + void ThreadStop(); + bool ThreadRunning(); + void MutexLock(); + void MutexUnlock(); + + bool MakeRealtime(); + virtual void ThreadFunc(void); // Override this if you want the entire function custom + virtual void ThreadLoop(); // + + private: + pthread_t wthread; + pthread_mutex_t mutex; + pthread_mutexattr_t mutexAttr; + bool running; + void ThreadStarter(); +}; + + +#endif//__MC_HID_SERVER_HPP \ No newline at end of file diff --git a/version.py b/version.py new file mode 100644 index 0000000..2c0d715 --- /dev/null +++ b/version.py @@ -0,0 +1,2 @@ +PACKAGE="mediacore-hid" +VERSION="1.0.0" diff --git a/version.py.in b/version.py.in new file mode 100644 index 0000000..fcf36eb --- /dev/null +++ b/version.py.in @@ -0,0 +1,2 @@ +PACKAGE="@V_PACKAGE@" +VERSION="@V_VERSION@"