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