summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan McGee <dpmcgee@gmail.com>2010-10-23 01:43:55 -0500
committerDan McGee <dpmcgee@gmail.com>2011-01-21 16:04:09 -0600
commit466e4e6986160ee591ee0e2601b1fbacfd15e064 (patch)
treebed7735afaef254a7ff1215b5bf7569f45be0abb
parent44f47962fc93a949bf3b478d7c012d03041d2ec8 (diff)
downloadonkyocontrol-466e4e6986160ee591ee0e2601b1fbacfd15e064.tar.gz
onkyocontrol-466e4e6986160ee591ee0e2601b1fbacfd15e064.zip
Receiver global state refactor
This is in anticipation of doing the eISCP network-based control, but I haven't been too happy with the amount of global state for a while. Get it a bit more wrapped up in this receiver class, which whether we need it or not, now allows us to send commands to as many receivers as we want. Signed-off-by: Dan McGee <dpmcgee@gmail.com>
-rw-r--r--command.c201
-rw-r--r--onkyo.c298
-rw-r--r--onkyo.h23
3 files changed, 293 insertions, 229 deletions
diff --git a/command.c b/command.c
index d15052b..41767f2 100644
--- a/command.c
+++ b/command.c
@@ -29,7 +29,7 @@
static struct command *command_list = NULL;
-typedef int (cmd_handler) (const struct command *, const char *);
+typedef int (cmd_handler) (struct receiver *, const struct command *, const char *);
struct command {
unsigned long hash;
@@ -56,16 +56,55 @@ static char *strtoupper(char *str)
}
/**
+ * Queue a receiver command to be sent when the serial device file descriptor
+ * is available for writing. Queueing and sending asynchronously allows
+ * the program to backlog many commands at once without blocking on the
+ * relatively slow serial device. When queueing, we check if this command
+ * is already in the queue- if so, we do not queue it again.
+ * @param rcvr the receiver to queue the command for
+ * @param cmd command to queue, will be freed once it is actually ran
+ * @return 0 on queueing success, 1 on queueing skip
+ */
+static int queue_rcvr_command(struct receiver *rcvr, char *cmd)
+{
+ struct cmdqueue *q = malloc(sizeof(struct cmdqueue));
+ q->hash = hash_sdbm(cmd);
+ q->cmd = cmd;
+ q->next = NULL;
+
+ if(rcvr->queue == NULL) {
+ rcvr->queue = q;
+ } else {
+ struct cmdqueue *ptr = rcvr->queue;
+ for(;;) {
+ if(ptr->hash == q->hash) {
+ /* command already in our queue, skip second copy */
+ free(q);
+ free(cmd);
+ return(1);
+ }
+ if(!ptr->next)
+ break;
+ ptr = ptr->next;
+ }
+ ptr->next = q;
+ }
+ return(0);
+}
+
+/**
* Attempt to write a receiver command out to the control channel.
- * This will take a in a simple receiver string minus any preamble or
- * ending characters, turn it into a valid command, and queue it to the
- * sent to the receiver.
+ * This will take a in a simple receiver string minus any preamble or ending
+ * characters, turn it into a valid command, and queue it to send to the
+ * receiver.
+ * @param rcvr the receiver the command should be queued for
* @param cmd the command struct for the first part of the receiver command
* string, usually containing a prefix aka "PWR"
* @param arg the second part of the receiver command string, aka "QSTN"
* @return 0 on success, -1 on missing args
*/
-static int cmd_attempt(const struct command *cmd, const char *arg)
+static int cmd_attempt(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
{
char *fullcmd;
@@ -80,57 +119,62 @@ static int cmd_attempt(const struct command *cmd, const char *arg)
sprintf(fullcmd, START_SEND "%s%s" END_SEND, cmd->prefix, arg);
/* send the command to the receiver */
- queue_rcvr_command(fullcmd);
+ queue_rcvr_command(rcvr, fullcmd);
return(0);
}
-static int cmd_attempt_raw(const char *fake, const char *arg)
+static int cmd_attempt_raw(struct receiver *rcvr,
+ const char *fake, const char *arg)
{
struct command c;
c.prefix = fake;
c.fake = 0;
- return cmd_attempt(&c, arg);
+ return cmd_attempt(rcvr, &c, arg);
}
/**
* Handle the standard question, up, and down operations if possible.
+ * @param rcvr the receiver the command should be queued for
* @param cmd the struct containing information on the command being called
* @param arg the provided argument, e.g. "QSTN"
* @return the return value of cmd_attempt() if we found a standard operation,
* else -2
*/
-static int handle_standard(const struct command *cmd, const char *arg)
+static int handle_standard(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
{
if(!arg || strcmp(arg, "status") == 0)
- return cmd_attempt(cmd, "QSTN");
+ return cmd_attempt(rcvr, cmd, "QSTN");
else if(strcmp(arg, "up") == 0)
- return cmd_attempt(cmd, "UP");
+ return cmd_attempt(rcvr, cmd, "UP");
else if(strcmp(arg, "down") == 0)
- return cmd_attempt(cmd, "DOWN");
+ return cmd_attempt(rcvr, cmd, "DOWN");
return(-2);
}
-static int handle_boolean(const struct command *cmd, const char *arg)
+static int handle_boolean(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
{
if(!arg || strcmp(arg, "status") == 0)
- return cmd_attempt(cmd, "QSTN");
+ return cmd_attempt(rcvr, cmd, "QSTN");
else if(strcmp(arg, "on") == 0)
- return cmd_attempt(cmd, "01");
+ return cmd_attempt(rcvr, cmd, "01");
else if(strcmp(arg, "off") == 0)
- return cmd_attempt(cmd, "00");
+ return cmd_attempt(rcvr, cmd, "00");
else if(strcmp(arg, "toggle") == 0) {
const char *prefix = cmd->prefix;
/* toggle is applicable for mute, not for power */
if(strcmp(prefix, "AMT") == 0 || strcmp(prefix, "ZMT") == 0
|| strcmp(prefix, "MT3") == 0)
- return cmd_attempt(cmd, "TG");
+ return cmd_attempt(rcvr, cmd, "TG");
}
/* unrecognized command */
return(-1);
}
-static int handle_ranged(const struct command *cmd, const char *arg,
+static int handle_ranged(struct receiver *rcvr,
+ const struct command *cmd, const char *arg,
int lower, int upper, int offset, const char *fmt)
{
int ret;
@@ -138,7 +182,7 @@ static int handle_ranged(const struct command *cmd, const char *arg,
char *test;
char cmdstr[3]; /* "XX\0" */
- ret = handle_standard(cmd, arg);
+ ret = handle_standard(rcvr, cmd, arg);
if(ret != -2)
return (ret);
@@ -156,38 +200,43 @@ static int handle_ranged(const struct command *cmd, const char *arg,
/* create our command */
sprintf(cmdstr, fmt, (unsigned long)level);
/* send the command */
- return cmd_attempt(cmd, cmdstr);
+ return cmd_attempt(rcvr, cmd, cmdstr);
}
-static int handle_volume(const struct command *cmd, const char *arg)
+static int handle_volume(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
{
- return handle_ranged(cmd, arg, 0, 100, 0, "%02lX");
+ return handle_ranged(rcvr, cmd, arg, 0, 100, 0, "%02lX");
}
-static int handle_dbvolume(const struct command *cmd, const char *arg)
+static int handle_dbvolume(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
{
- return handle_ranged(cmd, arg, -82, 18, 82, "%02lX");
+ return handle_ranged(rcvr, cmd, arg, -82, 18, 82, "%02lX");
}
-static int handle_preset(const struct command *cmd, const char *arg)
+static int handle_preset(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
{
- return handle_ranged(cmd, arg, 0, 40, 0, "%02lX");
+ return handle_ranged(rcvr, cmd, arg, 0, 40, 0, "%02lX");
}
-static int handle_avsync(const struct command *cmd, const char *arg)
+static int handle_avsync(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
{
/* the extra '0' is an easy way to not have to multiply by 10 */
- return handle_ranged(cmd, arg, 0, 250, 0, "%03ld0");
+ return handle_ranged(rcvr, cmd, arg, 0, 250, 0, "%03ld0");
}
-static int handle_swlevel(const struct command *cmd, const char *arg)
+static int handle_swlevel(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
{
int ret;
long level;
char *test;
char cmdstr[3]; /* "XX\0" */
- ret = handle_standard(cmd, arg);
+ ret = handle_standard(rcvr, cmd, arg);
if(ret != -2)
return (ret);
@@ -210,7 +259,7 @@ static int handle_swlevel(const struct command *cmd, const char *arg)
sprintf(cmdstr, "-%1lX", (unsigned long)-level);
}
/* send the command */
- return cmd_attempt(cmd, cmdstr);
+ return cmd_attempt(rcvr, cmd, cmdstr);
}
static const char * const inputs[][2] = {
@@ -242,13 +291,14 @@ static const char * const inputs[][2] = {
{ "SIRIUS", "32" },
};
-static int handle_input(const struct command *cmd, const char *arg)
+static int handle_input(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
{
unsigned int i, loopsize;
int ret;
char *dup;
- ret = handle_standard(cmd, arg);
+ ret = handle_standard(rcvr, cmd, arg);
if(ret != -2)
return (ret);
@@ -260,7 +310,7 @@ static int handle_input(const struct command *cmd, const char *arg)
loopsize = sizeof(inputs) / sizeof(*inputs);
for(i = 0; i < loopsize; i++) {
if(strcmp(dup, inputs[i][0]) == 0) {
- ret = cmd_attempt(cmd, inputs[i][1]);
+ ret = cmd_attempt(rcvr, cmd, inputs[i][1]);
break;
}
}
@@ -269,9 +319,9 @@ static int handle_input(const struct command *cmd, const char *arg)
(strcmp(cmd->prefix, "SLZ") == 0 ||
strcmp(cmd->prefix, "SL3") == 0)) {
if(strcmp(dup, "OFF") == 0)
- ret = cmd_attempt(cmd, "7F");
+ ret = cmd_attempt(rcvr, cmd, "7F");
else if(strcmp(dup, "SOURCE") == 0)
- ret = cmd_attempt(cmd, "80");
+ ret = cmd_attempt(rcvr, cmd, "80");
}
free(dup);
@@ -310,13 +360,14 @@ static const char * const modes[][2] = {
{ "NEURALTHX", "88" },
};
-static int handle_mode(const struct command *cmd, const char *arg)
+static int handle_mode(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
{
unsigned int i, loopsize;
int ret;
char *dup;
- ret = handle_standard(cmd, arg);
+ ret = handle_standard(rcvr, cmd, arg);
if(ret != -2)
return (ret);
@@ -328,7 +379,7 @@ static int handle_mode(const struct command *cmd, const char *arg)
loopsize = sizeof(modes) / sizeof(*modes);
for(i = 0; i < loopsize; i++) {
if(strcmp(dup, modes[i][0]) == 0) {
- ret = cmd_attempt(cmd, modes[i][1]);
+ ret = cmd_attempt(rcvr, cmd, modes[i][1]);
break;
}
}
@@ -337,13 +388,14 @@ static int handle_mode(const struct command *cmd, const char *arg)
return(ret);
}
-static int handle_tune(const struct command *cmd, const char *arg)
+static int handle_tune(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
{
int ret;
char cmdstr[6]; /* "00000\0" */
char *test;
- ret = handle_standard(cmd, arg);
+ ret = handle_standard(rcvr, cmd, arg);
if(ret != -2)
return (ret);
@@ -382,19 +434,20 @@ static int handle_tune(const struct command *cmd, const char *arg)
/* we want to print something like "TUN00780" */
sprintf(cmdstr, "%05ld", freq);
}
- return cmd_attempt(cmd, cmdstr);
+ return cmd_attempt(rcvr, cmd, cmdstr);
}
-static int handle_sleep(const struct command *cmd, const char *arg)
+static int handle_sleep(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
{
long mins;
char *test;
char cmdstr[3]; /* "XX\0" */
if(!arg || strcmp(arg, "status") == 0)
- return cmd_attempt(cmd, "QSTN");
+ return cmd_attempt(rcvr, cmd, "QSTN");
else if(strcmp(arg, "off") == 0)
- return cmd_attempt(cmd, "OFF");
+ return cmd_attempt(rcvr, cmd, "OFF");
/* otherwise we probably have a number */
mins = strtol(arg, &test, 10);
@@ -409,45 +462,48 @@ static int handle_sleep(const struct command *cmd, const char *arg)
/* create our command */
sprintf(cmdstr, "%02lX", (unsigned long)mins);
/* send the command */
- return cmd_attempt(cmd, cmdstr);
+ return cmd_attempt(rcvr, cmd, cmdstr);
}
-static int handle_memory(const struct command *cmd, const char *arg)
+static int handle_memory(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
{
if(!arg)
return(-1);
if(strcmp(arg, "lock") == 0)
- return cmd_attempt(cmd, "LOCK");
+ return cmd_attempt(rcvr, cmd, "LOCK");
else if(strcmp(arg, "unlock") == 0)
- return cmd_attempt(cmd, "UNLK");
+ return cmd_attempt(rcvr, cmd, "UNLK");
return(-1);
}
-static int handle_status(const struct command *cmd, const char *arg)
+
+static int handle_status(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
{
int ret = 0;
/* this handler is a bit different in that we call
* multiple receiver commands */
if(strcmp(cmd->name, "status") == 0 && (!arg || strcmp(arg, "main") == 0)) {
- ret += cmd_attempt_raw("PWR", "QSTN");
- ret += cmd_attempt_raw("MVL", "QSTN");
- ret += cmd_attempt_raw("AMT", "QSTN");
- ret += cmd_attempt_raw("SLI", "QSTN");
- ret += cmd_attempt_raw("LMD", "QSTN");
- ret += cmd_attempt_raw("TUN", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "PWR", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "MVL", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "AMT", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "SLI", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "LMD", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "TUN", "QSTN");
} else if(strcmp(cmd->name, "zone2status") == 0 || (arg && strcmp(arg, "zone2") == 0)) {
- ret += cmd_attempt_raw("ZPW", "QSTN");
- ret += cmd_attempt_raw("ZVL", "QSTN");
- ret += cmd_attempt_raw("ZMT", "QSTN");
- ret += cmd_attempt_raw("SLZ", "QSTN");
- ret += cmd_attempt_raw("TUZ", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "ZPW", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "ZVL", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "ZMT", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "SLZ", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "TUZ", "QSTN");
} else if(strcmp(cmd->name, "zone3status") == 0 || (arg && strcmp(arg, "zone3") == 0)) {
- ret += cmd_attempt_raw("PW3", "QSTN");
- ret += cmd_attempt_raw("VL3", "QSTN");
- ret += cmd_attempt_raw("MT3", "QSTN");
- ret += cmd_attempt_raw("SL3", "QSTN");
- ret += cmd_attempt_raw("TU3", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "PW3", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "VL3", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "MT3", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "SL3", "QSTN");
+ ret += cmd_attempt_raw(rcvr, "TU3", "QSTN");
} else {
return(-1);
}
@@ -455,12 +511,14 @@ static int handle_status(const struct command *cmd, const char *arg)
return(ret < 0 ? -2 : 0);
}
-static int handle_raw(const struct command *cmd, const char *arg)
+static int handle_raw(struct receiver *rcvr,
+ const struct command *cmd, const char *arg)
{
- return cmd_attempt(cmd, arg);
+ return cmd_attempt(rcvr, cmd, arg);
}
-static int handle_quit(UNUSED const struct command *cmd, UNUSED const char *arg)
+static int handle_quit(UNUSED struct receiver *rcvr,
+ UNUSED const struct command *cmd, UNUSED const char *arg)
{
return -2;
}
@@ -573,12 +631,13 @@ void free_commands(void)
* format. Attempt to locate a handler for the given command and delegate
* the work to it. If no handler is found, return an error; otherwise
* return the relevant human-readable status message.
+ * @param rcvr the receiver to process the command for
* @param str the full command string, e.g. "power on"
* @return 0 if the command string was correct and sent, -1 on invalid command,
* -2 if we should quit/close the connection
* string
*/
-int process_command(const char *str)
+int process_command(struct receiver *rcvr, const char *str)
{
unsigned long hashval;
char *cmdstr, *argstr;
@@ -606,7 +665,7 @@ int process_command(const char *str)
while(cmd) {
if(cmd->hash == hashval) {
/* we found the handler, call it and return the result */
- int ret = cmd->handler(cmd, argstr);
+ int ret = cmd->handler(rcvr, cmd, argstr);
free(cmdstr);
return(ret);
}
diff --git a/onkyo.c b/onkyo.c
index 384a0fe..e26df20 100644
--- a/onkyo.c
+++ b/onkyo.c
@@ -57,28 +57,17 @@ struct conn {
struct conn *next;
};
-struct cmdqueue {
- unsigned long hash;
- char *cmd;
- struct cmdqueue *next;
-};
-
-/* our serial device and associated dealings */
-static int serialdev;
-static struct termios serialdev_oldtio;
-static struct cmdqueue *serialdev_cmdqueue;
/** file descriptor for raw output logging */
-static int logfd;
+static int logfd = -1;
+/** our list of receivers we send commands to */
+static struct receiver *receivers = NULL;
/** our list of listening sockets/descriptors we accept connections on */
-static struct fdlist *listeners;
+static struct fdlist *listeners = NULL;
/** our list of open connections we process commands on */
-static struct conn *connections;
+static struct conn *connections = NULL;
/** pipe used for async-safe signal handling in our select */
static int signalpipe[2] = { -1, -1 };
-/** power status of receiver */
-static enum power serialdev_power;
-
/* common messages */
static const char * const startup_msg = "OK:onkyocontrol v1.1\n";
static const char * const invalid_cmd = "ERROR:Invalid Command\n";
@@ -183,20 +172,23 @@ static void end_connection(struct conn *c, int freebufs)
static void cleanup(int ret) __attribute__ ((noreturn));
static void cleanup(int ret)
{
- /* clear our command queue */
- while(serialdev_cmdqueue) {
- struct cmdqueue *ptr = serialdev_cmdqueue;
- free(serialdev_cmdqueue->cmd);
- serialdev_cmdqueue = serialdev_cmdqueue->next;
- free(ptr);
- }
-
- /* reset/close our serial device */
- if(serialdev > -1) {
- /* just ignore possible errors here, can't do anything */
- tcsetattr(serialdev, TCSANOW, &(serialdev_oldtio));
- xclose(serialdev);
- serialdev = -1;
+ while(receivers) {
+ struct receiver *rcvr = receivers;
+ /* clear our command queue */
+ while(rcvr->queue) {
+ struct cmdqueue *ptr = rcvr->queue;
+ free(ptr->cmd);
+ rcvr->queue = ptr->next;
+ free(ptr);
+ }
+ /* reset/close our receiver device */
+ if(rcvr->fd > -1) {
+ /* just ignore possible errors here, can't do anything */
+ tcsetattr(rcvr->fd, TCSANOW, &(rcvr->serial_oldtio));
+ xclose(rcvr->fd);
+ }
+ free(rcvr);
+ receivers = receivers->next;
}
/* close the log file descriptor */
@@ -270,9 +262,20 @@ static void pipehandler(int signo)
*/
static void show_status(void)
{
+ struct receiver *r;
struct fdlist *l;
struct conn *c;
- printf("serial device : %d\n", serialdev);
+ r = receivers;
+ while(r) {
+ printf("receiver : %d (%d)\n", r->fd, r->type);
+ printf("power status : %X; main (%s) zone2 (%s) zone3 (%s)\n",
+ r->power,
+ r->power & MAIN_POWER ? "ON" : "off",
+ r->power & ZONE2_POWER ? "ON" : "off",
+ r->power & ZONE3_POWER ? "ON" : "off");
+
+ r = r->next;
+ }
printf("log file : %d\n", logfd);
printf("listeners : ");
@@ -288,12 +291,6 @@ static void show_status(void)
printf("%d ", c->fd);
c = c->next;
}
-
- printf("\npower status : %X; main (%s) zone2 (%s) zone3 (%s)\n",
- serialdev_power,
- serialdev_power & MAIN_POWER ? "ON" : "off",
- serialdev_power & ZONE2_POWER ? "ON" : "off",
- serialdev_power & ZONE3_POWER ? "ON" : "off");
}
/**
@@ -378,32 +375,21 @@ static void daemonize(void)
*/
static int open_serial_device(const char *path)
{
- int fd, ret;
+ int ret;
struct termios newtio;
-
- /* reset/close any existing serial device */
- if(serialdev > -1) {
- /* just ignore possible errors here, can't do anything */
- tcsetattr(serialdev, TCSANOW, &(serialdev_oldtio));
- xclose(serialdev);
- serialdev = -1;
- }
+ struct receiver *rcvr = calloc(1, sizeof(struct receiver));
/* Open serial device for reading and writing, but not as controlling
* TTY because we don't want to get killed if linenoise sends CTRL-C.
*/
- fd = xopen(path, O_RDWR | O_NOCTTY);
- if (fd < 0) {
- perror(path);
- return(-1);
- }
+ rcvr->fd = xopen(path, O_RDWR | O_NOCTTY);
+ if (rcvr->fd < 0)
+ goto cleanup;
/* save current serial port settings */
- ret = tcgetattr(fd, &serialdev_oldtio);
- if (ret < 0) {
- perror(path);
- return(-1);
- }
+ ret = tcgetattr(rcvr->fd, &rcvr->serial_oldtio);
+ if (ret < 0)
+ goto cleanup;
memset(&newtio, 0, sizeof(struct termios));
/* Set:
@@ -425,20 +411,35 @@ static int open_serial_device(const char *path)
newtio.c_cc[VEOL] = END_RECV[strlen(END_RECV) - 1];
/* clean the line and activate the settings */
- ret = tcflush(fd, TCIOFLUSH);
- if(ret < 0) {
- perror(path);
- return(-1);
- }
- ret = tcsetattr(fd, TCSAFLUSH, &newtio);
- if(ret < 0) {
- perror(path);
- return(-1);
+ ret = tcflush(rcvr->fd, TCIOFLUSH);
+ if(ret < 0)
+ goto cleanup;
+
+ ret = tcsetattr(rcvr->fd, TCSAFLUSH, &newtio);
+ if(ret < 0)
+ goto cleanup;
+
+ /* a few more pieces of info filled in */
+ rcvr->power = initial_power_status();
+ /* queue up an initial power command */
+ process_command(rcvr, "power");
+
+ /* place the device in our global list */
+ if(!receivers) {
+ receivers = rcvr;
+ } else {
+ struct receiver *ptr = rcvr;
+ while(ptr->next)
+ ptr = ptr->next;
+ ptr->next = rcvr;
}
- /* place the device in our global fd */
- serialdev = fd;
- return(fd);
+ return(rcvr->fd);
+
+cleanup:
+ perror(path);
+ free(rcvr);
+ return(-1);
}
/**
@@ -664,10 +665,15 @@ static int process_input(struct conn *c)
while(count > 0) {
if(*c->recv_buf_pos == '\n') {
int processret;
+ struct receiver *r;
/* We have a newline. This means we should have a full command
* and can attempt to interpret it. */
*c->recv_buf_pos = '\0';
- processret = process_command(c->recv_buf);
+ r = receivers;
+ while(r) {
+ processret = process_command(r, c->recv_buf);
+ r = r->next;
+ }
if(processret == -1) {
/* watch our write for a failure */
if(xwrite(c->fd, invalid_cmd, strlen(invalid_cmd)) == -1)
@@ -704,60 +710,36 @@ static int process_input(struct conn *c)
return(ret);
}
-/**
- * Queue a receiver command to be sent when the serial device file descriptor
- * is available for writing. Queueing and sending asynchronously allows
- * the program to backlog many commands at once without blocking on the
- * relatively slow serial device. When queueing, we check if this command
- * is already in the queue- if so, we do not queue it again.
- * @param cmd command to queue, will be freed once it is actually ran
- * @return 0 on queueing success, 1 on queueing skip
- */
-int queue_rcvr_command(char *cmd)
+static struct timeval min_timeval(struct timeval *tv1, struct timeval *tv2)
{
- struct cmdqueue *q = malloc(sizeof(struct cmdqueue));
- q->hash = hash_sdbm(cmd);
- q->cmd = cmd;
- q->next = NULL;
-
- if(serialdev_cmdqueue == NULL) {
- serialdev_cmdqueue = q;
- } else {
- struct cmdqueue *ptr = serialdev_cmdqueue;
- for(;;) {
- if(ptr->hash == q->hash) {
- /* command already in our queue, skip second copy */
- free(q);
- free(cmd);
- return(1);
- }
- if(!ptr->next)
- break;
- ptr = ptr->next;
- }
- ptr->next = q;
+ if(tv1->tv_sec < tv2->tv_sec) {
+ return(*tv1);
+ } else if(tv1->tv_sec > tv2->tv_sec) {
+ return(*tv2);
}
- return(0);
+ /* getting here means seconds are equal */
+ return(tv1->tv_usec < tv2->tv_usec ? *tv1 : *tv2);
}
/**
* Get the next receiver command that should be sent. This implementation has
* logic to discard non-power commands if the receiver is not powered up.
+ * @param rcvr the receiver to pull a command out of the queue for
* @return the command to send (must be freed), NULL if none available
*/
-static char *next_rcvr_command(void)
+static char *next_rcvr_command(struct receiver *rcvr)
{
/* Determine whether we should send the command. This depends on two
* factors:
* 1. If the power is on, always send the command.
* 2. If the power is off, send only power commands through.
*/
- while(serialdev_cmdqueue) {
+ while(rcvr->queue) {
/* dequeue the next cmd queue item */
- struct cmdqueue *ptr = serialdev_cmdqueue;
- serialdev_cmdqueue = serialdev_cmdqueue->next;
+ struct cmdqueue *ptr = rcvr->queue;
+ rcvr->queue = rcvr->queue->next;
- if(serialdev_power || is_power_command(ptr->cmd)) {
+ if(rcvr->power || is_power_command(ptr->cmd)) {
char *cmd = ptr->cmd;
free(ptr);
return cmd;
@@ -822,19 +804,11 @@ int main(int argc, char *argv[])
{
int retval, opt;
struct sigaction sa;
- struct timeval serialdev_last = { 0, 0 };
/* options storage */
int daemon = 0;
char *bind_addr = NULL, *socket_path = NULL;
char *log_path = NULL, *serialdev_path = NULL;
- serialdev_power = initial_power_status();
-
- serialdev = -1;
- logfd = -1;
- listeners = NULL;
- connections = NULL;
-
/* options parsing */
while((opt = getopt_long(argc, argv, "b::dhl:s:u:", opts, NULL))) {
if(opt < 0)
@@ -926,15 +900,12 @@ int main(int argc, char *argv[])
daemonize();
}
- /* queue up an initial power command */
- process_command("power");
-
/* Terminal settings are all done. Now it is time to watch for input
* on our socket and handle it as necessary. We also handle incoming
* status messages from the receiver.
*
* Attempt to keep the crazyness in order:
- * signalpipe, serialdev, listeners, connections
+ * signalpipe, receivers, listeners, connections
*/
for(;;) {
int maxfd = -1;
@@ -942,6 +913,7 @@ int main(int argc, char *argv[])
struct timeval timeoutval;
struct timeval *timeout = NULL;
+ struct receiver *r;
struct fdlist *l;
struct conn *c;
@@ -951,18 +923,30 @@ int main(int argc, char *argv[])
/* add our signal pipe file descriptor */
FD_SET(signalpipe[READ], &readfds);
maxfd = signalpipe[READ] > maxfd ? signalpipe[READ] : maxfd;
- /* add our serial device */
- if(serialdev > -1) {
- FD_SET(serialdev, &readfds);
- maxfd = serialdev > maxfd ? serialdev : maxfd;
- /* check for write possibility if we have commands in queue */
- if(serialdev_cmdqueue) {
- if(can_send_command(&serialdev_last, &timeoutval)) {
- FD_SET(serialdev, &writefds);
- } else {
- timeout = &timeoutval;
+ /* add our receiver list */
+ r = receivers;
+ while(r) {
+ if(r->fd > -1) {
+ FD_SET(r->fd, &readfds);
+ maxfd = r->fd > maxfd ? r->fd : maxfd;
+ /* check for write possibility if we have commands in queue */
+ if(r->queue) {
+ struct timeval tv;
+ if(can_send_command(&(r->last_cmd), &tv)) {
+ FD_SET(r->fd, &writefds);
+ } else {
+ if(!timeout) {
+ timeoutval = tv;
+ timeout = &timeoutval;
+ } else {
+ /* We want the smallest timeout, so replace the
+ * existing if new is smaller. */
+ timeoutval = min_timeval(timeout, &tv);
+ }
+ }
}
}
+ r = r->next;
}
/* add all of our listeners */
l = listeners;
@@ -999,37 +983,41 @@ int main(int argc, char *argv[])
xread(signalpipe[READ], &signo, sizeof(int));
realhandler(signo);
}
- /* check if we have a status message on our serial device */
- if(serialdev > -1 && FD_ISSET(serialdev, &readfds)) {
- size_t len;
- char *msg = process_incoming_message(serialdev, logfd);
- len = strlen(msg);
- /* print to stdout and all current open connections */
- printf("response: %s", msg);
- c = connections;
- while(c) {
- if(c->fd > -1) {
- ssize_t ret = xwrite(c->fd, msg, len);
- if(ret == -1)
- end_connection(c, 0);
+ r = receivers;
+ while(r) {
+ /* check if we have a status message from the receivers */
+ if(r->fd > -1 && FD_ISSET(r->fd, &readfds)) {
+ size_t len;
+ char *msg = process_incoming_message(r->fd, logfd);
+ len = strlen(msg);
+ /* print to stdout and all current open connections */
+ printf("response: %s", msg);
+ c = connections;
+ while(c) {
+ if(c->fd > -1) {
+ ssize_t ret = xwrite(c->fd, msg, len);
+ if(ret == -1)
+ end_connection(c, 0);
+ }
+ c = c->next;
}
- c = c->next;
+ /* check for power messages- update our power state variable */
+ r->power = update_power_status(r->power, msg);
+ free(msg);
}
- /* check for power messages- update our power state variable */
- serialdev_power = update_power_status(serialdev_power, msg);
- free(msg);
- }
- /* check if we have outgoing messages to send to receiver */
- if(serialdev > -1 && FD_ISSET(serialdev, &writefds)) {
- char *cmd = next_rcvr_command();
- if(cmd) {
- int ret = rcvr_send_command(serialdev, cmd);
- if(ret != 0) {
- printf("%s", rcvr_err);
+ /* check if we have outgoing messages to send to receiver */
+ if(r->fd > -1 && r->queue != NULL && FD_ISSET(r->fd, &writefds)) {
+ char *cmd = next_rcvr_command(r);
+ if(cmd) {
+ int ret = rcvr_send_command(r->fd, cmd);
+ if(ret != 0) {
+ printf("%s", rcvr_err);
+ }
+ /* set our last sent time */
+ gettimeofday(&(r->last_cmd), NULL);
}
- /* set our last sent time */
- gettimeofday(&serialdev_last, NULL);
}
+ r = r->next;
}
/* check to see if we have listeners ready to accept */
l = listeners;
diff --git a/onkyo.h b/onkyo.h
index 50a89f3..326b093 100644
--- a/onkyo.h
+++ b/onkyo.h
@@ -7,7 +7,9 @@
#ifndef ONKYO_H
#define ONKYO_H
+#include <sys/time.h> /* struct timeval */
#include <sys/types.h> /* ssize_t, size_t */
+#include <termios.h> /* struct termios */
/** The default port number to listen on (note: it is a string, not a num) */
#define LISTENPORT "8701"
@@ -45,8 +47,23 @@ enum power {
/** Keep track of two paired file descriptors */
enum pipehalfs { READ = 0, WRITE = 1 };
-/* onkyo.c - functions operating on our static vars */
-int queue_rcvr_command(char *cmd);
+/** Represents a command waiting to be sent to the receiver */
+struct cmdqueue {
+ unsigned long hash;
+ char *cmd;
+ struct cmdqueue *next;
+};
+
+/** Our Receiver device and associated dealings */
+struct receiver {
+ int fd;
+ int type;
+ enum power power;
+ struct timeval last_cmd;
+ struct termios serial_oldtio;
+ struct cmdqueue *queue;
+ struct receiver *next;
+};
/* receiver.c - receiver interaction functions, status processing */
void init_statuses(void);
@@ -59,7 +76,7 @@ enum power update_power_status(enum power pwr, const char *msg);
/* command.c - user command processing */
void init_commands(void);
void free_commands(void);
-int process_command(const char *str);
+int process_command(struct receiver *rcvr, const char *str);
int is_power_command(const char *cmd);
/* util.c - trivial utility functions */