Initial commit
This commit is contained in:
		
						commit
						842719c84a
					
				
					 44 changed files with 5476 additions and 0 deletions
				
			
		
							
								
								
									
										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 | ||||
| 
 | ||||
							
								
								
									
										
											BIN
										
									
								
								sigtest
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								sigtest
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										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 a new issue
	
	 Miqra Developer
						Miqra Developer