diff options
author | Dan McGee <dpmcgee@gmail.com> | 2010-10-23 01:43:55 -0500 |
---|---|---|
committer | Dan McGee <dpmcgee@gmail.com> | 2011-01-21 16:04:09 -0600 |
commit | 466e4e6986160ee591ee0e2601b1fbacfd15e064 (patch) | |
tree | bed7735afaef254a7ff1215b5bf7569f45be0abb | |
parent | 44f47962fc93a949bf3b478d7c012d03041d2ec8 (diff) | |
download | onkyocontrol-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.c | 201 | ||||
-rw-r--r-- | onkyo.c | 298 | ||||
-rw-r--r-- | onkyo.h | 23 |
3 files changed, 293 insertions, 229 deletions
@@ -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); } @@ -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; @@ -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 */ |