diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
commit | 1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch) | |
tree | 4495d23e7b54ab5700e3839081e797c1eafe0db9 /cmd/ossmixd/ossmixd.c | |
download | oss4-upstream.tar.gz |
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'cmd/ossmixd/ossmixd.c')
-rw-r--r-- | cmd/ossmixd/ossmixd.c | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/cmd/ossmixd/ossmixd.c b/cmd/ossmixd/ossmixd.c new file mode 100644 index 0000000..2235c45 --- /dev/null +++ b/cmd/ossmixd/ossmixd.c @@ -0,0 +1,468 @@ +/* + * OSS mixer service daemon (used by libossmix) + */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#ifdef __SCO_VERSION__ +#include <sys/time.h> +#else +#include <time.h> +#endif +#include <sched.h> +#include <soundcard.h> + +#define OSSMIX_REMOTE +#include "libossmix.h" + +static int connfd; +static int listenfd; +static int verbose = 0; +static int polling_started = 0; +static int num_mixers=0; + +static unsigned char mixer_open_mask[MAX_TMP_MIXER/8] = {0}; + +static void +send_response (int cmd, int p1, int p2, int p3, int p4, int p5, int unsol) +{ + ossmix_commad_packet_t msg; + + memset (&msg, 0, sizeof (msg)); + + if (verbose) + printf ("Send %02d, p=0x%08x, %d, %d, %d, %d, unsol=%d\n", + cmd, p1, p2, p3, p4, p5, unsol); + + msg.cmd = cmd; + msg.unsolicited = unsol; + msg.p1 = p1; + msg.p2 = p2; + msg.p3 = p3; + msg.p4 = p4; + msg.p5 = p5; + + if (write (connfd, &msg, sizeof (msg)) != sizeof (msg)) + { + fprintf (stderr, "Write to socket failed\n"); + } +} + +static void +send_response_long (int cmd, int p1, int p2, int p3, int p4, int p5, + const char *payload, int plsize, int unsol) +{ + ossmix_commad_packet_t msg; + + if (verbose) + printf ("Send %02d, p=0x%08x, %d, %d, %d, %d, unsol=%d, pl=%d\n", + cmd, p1, p2, p3, p4, p5, unsol, plsize); + + memset (&msg, 0, sizeof (msg)); + + msg.cmd = cmd; + msg.unsolicited = unsol; + msg.p1 = p1; + msg.p2 = p2; + msg.p3 = p3; + msg.p4 = p4; + msg.p5 = p5; + msg.payload_size = plsize; + + if (write (connfd, &msg, sizeof (msg)) != sizeof (msg)) + { + fprintf (stderr, "Write to socket failed\n"); + } + + if (write (connfd, payload, msg.payload_size) != msg.payload_size) + { + fprintf (stderr, "Write to socket failed\n"); + } +} + +static void +send_error (const char *msg) +{ + int l = strlen (msg) + 1; + + send_response_long (OSSMIX_CMD_ERROR, 0, 0, 0, 0, 0, msg, l, 0); +} + +int +wait_connect (void) +{ + if (listen (listenfd, 1) == -1) + { + perror ("listen"); + exit (-1); + } + + if ((connfd = accept (listenfd, NULL, NULL)) == -1) + { + perror ("accept"); + exit (-1); + } + + return 1; +} + +static void +send_ack (void) +{ + send_response (OSSMIX_CMD_OK, 0, 0, 0, 0, 0, 0); +} + +static void +return_value (int val) +{ + send_response (val, 0, 0, 0, 0, 0, 0); +} + +static void +send_multiple_nodes (ossmix_commad_packet_t * pack) +{ + int i, n; + oss_mixext nodes[MAX_NODES]; + + n = 0; + for (i = pack->p2; i <= pack->p3; i++) + { + if (ossmix_get_nodeinfo (pack->p1, i, &nodes[n]) < 0) + { + send_error ("Cannot get mixer node info\n"); + return; + } + + mixc_add_node (pack->p1, i, &nodes[n]); + + if (++n >= MAX_NODES) + { + send_response_long (OSSMIX_CMD_GET_NODEINFO, n, i, 0, 0, 0, + (void *) &nodes, n * sizeof (oss_mixext), 0); + n = 0; + } + } + + if (n > 0) + send_response_long (OSSMIX_CMD_GET_NODEINFO, n, pack->p3, 0, 0, 0, + (void *) &nodes, n * sizeof (oss_mixext), 0); +} + +static void +update_values (int mixernum) +{ + oss_mixext *ext; + int i; + int nrext; + int value, prev_value; + + nrext = ossmix_get_nrext (mixernum); + + for (i = 0; i < nrext; i++) + { + if ((ext = mixc_get_node (mixernum, i)) == NULL) + continue; + + if (ext->type == MIXT_DEVROOT || ext->type == MIXT_GROUP + || ext->type == MIXT_MARKER) + continue; + + prev_value = mixc_get_value (mixernum, i); + + if ((value = ossmix_get_value (mixernum, i, ext->timestamp)) < 0) + continue; + // TODO check for EIDRM + + mixc_set_value (mixernum, i, value); + } +} + +static void +serve_command (ossmix_commad_packet_t * pack) +{ + switch (pack->cmd) + { + case OSSMIX_CMD_INIT: + polling_started = 0; + if (pack->ack_rq) + send_ack (); + break; + + case OSSMIX_CMD_EXIT: + //fprintf(stderr, "Exit\n"); + polling_started = 0; + memset(mixer_open_mask, 0, sizeof(mixer_open_mask)); + if (pack->ack_rq) + send_ack (); + break; + + case OSSMIX_CMD_START_EVENTS: + polling_started = 1; + break; + + case OSSMIX_CMD_GET_NMIXERS: + return_value (num_mixers=ossmix_get_nmixers ()); + break; + + case OSSMIX_CMD_GET_MIXERINFO: + { + oss_mixerinfo mi; + + if (ossmix_get_mixerinfo (pack->p1, &mi) < 0) + send_error ("Cannot get mixer info\n"); + else + send_response_long (OSSMIX_CMD_OK, 0, 0, 0, 0, 0, (void *) &mi, + sizeof (mi), 0); + } + break; + + case OSSMIX_CMD_OPEN_MIXER: + mixer_open_mask[pack->p1 / 8] |= (1<<(pack->p1 % 8)); // Open + return_value (ossmix_open_mixer (pack->p1)); + break; + + case OSSMIX_CMD_CLOSE_MIXER: + mixer_open_mask[pack->p1 / 8] &= ~(1<<(pack->p1 % 8)); // Closed + ossmix_close_mixer (pack->p1); + break; + + case OSSMIX_CMD_GET_NREXT: + return_value (ossmix_get_nrext (pack->p1)); + break; + + case OSSMIX_CMD_GET_NODEINFO: + { + oss_mixext ext; + + if (pack->p3 > pack->p2) + { + send_multiple_nodes (pack); + break; + } + + if (ossmix_get_nodeinfo (pack->p1, pack->p2, &ext) < 0) + send_error ("Cannot get mixer node info\n"); + else + { + mixc_add_node (pack->p1, pack->p2, &ext); + send_response_long (OSSMIX_CMD_OK, 0, 0, 0, 0, 0, (void *) &ext, + sizeof (ext), 0); + } + } + break; + + case OSSMIX_CMD_GET_ENUMINFO: + { + oss_mixer_enuminfo desc; + + if (ossmix_get_enuminfo (pack->p1, pack->p2, &desc) < 0) + send_error ("Cannot get mixer enum strings\n"); + else + send_response_long (OSSMIX_CMD_OK, 0, 0, 0, 0, 0, (void *) &desc, + sizeof (desc), 0); + } + break; + + case OSSMIX_CMD_GET_DESCRIPTION: + { + oss_mixer_enuminfo desc; + + if (ossmix_get_description (pack->p1, pack->p2, &desc) < 0) + send_error ("Cannot get mixer description\n"); + else + send_response_long (OSSMIX_CMD_OK, 0, 0, 0, 0, 0, (void *) &desc, + sizeof (desc), 0); + } + break; + + case OSSMIX_CMD_GET_VALUE: + return_value (ossmix_get_value (pack->p1, pack->p2, pack->p3)); + break; + + case OSSMIX_CMD_GET_ALL_VALUES: + { + int n; + value_packet_t value_packet; + + update_values (pack->p1); + n = mixc_get_all_values (pack->p1, value_packet, 0); + + send_response_long (OSSMIX_CMD_GET_ALL_VALUES, n, pack->p1, 0, 0, 0, + (void *) &value_packet, + n * sizeof (value_record_t), 0); + mixc_clear_changeflags (pack->p1); + } + break; + + case OSSMIX_CMD_SET_VALUE: + ossmix_set_value (pack->p1, pack->p2, pack->p3, pack->p4); + break; + + default: + + if (pack->ack_rq) + send_error ("Unrecognized request"); + } +} + +static void +poll_devices (void) +{ + int n; + int mixernum; + value_packet_t value_packet; + + for (mixernum=0;mixernum<num_mixers;mixernum++) + if (mixer_open_mask[mixernum / 8] & 1<<(mixernum % 8)) + { + update_values (mixernum); + n = mixc_get_all_values (mixernum, value_packet, 1); + + if (n==0) /* Nothing changed */ + continue; + + send_response_long (OSSMIX_EVENT_VALUE, n, mixernum, 0, 0, 0, + (void *) &value_packet, + n * sizeof (value_record_t), 1); + mixc_clear_changeflags (mixernum); + } + + n=ossmix_get_nmixers(); + if (n>num_mixers) + { + num_mixers=n; + send_response (OSSMIX_EVENT_NEWMIXER, n, 0, 0, 0, 0, 1); + } +} + +static void +handle_connection (int connfd) +{ + ossmix_commad_packet_t pack; + struct timeval tmout; + + send_response (OSSMIX_CMD_HALOO, OSSMIX_P1_MAGIC, 0, 0, 0, 0, 0); + + tmout.tv_sec = 1; + tmout.tv_usec = 0; + + while (1) + { + int ndevs; + fd_set readfds, exfds; + FD_ZERO (&readfds); + FD_ZERO (&exfds); + + FD_SET (connfd, &readfds); + FD_SET (connfd, &exfds); + + if ((ndevs = select (connfd + 1, &readfds, NULL, &exfds, &tmout)) == -1) + { + perror ("select"); + exit (-1); + } + + if (ndevs == 0) + { + if (polling_started) + { + poll_devices (); + tmout.tv_sec = 0; + tmout.tv_usec = 100000; + } + else + { + tmout.tv_sec = 1; + tmout.tv_usec = 0; + } + } + + if (FD_ISSET (connfd, &readfds) || FD_ISSET (connfd, &exfds)) + { + if (read (connfd, &pack, sizeof (pack)) == sizeof (pack)) + { + serve_command (&pack); + } + else + return; + } + } + + +} + +int +main (int argc, char *argv[]) +{ + struct sockaddr_in servaddr; + int port = 7777; + int c; + int err; + + if ((err = ossmix_init ()) < 0) + { + fprintf (stderr, "ossmix_init() failed, err=%d\n", err); + exit (EXIT_FAILURE); + } + + if ((err = ossmix_connect (NULL, 0)) < 0) /* Force local connection */ + { + fprintf (stderr, "ossmix_connect() failed, err=%d\n", err); + exit (EXIT_FAILURE); + } + + while ((c = getopt (argc, argv, "vp:")) != EOF) + { + switch (c) + { + case 'p': /* TCP/IP port */ + port = atoi (optarg); + if (port <= 0) + port = 9876; + break; + + case 'v': /* Verbose */ + verbose++; + break; + } + } + + printf ("Listening socket %d\n", port); + + if ((listenfd = socket (AF_INET, SOCK_STREAM, 0)) == -1) + { + perror ("socket"); + exit (-1); + } + + memset (&servaddr, 0, sizeof (servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = htonl (INADDR_ANY); + servaddr.sin_port = htons (port); + + if (bind (listenfd, (struct sockaddr *) &servaddr, sizeof (servaddr)) == -1) + { + perror ("bind"); + exit (-1); + } + + while (1) + { + + if (!wait_connect ()) + exit (-1); + + handle_connection (connfd); + close (connfd); + } + + // close (listenfd); /* Not reached */ + exit (0); +} |