#include <errno.h> // for errno
#include <time.h> // for time(2)
#include <ctype.h> // for isdigit
+#include <signal.h> // for sigaction
+#include <sys/wait.h> // for wait
+#include <sys/time.h> // setitimer
#include <linux/input.h> // for keys
//#include "../../kernel-rip/input.h" // for keys
#include "pnd_logger.h"
#include "pnd_utility.h"
#include "pnd_notify.h"
+#include "pnd_device.h"
// daemon and logging
//
evmap_t g_evmap [ MAXEVENTS ];
unsigned int g_evmap_max = 0;
+// battery
+unsigned char b_threshold = 5; // %battery
+unsigned int b_frequency = 300; // frequency to check
+unsigned int b_blinkfreq = 2; // blink every 2sec
+unsigned int b_blinkdur = 1000; // blink duration (uSec), 0sec + uSec is assumed
+unsigned char b_active = 0; // 0=inactive, 1=active and waiting to blink, 2=blink is on, waiting to turn off
+unsigned char b_shutdown = 1; // %age battery to force a shutdown!
+char *b_shutdown_script = NULL;
+
/* get to it
*/
void dispatch_key ( int keycode, int val );
void dispatch_event ( int code, int val );
+void sigchld_handler ( int n );
+unsigned char set_next_alarm ( unsigned int secs, unsigned int usecs );
+void sigalrm_handler ( int n );
static void usage ( char *argv[] ) {
printf ( "%s [-d]\n", argv [ 0 ] );
printf ( "-d\tDaemon mode; detach from terminal, chdir to /tmp, suppress output. Optional.\n" );
printf ( "-l#\tLog-it; -l is 0-and-up (or all), and -l2 means 2-and-up (not all); l[0-3] for now. Log goes to /tmp/pndevmapperd.log\n" );
- printf ( "Signal: HUP the process to force reload of configuration and reset the notifier watch paths\n" );
return;
}
} else if ( strncmp ( k, "pndevmapperd.", 7 ) == 0 ) {
// not consumed here, skip silently
+ } else if ( strncmp ( k, "battery.", 8 ) == 0 ) {
+ // not consumed here, skip silently
+
} else {
// uhhh
pnd_log ( pndn_warning, "Unknown config key '%s'; skipping.\n", k );
pnd_log ( pndn_rem, "config file causes minimum_separation to change to %u", g_minimum_separation );
}
+ // battery conf
+ if ( pnd_conf_get_as_int ( evmaph, "battery.threshold" ) != PND_CONF_BADNUM ) {
+ b_threshold = pnd_conf_get_as_int ( evmaph, "battery.threshold" );
+ pnd_log ( pndn_rem, "Battery threshold set to %u", b_threshold );
+ }
+ if ( pnd_conf_get_as_int ( evmaph, "battery.check_interval" ) != PND_CONF_BADNUM ) {
+ b_frequency = pnd_conf_get_as_int ( evmaph, "battery.check_interval" );
+ pnd_log ( pndn_rem, "Battery check interval set to %u", b_frequency );
+ }
+ if ( pnd_conf_get_as_int ( evmaph, "battery.blink_interval" ) != PND_CONF_BADNUM ) {
+ b_blinkfreq = pnd_conf_get_as_int ( evmaph, "battery.blink_interval" );
+ pnd_log ( pndn_rem, "Battery blink interval set to %u", b_blinkfreq );
+ }
+ if ( pnd_conf_get_as_int ( evmaph, "battery.blink_duration" ) != PND_CONF_BADNUM ) {
+ b_blinkdur = pnd_conf_get_as_int ( evmaph, "battery.blink_duration" );
+ pnd_log ( pndn_rem, "Battery blink duration set to %u", b_blinkdur );
+ }
+ b_active = 0;
+ if ( pnd_conf_get_as_int ( evmaph, "battery.shutdown_threshold" ) != PND_CONF_BADNUM ) {
+ b_shutdown = pnd_conf_get_as_int ( evmaph, "battery.shutdown_threshold" );
+ pnd_log ( pndn_rem, "Battery shutdown threshold set to %u", b_shutdown );
+ }
+ if ( pnd_conf_get_as_char ( evmaph, "battery.shutdown_script" ) != NULL ) {
+ b_shutdown_script = strdup ( pnd_conf_get_as_char ( evmaph, "battery.shutdown_script" ) );
+ pnd_log ( pndn_rem, "Battery shutdown script set to %s", b_shutdown_script );
+ }
+
/* do we have anything to do?
*/
if ( ! g_evmap_max ) {
exit ( -1 );
} // spin
+ /* set up sigchld -- don't want zombies all over; well, we do, but not process zombies
+ */
+ sigset_t ss;
+ sigemptyset ( &ss );
+
+ struct sigaction siggy;
+ siggy.sa_handler = sigchld_handler;
+ siggy.sa_mask = ss; /* implicitly blocks the origin signal */
+ siggy.sa_flags = SA_RESTART; /* don't need anything */
+ sigaction ( SIGCHLD, &siggy, NULL );
+
+ /* set up the battery level warning timers
+ */
+ siggy.sa_handler = sigalrm_handler;
+ siggy.sa_mask = ss; /* implicitly blocks the origin signal */
+ siggy.sa_flags = SA_RESTART; /* don't need anything */
+ sigaction ( SIGALRM, &siggy, NULL );
+
+ if ( set_next_alarm ( b_frequency, 0 ) ) { // check every 'frequency' seconds
+ pnd_log ( pndn_rem, "Checking for low battery every %u seconds\n", b_frequency );
+ } else {
+ pnd_log ( pndn_error, "ERROR: Couldn't set up timer for every %u seconds\n", b_frequency );
+ }
+
/* actually try to do something useful
*/
// try to locate the appropriate devices
int id;
- int fds [ 5 ] = { -1, -1, -1, -1, -1 }; // 0 = keypad, 1 = gpio keys
+ int fds [ 8 ] = { -1, -1, -1, -1, -1, -1, -1, -1 }; // 0 = keypad, 1 = gpio keys
int imaxfd = 0;
for ( id = 0; ; id++ ) {
break;
}
- ioctl (fd, EVIOCGNAME(sizeof(name)), name );
+ if ( ioctl (fd, EVIOCGNAME(sizeof(name)), name ) < 0 ) {
+ name [ 0 ] = '\0';
+ }
+
+ pnd_log ( pndn_rem, "%s maps to %s\n", fname, name );
if ( strcmp ( name, "omap_twl4030keypad" ) == 0 ) {
fds [ 0 ] = fd;
fds [ 1 ] = fd;
} else if ( strcmp ( name, "AT Translated Set 2 keyboard" ) == 0) { // for vmware, my dev environment
fds [ 0 ] = fd;
+ } else if ( strcmp ( name, "triton2-pwrbutton" ) == 0) {
+ fds [ 2 ] = fd;
+ } else if ( strcmp ( name, "ADS784x Touchscreen" ) == 0) {
+ fds [ 3 ] = fd;
+ } else if ( strcmp ( name, "vsense66" ) == 0) {
+ fds [ 4 ] = fd;
+ } else if ( strcmp ( name, "vsense67" ) == 0) {
+ fds [ 5 ] = fd;
} else {
pnd_log ( pndn_rem, "Ignoring unknown device '%s'\n", name );
+ //fds [ 6 ] = fd;
close ( fd );
+ fd = -1;
continue;
}
if (imaxfd < fd) imaxfd = fd;
+
} // for
if ( fds [ 0 ] == -1 ) {
}
if ( fds [ 0 ] == -1 && fds [ 1 ] == -1 ) {
- pnd_log ( pndn_error, "ERROR! Couldn't find either device; exiting!\n" );
- exit ( -2 );
+ pnd_log ( pndn_error, "ERROR! Couldn't find either device!\n" );
+ //exit ( -2 );
}
/* loop forever, watching for events
while ( 1 ) {
struct input_event ev[64];
+ unsigned int max_fd = 3; /* imaxfd */
int fd = -1, rd, ret;
fd_set fdset;
FD_ZERO ( &fdset );
- for (i = 0; i < 2; i++) {
+ imaxfd = 0;
+ for (i = 0; i < max_fd /*imaxfd*/; i++) {
if ( fds [ i ] != -1 ) {
FD_SET( fds [ i ], &fdset );
+
+ if ( fds [ i ] > imaxfd ) {
+ imaxfd = fds [ i ];
+ }
+
}
}
if ( ret == -1 ) {
pnd_log ( pndn_error, "ERROR! select(2) failed with: %s\n", strerror ( errno ) );
- break;
+ continue; // retry!
}
- for ( i = 0; i < 2; i++ ) {
+ for ( i = 0; i < max_fd; i++ ) {
if ( fds [ i ] != -1 && FD_ISSET ( fds [ i ], &fdset ) ) {
fd = fds [ i ];
- }
- }
+ } // fd is set?
+ } // for
/* buttons or keypad */
rd = read ( fd, ev, sizeof(struct input_event) * 64 );
// 2 - down again (hold)
// 0 - up (released)
+ pnd_log ( pndn_rem, "Dispatching Key..\n" );
+
for ( i = 0; i < g_evmap_max; i++ ) {
if ( ( g_evmap [ i ].key_p ) &&
// 1 - closing
// 0 - opening
+ pnd_log ( pndn_rem, "Dispatching Event..\n" );
+
for ( i = 0; i < g_evmap_max; i++ ) {
if ( ( g_evmap [ i ].key_p == 0 ) &&
return;
}
+
+void sigchld_handler ( int n ) {
+
+ pnd_log ( pndn_rem, "---[ SIGCHLD received ]---\n" );
+
+ int status;
+ wait ( &status );
+
+ pnd_log ( pndn_rem, " SIGCHLD done ]---\n" );
+
+ return;
+}
+
+unsigned char set_next_alarm ( unsigned int secs, unsigned int usecs ) {
+
+ // assume that SIGALRM is already being caught, we just set the itimer here
+
+ struct itimerval itv;
+
+ // if no timer at all, set the 'current' one so it does something; otherwise
+ // let it continue..
+ getitimer ( ITIMER_REAL, &itv );
+
+ if ( itv.it_value.tv_sec == 0 && itv.it_value.tv_sec == 0 ) {
+ itv.it_value.tv_sec = secs;
+ itv.it_value.tv_usec = usecs;
+ }
+
+ // set the next timer
+ //bzero ( &itv, sizeof(struct itimerval) );
+
+ itv.it_interval.tv_sec = secs;
+ itv.it_interval.tv_usec = usecs;
+
+ // if next-timer is less than current, set current too
+ if ( itv.it_value.tv_sec > itv.it_interval.tv_sec ) {
+ itv.it_value.tv_sec = secs;
+ itv.it_value.tv_usec = usecs;
+ }
+
+ if ( setitimer ( ITIMER_REAL, &itv, NULL /* old value returned here */ ) < 0 ) {
+ // sucks
+ return ( 0 );
+ }
+
+ return ( 1 );
+}
+
+void sigalrm_handler ( int n ) {
+
+ pnd_log ( pndn_debug, "---[ SIGALRM ]---\n" );
+
+ int batlevel = pnd_device_get_battery_gauge_perc();
+
+ if ( batlevel < 0 ) {
+ // couldn't read the battery level, so just assume low and make blinks?
+ batlevel = 4; // low, but not cause a shutdown
+ }
+
+ // first -- are we critical yet? if so, shut down!
+ if ( batlevel <= b_shutdown && b_shutdown_script ) {
+ int x;
+
+ pnd_log ( pndn_error, "CRITICAL BATTERY LEVEL -- shutdown the system down! Invoke: %s\n", b_shutdown_script );
+
+ if ( ( x = fork() ) < 0 ) {
+ pnd_log ( pndn_error, "ERROR: Couldn't fork()\n" );
+ exit ( -3 );
+ }
+
+ if ( x == 0 ) {
+ execl ( b_shutdown_script, b_shutdown_script, (char*)NULL );
+ pnd_log ( pndn_error, "ERROR: Couldn't exec(%s)\n", b_shutdown_script );
+ exit ( -4 );
+ }
+
+ }
+
+ // is battery warning already active?
+ if ( b_active ) {
+ // warning is on!
+
+ // is user charging up? if so, stop blinking.
+ // perhaps we shoudl check if charger is connected, and not blink at all in that case..
+ if ( batlevel > b_threshold + 1 /* allow for error in read */ ) {
+ pnd_log ( pndn_debug, "Battery is high again, flipping to non-blinker mode\n" );
+ b_active = 0;
+ set_next_alarm ( b_frequency, 0 );
+ pnd_device_set_led_charger_brightness ( 250 );
+ return;
+ }
+
+ if ( b_active == 1 ) {
+ // turn LED on
+ pnd_log ( pndn_debug, "Blink on\n" );
+ pnd_device_set_led_charger_brightness ( 200 );
+ // set timer to short duration
+ b_active = 2;
+ set_next_alarm ( 0, b_blinkdur );
+ } else if ( b_active == 2 ) {
+ // turn LED off
+ pnd_log ( pndn_debug, "Blink off\n" );
+ pnd_device_set_led_charger_brightness ( 10 );
+ // back to longer duration
+ b_active = 1;
+ set_next_alarm ( b_blinkfreq, 0 );
+ }
+
+ return;
+ }
+
+ // warning is off..
+ if ( batlevel <= b_threshold ) {
+ // battery seems low, go to active mode
+ pnd_log ( pndn_debug, "Battery is low, flipping to blinker mode\n" );
+ b_active = 1;
+ set_next_alarm ( b_blinkfreq, 0 );
+ } // battery level
+
+ return;
+}