Initial commit
This commit is contained in:
commit
842719c84a
50
.gitignore
vendored
Normal file
50
.gitignore
vendored
Normal 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
|
83
Makefile.am
Executable file
83
Makefile.am
Executable 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
8
TESTCMDS.txt
Normal 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
|
17
cfg/dbus/nl.miqra.MediaCore.Hid.conf
Normal file
17
cfg/dbus/nl.miqra.MediaCore.Hid.conf
Normal 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
163
cfg/init.d/mediacore-hid.in
Normal 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
|
||||||
|
|
||||||
|
:
|
4
cfg/modprobe.d/raspi-blacklist.conf
Normal file
4
cfg/modprobe.d/raspi-blacklist.conf
Normal 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
8
cfg/modules
Normal 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
148
cfg/piio.conf
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
1
cfg/rsyslog/syslog.MediaCore.Hid.conf
Normal file
1
cfg/rsyslog/syslog.MediaCore.Hid.conf
Normal file
|
@ -0,0 +1 @@
|
||||||
|
local0.* -/var/log/mediacore.log
|
23
config.hpp
Normal file
23
config.hpp
Normal 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
22
config.hpp.in
Normal 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
25
config.hpp.in~
Normal 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
21
configure.ac
Executable 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
6
deb-dependencies.txt
Normal 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
15
debian/control
vendored
Executable 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
40
debian/postinst
vendored
Normal 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
39
debian/prerm
vendored
Normal 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
56
debpackager.py
Normal 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
6
debpackager.sh
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
rm version.py
|
||||||
|
make version.py
|
||||||
|
python debpackager.py
|
||||||
|
|
31
sigtest.cpp
Normal file
31
sigtest.cpp
Normal 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);
|
||||||
|
}
|
120
src/buttontimer/buttontimer.cpp
Normal file
120
src/buttontimer/buttontimer.cpp
Normal 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);
|
||||||
|
}
|
35
src/buttontimer/buttontimer.hpp
Normal file
35
src/buttontimer/buttontimer.hpp
Normal 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);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
75
src/exception/baseexceptions.cpp
Normal file
75
src/exception/baseexceptions.cpp
Normal 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 ()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
69
src/exception/baseexceptions.hpp
Normal file
69
src/exception/baseexceptions.hpp
Normal 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
458
src/gpio/gpio.cpp
Normal 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
125
src/gpio/gpio.hpp
Normal 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
127
src/i2c/i2c.c
Normal 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
19
src/i2c/i2c.h
Normal 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
67
src/log/log.cpp
Normal 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
42
src/log/log.hpp
Normal 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
36
src/mc-hid-introspect.xml
Normal 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
223
src/mc-hid-server-glue.hpp
Normal 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
512
src/mc-hid-server.cpp
Normal 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
65
src/mc-hid-server.hpp
Normal 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
BIN
src/mc-hid-server.o
Normal file
Binary file not shown.
968
src/mcp23017/mcp23017.cpp
Normal file
968
src/mcp23017/mcp23017.cpp
Normal 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
291
src/mcp23017/mcp23017.hpp
Normal 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
130
src/test/mcp23017-i2ctest.c
Normal 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
145
src/thread/thread.cpp
Normal 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, ¶ms); // This thread is always key->pwm_thread, and if not, then it should be
|
||||||
|
|
||||||
|
if (ret != 0) {
|
||||||
|
// Print the error
|
||||||
|
clog << kLogWarning << "Unsuccessful in setting thread realtime priority" << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::ThreadLoop(void)
|
||||||
|
{
|
||||||
|
usleep(25000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::ThreadFunc(void)
|
||||||
|
{
|
||||||
|
while(running)
|
||||||
|
{
|
||||||
|
ThreadLoop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::MutexLock()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&mutex);
|
||||||
|
}
|
||||||
|
void Thread::MutexUnlock()
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// static function that calls the real function
|
||||||
|
void * thread_threadStarter(void * obj)
|
||||||
|
{
|
||||||
|
Thread * wt = (Thread*)obj;
|
||||||
|
wt->ThreadStarter();
|
||||||
|
}
|
48
src/thread/thread.hpp
Normal file
48
src/thread/thread.hpp
Normal 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
2
version.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
PACKAGE="mediacore-hid"
|
||||||
|
VERSION="1.0.0"
|
2
version.py.in
Normal file
2
version.py.in
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
PACKAGE="@V_PACKAGE@"
|
||||||
|
VERSION="@V_VERSION@"
|
Loading…
Reference in New Issue
Block a user