/* vim:set ts=4: */
/* qsb.c - quick start button hotkey support for gericom x5
 *
 * Copyright (C) 2006 Stefan Scheler <sts[at]synflood[dot]de>
 *
 * This program might also work with other Gericom or Twinhead laptops.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <asm/io.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>

#define CONFIGFILE "/etc/qsb.conf"
#define MAXLINEWIDTH 200
#define MAXBUTTONS 4
#define VERSION 0.20

/* hotkey actions */
char *action_button[MAXBUTTONS];
/* poll hotkey status every 0.5 secs */
unsigned int poll_intervall = 500000;
/* ignore pending hotkeys for 1 sec */
unsigned int sleep_duration = 1000000;

/* fork a child process and run hotkey action */
void run(char *cmd) {
	int status;
	syslog(LOG_NOTICE, "launching '%s'\n", cmd);
	pid_t pid = fork();
	if (pid == 0) {
		execl("/bin/bash", "bash", "-c", cmd, NULL);
		exit(EXIT_SUCCESS);
	}
	waitpid(-1, &status, WNOHANG);
}

void exit_failure(void) {
	syslog(LOG_NOTICE, "stopping\n");
	exit(EXIT_FAILURE);
}

/* load our settings from a configuration file */
void read_configuration() {
	FILE *f;
	char *line, *token, *value;
	int action;
	line = (char *)malloc(MAXLINEWIDTH * sizeof(char));
	syslog(LOG_NOTICE, "reading configuration file %s\n", CONFIGFILE);
	if (!(f = fopen(CONFIGFILE, "r"))) {
		fprintf(stderr, "error: could not open configuration file '%s'.\n", CONFIGFILE);
		exit_failure();
	}
	/* parse configuration file */
	while (fgets(line, MAXLINEWIDTH, f)) {
		/* ignore comments and empty lines */
		if (line[0] == '#' || line[0] == '\n' || line[0] == ' ')
			continue;

		token = strtok(line, "=");
		value = strtok(NULL, "\n");

		/* set actions */
		if (strncmp(token, "action_button", 13) == 0) {
			sscanf(token, "action_button%d", &action);
			if (action > MAXBUTTONS || action < 1)
				continue;
			action--;
			action_button[action] = realloc(action_button[action], strlen(value) + 1);
			strcpy(action_button[action], value);
			syslog(LOG_NOTICE, "adjusted button %d action to '%s'\n", action+1, value);	
		}

		/* set poll_intervall */
		if (strncmp(token, "poll_intervall", 14) == 0) {
			poll_intervall = atoi(value);
			syslog(LOG_NOTICE, "adjusted poll_intervall to %d\n", poll_intervall);
			continue;
		}

		/* set sleep_duration */
		if (strncmp(token, "sleep_duration", 15) == 0) {
			sleep_duration = atoi(value);
			syslog(LOG_NOTICE, "adjusted poll_intervall to %d\n", sleep_duration);
			continue;
		}

	}
	fclose(f);
}

/* shut down gracefully */
void sig_term(int sig) {
	syslog(LOG_NOTICE, "shutting down\n");
	ioperm(0x70, 2, 0);
	closelog();
	exit(EXIT_SUCCESS);
}
 
/* reload configuration on SIGUSR1 */
void sig_usr1(int sig) {
	read_configuration();
}

/* main */
int main(int argc, char *argv[]) {
	short status = 0;
	int mode;
	/* connect to syslog */
	openlog("qsb", LOG_PID, LOG_USER);
	syslog(LOG_NOTICE, "starting\n");
	/* request permission for i/o port */
	if (ioperm(0x70, 2, 1) == -EPERM) {
		fprintf(stderr, "error: insufficient privileges to call ioperm.\n");
		exit_failure();
	}
	/* fork into background */
	daemon(0, 0);
	/* read our configuration */
	read_configuration();
	/* install some signal handlers */
	signal(SIGCLD, SIG_IGN);
	signal(SIGTERM, sig_term);
	signal(SIGUSR1, sig_usr1);
	/* reset hotkey status */
	outb(0x7b, 0x70);
	outb(0, 0x71);
	/* main loop */
	while (1) {
		/* query hotkey status */
		outb(0x7b, 0x70);
		status = inb(0x71) >> 4;
		if (status != 0) {	
			/* do hotkey action */
			switch(status) { 
				case 1: /* S1 */ 
					syslog(LOG_NOTICE, "received button 1 event\n");
					run(action_button[0]);
					break;
				case 3: /* S2 */
					syslog(LOG_NOTICE, "received button 2 event\n");	
					run(action_button[1]);
					break;
				case 4: /* S3 */
					syslog(LOG_NOTICE, "received button 3 event\n");
					run(action_button[2]);
					break;
				case 2: /* S4 */
					syslog(LOG_NOTICE, "received button 4 event\n");
					run(action_button[3]);
					break;
			}
			/* reset hotkey status */
			outb(0x7b, 0x70);
			outb(0, 0x71);
			usleep(sleep_duration);
		}
		/* sleep */
		usleep(poll_intervall);
	}
}

