Initial commit

This commit is contained in:
Miqra Developer 2018-09-24 15:45:06 +00:00
commit 842719c84a
44 changed files with 5476 additions and 0 deletions

50
.gitignore vendored Normal file
View File

@ -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

1151
Makefile Normal file

File diff suppressed because it is too large Load Diff

83
Makefile.am Executable file
View File

@ -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)#" $@

8
TESTCMDS.txt Normal file
View File

@ -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

View File

@ -0,0 +1,17 @@
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Only root can own MediaCore connections -->
<policy user="root">
<allow own="nl.miqra.MediaCore.Hid"/>
</policy>
<!-- Allow anyone to invoke methods on MediaCore connections -->
<policy context="default">
<allow send_destination="nl.miqra.MediaCore.Hid"/>
<allow receive_sender="nl.miqra.MediaCore.Hid"/>
</policy>
</busconfig>

163
cfg/init.d/mediacore-hid.in Normal file
View File

@ -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
:

View File

@ -0,0 +1,4 @@
# blacklist spi and i2c by default (many users don't need them)
blacklist spi-bcm2708
#blacklist i2c-bcm2708

8
cfg/modules Normal file
View File

@ -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

148
cfg/piio.conf Normal file
View File

@ -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;
}
}
}
*/

View File

@ -0,0 +1 @@
local0.* -/var/log/mediacore.log

23
config.hpp Normal file
View File

@ -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"

22
config.hpp.in Normal file
View File

@ -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

25
config.hpp.in~ Normal file
View File

@ -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

21
configure.ac Executable file
View File

@ -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

6
deb-dependencies.txt Normal file
View File

@ -0,0 +1,6 @@
autoconf-archive
libboost-all-dev
libdbus-c++-bin
libdbus-c++-dev
libdbus-1-dev

15
debian/control vendored Executable file
View File

@ -0,0 +1,15 @@
Source: mediacore-hid
Section: misc
Suite: stable
Priority: optional
Maintainer: Miqra Engineering Packaging <packaging@miqra.nl>
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: <insert the upstream URL, if relevant>
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

40
debian/postinst vendored Normal file
View File

@ -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:
# * <postinst> `configure' <most-recently-configured-version>
# * <old-postinst> `abort-upgrade' <new version>
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
# <new-version>
# * <postinst> `abort-remove'
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
# <failed-install-package> <version> `removing'
# <conflicting-package> <version>
# 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

39
debian/prerm vendored Normal file
View File

@ -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:
# * <prerm> `remove'
# * <old-prerm> `upgrade' <new-version>
# * <new-prerm> `failed-upgrade' <old-version>
# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
# * <deconfigured's-prerm> `deconfigure' `in-favour'
# <package-being-installed> <version> `removing'
# <conflicting-package> <version>
# 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

56
debpackager.py Normal file
View File

@ -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)

6
debpackager.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
rm version.py
make version.py
python debpackager.py

BIN
sigtest Executable file

Binary file not shown.

31
sigtest.cpp Normal file
View File

@ -0,0 +1,31 @@
#include <boost/signals2.hpp>
//#include <boost/bind.hpp>
#include <iostream>
using namespace boost;
using namespace std;
struct ClassA
{
boost::signals2::signal<void ()> SigA;
boost::signals2::signal<void (int)> 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);
}

View File

@ -0,0 +1,120 @@
#include "buttontimer.hpp"
#include <time.h>
#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<uint16_t> btnList;
boost::optional<bool> valid;
MutexLock();
// list through all the items
for( std::map<uint16_t,uint64_t>::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<uint16_t>::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);
}

View File

@ -0,0 +1,35 @@
#include "../thread/thread.hpp"
#include <stdint.h>
#include <boost/signals2.hpp>
#include <map>
// 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<void (uint16_t keycode)> onShortPress;
boost::signals2::signal<void (uint16_t keycode)> onLongPress;
boost::signals2::signal<bool (uint16_t keycode)> onValidatePress;
protected:
virtual void ThreadLoop(void);
private:
boost::signals2::connection onThreadErrorConnection;
uint32_t longpressTime;
uint32_t shortpressMinTime;
std::map<uint16_t, uint64_t> pressRegistry;
bool eventLock;
static int64_t now_ms(void);
};

View File

@ -0,0 +1,75 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#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 ()
{
}

View File

@ -0,0 +1,69 @@
#ifndef __BASEEXCEPTIONS_HPP_
#define __BASEEXCEPTIONS_HPP_
#include <cstdarg>
#include <string>
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

458
src/gpio/gpio.cpp Normal file
View File

@ -0,0 +1,458 @@
#include <stdio.h>
#include <poll.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <malloc.h>
#include <unistd.h>
#include "gpio.hpp"
#include "../log/log.hpp"
#include <iostream>
#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<sizeof(validpinsRev1);i++)
{
if(gpiopin == validpinsRev1[i])
return gpiopin;
}
return -1;
}
else
{ // REV 2 BOARD
// Verify that the pin number is valid, otherwise return -1
for(i=0; i<sizeof(validpinsRev2);i++)
{
if(gpiopin == validpinsRev2[i])
return gpiopin;
}
return -1;
}
}
//! Export a certain Gpio pin
/*!
\param gpiopin The gpio pin to export
\return true if pin was exported by us, false if previously exported
*/
bool GpioPin::exportPin(int gpiopin)
{
FILE *fd ;
int pin_id;
pin_id = verifyPin(gpiopin); // verify that the pin is correct
if(pin_id < 0)
throw InvalidArgumentException("Pin %d is not a usable Raspberry Pi GPIO pin",pin_id);
if ((fd = fopen ("/sys/class/gpio/export", "w")) == NULL)
{
throw OperationFailedException("Pin %d cannot be exported (cannot write to /sys/class/gpio/export)",pin_id);
}
fprintf (fd, "%d\n", pin_id) ;
if(fclose (fd) != 0)
{
// fprintf(stderr, "Got error code %d - %s\n", errno,strerror(errno));
if(errno == EBUSY) // indicates the pin is currently already exported
return false;
else
throw OperationFailedException("Pin %d cannot be exported",pin_id);
}
else
return true;
}
//! Unexport a certain Gpio pin
/*!
\param gpiopin The gpio pin to unexport
\return true if pin was unexported by us, false if previously unexported
*/
bool GpioPin::unexportPin(int gpiopin)
{
FILE *fd ;
int pin_id;
pin_id = verifyPin(gpiopin); // verify that the pin is correct
if(pin_id < 0)
throw InvalidArgumentException("Pin %d is not a usable Raspberry Pi GPIO pin",pin_id);
if ((fd = fopen ("/sys/class/gpio/unexport", "w")) == NULL)
{
throw OperationFailedException("Pin %d cannot be unexported (cannot write to /sys/class/gpio/unexport)",pin_id);
}
fprintf (fd, "%d\n", pin_id) ;
if(fclose (fd) != 0)
{
// fprintf(stderr, "Got error code %d - %s\n", errno,strerror(errno));
if(errno == EINVAL) // indicates the pin is not currently exported
return false;
else
throw OperationFailedException("Pin %d cannot be unexported",pin_id);
}
else
return true;
}
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;
}

125
src/gpio/gpio.hpp Normal file
View File

@ -0,0 +1,125 @@
#ifndef __GPIO_HPP_
#define __GPIO_HPP_
#include "../exception/baseexceptions.hpp"
#include "../thread/thread.hpp"
#include <boost/signals2.hpp>
/*! \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<void (GpioPin *, GpioEdge, bool)> 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

127
src/i2c/i2c.c Normal file
View File

@ -0,0 +1,127 @@
#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#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;
}

19
src/i2c/i2c.h Normal file
View File

@ -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

67
src/log/log.cpp Normal file
View File

@ -0,0 +1,67 @@
#include "log.hpp"
#include <string.h>
#include <stdio.h>
#include <cstdarg>
#include <stdint.h>
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<char>(c);
} else {
sync();
}
return c;
}
std::ostream& operator<< (std::ostream& os, const LogPriority& log_priority) {
static_cast<Log *>(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);
}

42
src/log/log.hpp Normal file
View File

@ -0,0 +1,42 @@
#ifndef __LOG_HPP__
#define __LOG_HPP__
#include <syslog.h>
#include <iostream>
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<char, std::char_traits<char> > {
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

36
src/mc-hid-introspect.xml Normal file
View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" ?>
<node name="/nl/miqra/MediaCore/Hid">
<interface name="nl.miqra.MediaCore.Hid">
<method name="SetColor">
<arg type="y" name="r" direction="in" />
<arg type="y" name="g" direction="in" />
<arg type="y" name="b" direction="in" />
</method>
<method name="ClearColor">
</method>
<method name="PulseColor">
<arg type="y" name="r" direction="in" />
<arg type="y" name="g" direction="in" />
<arg type="y" name="b" direction="in" />
<arg type="d" name="interval" direction="in" />
</method>
<method name="JackState">
<arg type="b" name="jackedin" direction="out" />
</method>
<signal name="ButtonDown">
<arg name="button" type="s"/>
</signal>
<signal name="ButtonUp">
<arg name="button" type="s"/>
</signal>
<signal name="JackIn" />
<signal name="JackOut" />
<signal name="ButtonPress">
<arg name="button" type="s"/>
</signal>
<signal name="ButtonLongPress">
<arg name="button" type="s"/>
</signal>
</interface>
</node>

223
src/mc-hid-server-glue.hpp Normal file
View File

@ -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 <dbus-c++/dbus.h>
#include <cassert>
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

512
src/mc-hid-server.cpp Normal file
View File

@ -0,0 +1,512 @@
#ifdef HAVE_CONFIG_H
#include <config.hpp>
#endif
#include "mc-hid-server.hpp"
#include "log/log.hpp"
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <limits.h>
#include <pthread.h>
// For getting current time
#include <time.h>
#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;
}

65
src/mc-hid-server.hpp Normal file
View File

@ -0,0 +1,65 @@
#ifndef __MC_HID_SERVER_HPP
#define __MC_HID_SERVER_HPP
#include <stdint.h>
#include <dbus-c++/dbus.h>
#include <boost/signals2.hpp>
#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

BIN
src/mc-hid-server.o Normal file

Binary file not shown.

968
src/mcp23017/mcp23017.cpp Normal file
View File

@ -0,0 +1,968 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sched.h>
#include <malloc.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#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();
}

291
src/mcp23017/mcp23017.hpp Normal file
View File

@ -0,0 +1,291 @@
#ifndef __MCP23017_H_
#define __MCP23017_H_
#include "../exception/baseexceptions.hpp"
#include "../thread/thread.hpp"
#include <stdint.h>
/*! \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

130
src/test/mcp23017-i2ctest.c Normal file
View File

@ -0,0 +1,130 @@
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#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;
}

145
src/thread/thread.cpp Normal file
View File

@ -0,0 +1,145 @@
#include "thread.hpp"
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
#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, &params); // 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();
}

48
src/thread/thread.hpp Normal file
View File

@ -0,0 +1,48 @@
#ifndef __THREAD_HPP
#define __THREAD_HPP
#include <pthread.h>
#include <string>
#include "../exception/baseexceptions.hpp"
#include <boost/signals2.hpp>
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<void (Thread *, ThreadException)> 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

2
version.py Normal file
View File

@ -0,0 +1,2 @@
PACKAGE="mediacore-hid"
VERSION="1.0.0"

2
version.py.in Normal file
View File

@ -0,0 +1,2 @@
PACKAGE="@V_PACKAGE@"
VERSION="@V_VERSION@"