summaryrefslogtreecommitdiff
path: root/lib/libossmix
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libossmix')
-rw-r--r--lib/libossmix/.config3
-rw-r--r--lib/libossmix/libossmix_cache.c210
-rw-r--r--lib/libossmix/libossmix_impl.h36
-rw-r--r--lib/libossmix/libossmix_local.c332
-rw-r--r--lib/libossmix/libossmix_main.c238
-rw-r--r--lib/libossmix/libossmix_tcp.c813
6 files changed, 1632 insertions, 0 deletions
diff --git a/lib/libossmix/.config b/lib/libossmix/.config
new file mode 100644
index 0000000..3d02eca
--- /dev/null
+++ b/lib/libossmix/.config
@@ -0,0 +1,3 @@
+mode=shlib
+forgetos=BeOS
+forgetos=VxWorks
diff --git a/lib/libossmix/libossmix_cache.c b/lib/libossmix/libossmix_cache.c
new file mode 100644
index 0000000..a15fbf4
--- /dev/null
+++ b/lib/libossmix/libossmix_cache.c
@@ -0,0 +1,210 @@
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+/*
+ * Settings cache for libossmix
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <soundcard.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netdb.h>
+
+#define OSSMIX_REMOTE
+
+#include "libossmix.h"
+#include "libossmix_impl.h"
+
+static local_mixer_t *mixers[MAX_TMP_MIXER] = { NULL };
+
+void
+mixc_add_node (int mixernum, int node, oss_mixext * ext)
+{
+ local_mixer_t *lmixer;
+ oss_mixext *lnode;
+
+ if (mixers[mixernum] == NULL)
+ {
+ int i;
+
+ mixers[mixernum] = lmixer = malloc (sizeof (*lmixer));
+ if (lmixer == NULL)
+ {
+ fprintf (stderr, "mixc_add_node: Out of memory\n");
+ exit (EXIT_FAILURE);
+ }
+
+ memset (lmixer, 0, sizeof (*lmixer));
+ for (i = 0; i < MAX_TMP_NODES; i++)
+ lmixer->values[i] = -1; // Invalid
+ }
+ else
+ lmixer = mixers[mixernum];
+
+ if (ext->ctrl >= lmixer->nrext)
+ lmixer->nrext = ext->ctrl + 1;
+
+ if (node >= MAX_TMP_NODES)
+ {
+ fprintf (stderr, "mixc_add_node: Node number too large %d\n", node);
+ exit (EXIT_FAILURE);
+ }
+
+ lnode = lmixer->nodes[node];
+
+ if (lnode == NULL)
+ {
+ lmixer->nodes[node] = lnode = malloc (sizeof (*lnode));
+
+ if (lnode == NULL)
+ {
+ fprintf (stderr, "mixc_get_node: Out of memory\n");
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ memcpy (lnode, ext, sizeof (*ext));
+
+}
+
+oss_mixext *
+mixc_get_node (int mixernum, int node)
+{
+ local_mixer_t *lmixer;
+ oss_mixext *lnode;
+
+ if (mixers[mixernum] == NULL)
+ {
+ return NULL;
+ }
+ lmixer = mixers[mixernum];
+
+ if (node >= MAX_TMP_NODES)
+ {
+ fprintf (stderr, "mixc_get_node: Node number too large %d\n", node);
+ exit (EXIT_FAILURE);
+ }
+
+ lnode = lmixer->nodes[node];
+
+ return lnode;
+}
+
+void
+mixc_clear_changeflags(int mixernum)
+{
+ local_mixer_t *lmixer;
+
+ if (mixers[mixernum] == NULL)
+ {
+ return;
+ }
+ lmixer = mixers[mixernum];
+
+ memset(lmixer->changemask, 0, sizeof(lmixer->changemask));
+}
+
+void
+mixc_set_value (int mixernum, int node, int value)
+{
+ local_mixer_t *lmixer;
+
+ if (mixers[mixernum] == NULL)
+ {
+ return;
+ }
+ lmixer = mixers[mixernum];
+
+ if (node >= MAX_TMP_NODES)
+ {
+ fprintf (stderr, "mixc_set_value: Node number too large %d\n", node);
+ exit (EXIT_FAILURE);
+ }
+
+ if (lmixer->values[node] != value)
+ lmixer->changemask[node / 8] |= (1 << (node % 8));
+
+ lmixer->values[node] = value;
+}
+
+int
+mixc_get_value (int mixernum, int node)
+{
+ local_mixer_t *lmixer;
+
+ if (mixers[mixernum] == NULL)
+ {
+ return -1;
+ }
+ lmixer = mixers[mixernum];
+
+ if (node >= MAX_TMP_NODES)
+ {
+ fprintf (stderr, "mixc_get_value: Node number too large %d\n", node);
+ exit (EXIT_FAILURE);
+ }
+ lmixer->changemask[node / 8] &= ~(1 << (node % 8));
+
+ return lmixer->values[node];
+}
+
+int
+mixc_get_all_values (int mixernum, value_packet_t value_packet, int changecheck)
+{
+ int i, n = 0;
+ oss_mixext *lnode;
+ local_mixer_t *lmixer;
+
+ if (mixers[mixernum] == NULL)
+ {
+ fprintf (stderr, "mixc_get_all_values: Mixer %d doesn't exist\n",
+ mixernum);
+ return 0;
+ }
+ lmixer = mixers[mixernum];
+
+ for (i = 0; i < lmixer->nrext; i++)
+ {
+ lnode = lmixer->nodes[i];
+
+ if (lnode == NULL)
+ {
+ fprintf (stderr, "mixc_get_all_values: Mixer %d, node %d == NULL\n",
+ mixernum, i);
+ continue;
+ }
+
+ if (changecheck) // Not changed since the last time
+ if (!(lmixer->changemask[i / 8] & (1 << (i % 8)) ))
+ continue;
+
+ if (lnode->type != MIXT_DEVROOT && lnode->type != MIXT_GROUP
+ && lnode->type != MIXT_MARKER)
+ {
+ value_packet[n].node = i;
+ value_packet[n].value = lmixer->values[i];
+
+ lmixer->changemask[i / 8] &= ~(1 << (i % 8));
+
+//printf("Send %d = %08x\n", i, lmixer->values[i]);
+ n++;
+ }
+ }
+
+ return n;
+}
diff --git a/lib/libossmix/libossmix_impl.h b/lib/libossmix/libossmix_impl.h
new file mode 100644
index 0000000..3848166
--- /dev/null
+++ b/lib/libossmix/libossmix_impl.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+extern int mixlib_trace;
+
+typedef struct
+{
+ int (*connect) (const char *hostname, int port);
+ int (*get_fd) (ossmix_select_poll_t * cb);
+ void (*disconnect) (void);
+ void (*enable_events) (void);
+ int (*get_nmixers) (void);
+ int (*get_mixerinfo) (int mixernum, oss_mixerinfo * mi);
+ int (*open_mixer) (int mixernum);
+ void (*close_mixer) (int mixernum);
+ int (*get_nrext) (int mixernum);
+ int (*get_nodeinfo) (int mixernum, int node, oss_mixext * ext);
+ int (*get_enuminfo) (int mixernum, int node, oss_mixer_enuminfo * ei);
+ int (*get_description) (int mixernum, int node, oss_mixer_enuminfo * desc);
+ int (*get_value) (int mixernum, int ctl, int timestamp);
+ void (*set_value) (int mixernum, int ctl, int timestamp, int value);
+ void (*timertick)(void);
+} ossmix_driver_t;
+
+extern ossmix_driver_t ossmix_local_driver, ossmix_tcp_driver;
+extern void _client_event (int cmd, int p1, int p2, int p3, int p4, int p5);
+extern int _ossmix_refresh_mixer(int mixernum, int prev_nmixers);
diff --git a/lib/libossmix/libossmix_local.c b/lib/libossmix/libossmix_local.c
new file mode 100644
index 0000000..22d39f7
--- /dev/null
+++ b/lib/libossmix/libossmix_local.c
@@ -0,0 +1,332 @@
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+/*
+ * Local driver for libossmix
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include <soundcard.h>
+
+#define OSSMIX_REMOTE
+
+#include "libossmix.h"
+#include "libossmix_impl.h"
+
+static int global_fd = -1;
+static int num_mixers=0;
+
+static int mixer_fd[MAX_TMP_MIXER];
+
+static int
+local_connect (const char *hostname, int port)
+{
+ char *devmixer;
+ int i;
+
+ if (mixlib_trace > 0)
+ fprintf (stderr, "Entered local_connect()\n");
+
+ for (i = 0; i < MAX_TMP_MIXER; i++)
+ mixer_fd[i] = -1;
+
+ if ((devmixer = getenv ("OSS_MIXERDEV")) == NULL)
+ devmixer = "/dev/mixer";
+
+/*
+ * Open the mixer device
+ */
+ if ((global_fd = open (devmixer, O_RDWR, 0)) == -1)
+ {
+ perror (devmixer);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+local_disconnect (void)
+{
+ if (mixlib_trace > 0)
+ fprintf (stderr, "Entered local_disconnect()\n");
+
+ if (global_fd >= 0)
+ close (global_fd);
+
+ global_fd = -1;
+}
+
+static void
+local_enable_events (void)
+{
+}
+
+static int
+local_get_fd (ossmix_select_poll_t * cb)
+{
+ *cb = NULL;
+ return -1; /* No poll handling required */
+}
+
+static int
+local_get_nmixers (void)
+{
+ oss_sysinfo si;
+
+ if (ioctl (global_fd, SNDCTL_SYSINFO, &si) == -1)
+ {
+ perror ("SNDCTL_SYSINFO");
+ return -1;
+ }
+
+ return num_mixers = si.nummixers;
+}
+
+static int
+local_get_mixerinfo (int mixernum, oss_mixerinfo * mi)
+{
+ mi->dev = mixernum;
+
+ if (ioctl (global_fd, SNDCTL_MIXERINFO, mi) == -1)
+ {
+ perror ("SNDCTL_MIXERINFO");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+local_open_mixer (int mixernum)
+{
+ oss_mixerinfo mi;
+
+ if (mixer_fd[mixernum] > -1)
+ return 0;
+
+ if (ossmix_get_mixerinfo (mixernum, &mi) < 0)
+ return -1;
+
+//fprintf(stderr, "local_open_mixer(%d: %s)\n", mixernum, mi.devnode);
+
+ if ((mixer_fd[mixernum] = open (mi.devnode, O_RDWR, 0)) == -1)
+ {
+ perror (mi.devnode);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+local_close_mixer (int mixernum)
+{
+//fprintf(stderr, "local_close_mixer(%d)\n", mixernum);
+
+ if (mixer_fd[mixernum] == -1)
+ return;
+
+ close (mixer_fd[mixernum]);
+ mixer_fd[mixernum] = -1;
+}
+
+static int
+local_get_nrext (int mixernum)
+{
+ int n = -1;
+
+ if (ioctl (mixer_fd[mixernum], SNDCTL_MIX_NREXT, &n) == -1)
+ {
+ perror ("SNDCTL_MIX_NREXT");
+ return -1;
+ }
+
+ return n;
+}
+
+static int
+local_get_nodeinfo (int mixernum, int node, oss_mixext * ext)
+{
+ ext->dev = mixernum;
+ ext->ctrl = node;
+
+ if (ioctl (mixer_fd[mixernum], SNDCTL_MIX_EXTINFO, ext) == -1)
+ {
+ perror ("SNDCTL_MIX_EXTINFO");
+ return -1;
+ }
+
+ mixc_add_node (mixernum, node, ext);
+ return 0;
+}
+
+static int
+local_get_enuminfo (int mixernum, int node, oss_mixer_enuminfo * ei)
+{
+ ei->dev = mixernum;
+ ei->ctrl = node;
+
+ if (ioctl (mixer_fd[mixernum], SNDCTL_MIX_ENUMINFO, ei) == -1)
+ {
+ perror ("SNDCTL_MIX_ENUMINFO");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+local_get_description (int mixernum, int node, oss_mixer_enuminfo * desc)
+{
+ desc->dev = mixernum;
+ desc->ctrl = node;
+
+ if (ioctl (mixer_fd[mixernum], SNDCTL_MIX_DESCRIPTION, desc) == -1)
+ {
+ perror ("SNDCTL_MIX_DESCRIPTION");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+local_get_value (int mixernum, int ctl, int timestamp)
+{
+ oss_mixer_value val;
+
+ val.dev = mixernum;
+ val.ctrl = ctl;
+ val.timestamp = timestamp;
+
+ if (ioctl (mixer_fd[mixernum], SNDCTL_MIX_READ, &val) == -1)
+ {
+ perror ("SNDCTL_MIX_READ");
+ return -1;
+ }
+
+ mixc_set_value (mixernum, ctl, val.value);
+ return val.value;
+}
+
+static int
+private_get_value (int mixernum, int ctl, int timestamp)
+{
+ oss_mixer_value val;
+
+ val.dev = mixernum;
+ val.ctrl = ctl;
+ val.timestamp = timestamp;
+
+ if (ioctl (mixer_fd[mixernum], SNDCTL_MIX_READ, &val) == -1)
+ {
+ perror ("SNDCTL_MIX_READ");
+ return -1;
+ }
+
+ return val.value;
+}
+
+static void
+local_set_value (int mixernum, int ctl, int timestamp, int value)
+{
+ oss_mixer_value val;
+
+ val.dev = mixernum;
+ val.ctrl = ctl;
+ val.timestamp = timestamp;
+ val.value = value;
+
+ if (ioctl (mixer_fd[mixernum], SNDCTL_MIX_WRITE, &val) == -1)
+ {
+ perror ("SNDCTL_MIX_WRITE");
+ }
+ mixc_set_value (mixernum, ctl, val.value);
+}
+
+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 = private_get_value (mixernum, i, ext->timestamp)) < 0)
+ continue;
+ // TODO check for EIDRM
+
+ if (value != prev_value)
+ {
+ mixc_set_value (mixernum, i, value);
+ _client_event (OSSMIX_EVENT_VALUE, mixernum, i, value, 0, 0);
+ }
+
+ }
+}
+
+static void
+local_timertick(void)
+{
+ int mixernum, n;
+
+ for (mixernum=0;mixernum<num_mixers;mixernum++)
+ if (mixer_fd[mixernum] >= 0) /* Open */
+ {
+ update_values (mixernum);
+ }
+
+ n=ossmix_get_nmixers();
+ if (n>num_mixers)
+ {
+ num_mixers=n;
+ _client_event (OSSMIX_EVENT_NEWMIXER, n, 0, 0, 0, 0);
+ }
+}
+
+ossmix_driver_t ossmix_local_driver = {
+ local_connect,
+ local_get_fd,
+ local_disconnect,
+ local_enable_events,
+ local_get_nmixers,
+ local_get_mixerinfo,
+ local_open_mixer,
+ local_close_mixer,
+ local_get_nrext,
+ local_get_nodeinfo,
+ local_get_enuminfo,
+ local_get_description,
+ local_get_value,
+ local_set_value,
+ local_timertick
+};
diff --git a/lib/libossmix/libossmix_main.c b/lib/libossmix/libossmix_main.c
new file mode 100644
index 0000000..a2b3597
--- /dev/null
+++ b/lib/libossmix/libossmix_main.c
@@ -0,0 +1,238 @@
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+/*
+ * Main module for libossmix
+ */
+#include <stdio.h>
+#include <soundcard.h>
+
+#include "libossmix.h"
+#include "libossmix_impl.h"
+
+static ossmix_driver_t *mixer_driver = NULL;
+int mixlib_trace = 0;
+static int num_mixers = 0;
+
+ossmix_callback_t event_callback = NULL;
+
+int
+ossmix_init (void)
+{
+ if (mixlib_trace > 0)
+ fprintf (stderr, "ossmix_init() called\n");
+ return 0;
+}
+
+void
+ossmix_close (void)
+{
+ if (mixlib_trace > 0)
+ fprintf (stderr, "ossmix_close() called\n");
+}
+
+void
+_client_event (int event, int p1, int p2, int p3, int p4, int p5)
+{
+ /*
+ * To be called only by the internals of ossmixlib
+ */
+
+ if (event_callback != NULL)
+ {
+ ossmix_callback_parm_t parm;
+
+ parm.event = event;
+ parm.p1 = p1;
+ parm.p2 = p2;
+ parm.p3 = p3;
+ parm.p4 = p4;
+ parm.p5 = p5;
+ event_callback (&parm);
+ }
+}
+
+int
+ossmix_connect (const char *hostname, int port)
+{
+ if (mixlib_trace > 0)
+ fprintf (stderr, "ossmix_connect(%s, %d)) called\n", hostname, port);
+
+ if (hostname == NULL)
+ mixer_driver = &ossmix_local_driver;
+ else
+ mixer_driver = &ossmix_tcp_driver;
+
+ event_callback = NULL;
+
+ return mixer_driver->connect (hostname, port);
+}
+
+int
+ossmix_get_fd (ossmix_select_poll_t * cb)
+{
+ return mixer_driver->get_fd (cb);
+}
+
+void
+ossmix_set_callback (ossmix_callback_t cb)
+{
+ event_callback = cb;
+ mixer_driver->enable_events ();
+}
+
+void
+ossmix_disconnect (void)
+{
+ if (mixlib_trace > 0)
+ fprintf (stderr, "ossmix_disconnect() called\n");
+
+ event_callback = NULL;
+
+ mixer_driver->disconnect ();
+}
+
+int
+ossmix_get_nmixers (void)
+{
+ if (mixlib_trace > 0)
+ fprintf (stderr, "ossmix_get_nmixes() called\n");
+ return (num_mixers = mixer_driver->get_nmixers ());
+}
+
+int
+ossmix_get_mixerinfo (int mixernum, oss_mixerinfo * mi)
+{
+ if (mixernum >= num_mixers)
+ {
+ fprintf (stderr, "ossmix_get_mixerinfo: Bad mixer number (%d >= %d)\n",
+ mixernum, num_mixers);
+ return -1;
+ }
+
+ return mixer_driver->get_mixerinfo (mixernum, mi);
+}
+
+int
+ossmix_open_mixer (int mixernum)
+{
+ if (mixernum >= num_mixers)
+ {
+ fprintf (stderr, "ossmix_open_mixer: Bad mixer number (%d >= %d)\n",
+ mixernum, num_mixers);
+ return -1;
+ }
+ return mixer_driver->open_mixer (mixernum);
+}
+
+void
+ossmix_close_mixer (int mixernum)
+{
+ if (mixernum >= num_mixers)
+ {
+ fprintf (stderr, "ossmix_close_mixer: Bad mixer number (%d >= %d)\n",
+ mixernum, num_mixers);
+ return;
+ }
+
+ mixer_driver->close_mixer (mixernum);
+}
+
+int
+ossmix_get_nrext (int mixernum)
+{
+ if (mixernum >= num_mixers)
+ {
+ fprintf (stderr, "ossmix_get_nrext: Bad mixer number (%d >= %d)\n",
+ mixernum, num_mixers);
+ return -1;
+ }
+ return mixer_driver->get_nrext (mixernum);
+}
+
+int
+ossmix_get_nodeinfo (int mixernum, int node, oss_mixext * ext)
+{
+ if (mixernum >= num_mixers)
+ {
+ fprintf (stderr, "ossmix_get_nodeinfo: Bad mixer number (%d >= %d)\n",
+ mixernum, num_mixers);
+ return -1;
+ }
+ return mixer_driver->get_nodeinfo (mixernum, node, ext);
+}
+
+int
+ossmix_get_enuminfo (int mixernum, int node, oss_mixer_enuminfo * ei)
+{
+ if (mixernum >= num_mixers)
+ {
+ fprintf (stderr, "ossmix_get_enuminfo: Bad mixer number (%d >= %d)\n",
+ mixernum, num_mixers);
+ return -1;
+ }
+ return mixer_driver->get_enuminfo (mixernum, node, ei);
+}
+
+int
+ossmix_get_description (int mixernum, int node, oss_mixer_enuminfo * desc)
+{
+ if (mixernum >= num_mixers)
+ {
+ fprintf (stderr,
+ "ossmix_get_description: Bad mixer number (%d >= %d)\n",
+ mixernum, num_mixers);
+ return -1;
+ }
+ return mixer_driver->get_description (mixernum, node, desc);
+}
+
+int
+ossmix_get_value (int mixernum, int node, int timestamp)
+{
+ if (mixernum >= num_mixers)
+ {
+ fprintf (stderr, "ossmix_get_value: Bad mixer number (%d >= %d)\n",
+ mixernum, num_mixers);
+ return -1;
+ }
+ return mixer_driver->get_value (mixernum, node, timestamp);
+}
+
+void
+ossmix_set_value (int mixernum, int node, int timestamp, int value)
+{
+ if (mixernum >= num_mixers)
+ {
+ fprintf (stderr, "ossmix_set_value: Bad mixer number (%d >= %d)\n",
+ mixernum, num_mixers);
+ return;
+ }
+
+ mixer_driver->set_value (mixernum, node, timestamp, value);
+}
+
+void
+ossmix_timertick(void)
+{
+ mixer_driver->timertick();
+}
+
+/*
+ * Internal use functions (not to be used by applications)
+ */
+int
+_ossmix_refresh_mixer(int mixernum, int prev_nmixers)
+{
+printf("_ossmix_refresh_mixer(%d, %d) called\n", mixernum, prev_nmixers);
+
+ return prev_nmixers; // TODO
+}
diff --git a/lib/libossmix/libossmix_tcp.c b/lib/libossmix/libossmix_tcp.c
new file mode 100644
index 0000000..a5da6f6
--- /dev/null
+++ b/lib/libossmix/libossmix_tcp.c
@@ -0,0 +1,813 @@
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+/*
+ * TCP driver for libossmix
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <soundcard.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netdb.h>
+
+#define OSSMIX_REMOTE
+
+#include "libossmix.h"
+#include "libossmix_impl.h"
+
+static int initialized = 0;
+static void tcp_disconnect (void);
+static int sockfd = -1;
+static int do_byteswap = 0;
+
+static void poll_callback (void);
+static void handle_packet (ossmix_commad_packet_t * msg, char *payload,
+ int payload_size);
+
+static int
+read_all (int sock, void *b, int count)
+{
+ unsigned char *buf = b;
+ int l = 0;
+
+ while (l < count)
+ {
+ int c, n = count - l;
+
+ if ((c = read (sock, buf + l, n)) <= 0)
+ return c;
+
+ l += c;
+ }
+
+ return l;
+}
+
+static inline int
+bswap32 (int x)
+{
+
+ int y = 0;
+ unsigned char *a = ((unsigned char *) &x) + 3;
+ unsigned char *b = (unsigned char *) &y;
+
+ *b++ = *a--;
+ *b++ = *a--;
+ *b++ = *a--;
+ *b++ = *a--;
+
+ return y;
+}
+
+#define BSWAP32(x) x=bswap32(x)
+
+static void
+byteswap_msg (ossmix_commad_packet_t * msg)
+{
+ BSWAP32 (msg->cmd);
+ BSWAP32 (msg->p1);
+ BSWAP32 (msg->p2);
+ BSWAP32 (msg->p3);
+ BSWAP32 (msg->p4);
+ BSWAP32 (msg->p5);
+ BSWAP32 (msg->ack_rq);
+ BSWAP32 (msg->unsolicited);
+ BSWAP32 (msg->payload_size);
+}
+
+typedef void (*bswap_func_t) (void *data, int len);
+
+static void
+bswap_int_array (void *data, int len)
+{
+ int *arr = (int *) data;
+ int i;
+
+ if (len % sizeof (int) != 0)
+ {
+ fprintf (stderr, "bswap_int_array: Bad size %d\n", len);
+ exit (EXIT_FAILURE);
+ }
+
+ for (i = 0; i < len / sizeof (int); i++)
+ BSWAP32 (arr[i]);
+}
+
+static void
+bswap_mixerinfo (void *data, int len)
+{
+ oss_mixerinfo *mi = (oss_mixerinfo *) data;
+
+ if (len != sizeof (*mi))
+ {
+ fprintf (stderr, "bswap_mixerinfo: Bad size (%d/%lu)\n", len,
+ (unsigned long)sizeof (*mi));
+ exit (EXIT_FAILURE);
+ }
+
+ BSWAP32 (mi->dev);
+ BSWAP32 (mi->modify_counter);
+ BSWAP32 (mi->card_number);
+ BSWAP32 (mi->port_number);
+ BSWAP32 (mi->magic);
+ BSWAP32 (mi->enabled);
+ BSWAP32 (mi->caps);
+ BSWAP32 (mi->flags);
+ BSWAP32 (mi->nrext);
+ BSWAP32 (mi->priority);
+ BSWAP32 (mi->legacy_device);
+}
+
+static void
+bswap_nodeinfo (void *data, int len)
+{
+ oss_mixext *ei = (oss_mixext *) data;
+
+ if (len != sizeof (*ei))
+ {
+ fprintf (stderr, "bswap_nodeinfo: Bad size (%d/%lu)\n", len,
+ (unsigned long)sizeof (*ei));
+ exit (EXIT_FAILURE);
+ }
+
+ BSWAP32 (ei->dev);
+ BSWAP32 (ei->ctrl);
+ BSWAP32 (ei->type);
+ BSWAP32 (ei->maxvalue);
+ BSWAP32 (ei->minvalue);
+ BSWAP32 (ei->flags);
+ BSWAP32 (ei->parent);
+ BSWAP32 (ei->dummy);
+ BSWAP32 (ei->timestamp);
+ BSWAP32 (ei->control_no);
+ BSWAP32 (ei->desc);
+ BSWAP32 (ei->update_counter);
+ BSWAP32 (ei->rgbcolor);
+}
+
+static void
+bswap_nodeinfo_array (void *data, int len)
+{
+ oss_mixext *ei = (oss_mixext *) data;
+ int i, n;
+
+ n = len / sizeof (oss_mixext);
+
+ if (len != n * sizeof (oss_mixext))
+ {
+ fprintf (stderr, "bswap_enuminfo_array: Bad size (%d/%d*%lu)\n", len, n,
+ (unsigned long)sizeof (*ei));
+ exit (EXIT_FAILURE);
+ }
+
+ for (i = 0; i < n; i++)
+ bswap_nodeinfo ((void *) &ei[i], sizeof (oss_mixext));
+
+}
+
+
+static void
+bswap_enuminfo (void *data, int len)
+{
+ oss_mixer_enuminfo *ei = (oss_mixer_enuminfo *) data;
+
+ int i;
+
+ if (len != sizeof (*ei))
+ {
+ fprintf (stderr, "bswap_enuminfo: Bad size (%d/%lu)\n", len,
+ (unsigned long)sizeof (*ei));
+ exit (EXIT_FAILURE);
+ }
+
+ BSWAP32 (ei->dev);
+ BSWAP32 (ei->ctrl);
+ BSWAP32 (ei->nvalues);
+ BSWAP32 (ei->version);
+
+ for (i = 0; i < OSS_ENUM_MAXVALUE; i++)
+ BSWAP32 (ei->strindex[i]);
+}
+
+static int
+get_response (void)
+{
+ ossmix_commad_packet_t msg;
+ char payload[4096];
+ int l;
+
+ if (sockfd == -1)
+ return -1;
+
+ while (1)
+ {
+ payload[0] = 0;
+
+ if ((l = read_all (sockfd, &msg, sizeof (msg))) != sizeof (msg))
+ {
+ if (l == 0) /* Connection closed */
+ return -1;
+
+ perror ("get response");
+ return -1;
+ }
+
+ if (do_byteswap)
+ byteswap_msg (&msg);
+
+ if (msg.payload_size > 0)
+ {
+ if ((l =
+ read_all (sockfd, payload,
+ msg.payload_size)) != msg.payload_size)
+ {
+ perror ("Get response payload");
+ fprintf (stderr, "Payload size %d/%d\n", l, msg.payload_size);
+ return -1;
+ }
+
+ payload[l] = 0;
+ }
+
+ if (msg.cmd == OSSMIX_CMD_ERROR)
+ {
+ fprintf (stderr, "Remote error: %s\n", payload);
+ }
+
+ /*
+ * Return if this was not an async notification message sent by the
+ * server.
+ */
+ if (!msg.unsolicited)
+ return msg.cmd;
+
+ handle_packet (&msg, payload, msg.payload_size);
+ }
+}
+
+static int
+check_welcome (void)
+{
+ ossmix_commad_packet_t msg;
+ int l;
+
+ if (sockfd == -1)
+ return 0;
+
+ if ((l = read_all (sockfd, &msg, sizeof (msg))) != sizeof (msg))
+ {
+ if (l == 0) /* Connection closed */
+ return 0;
+
+ perror ("get response");
+ return 0;
+ }
+
+ if (msg.cmd != OSSMIX_CMD_HALOO)
+ {
+ fprintf (stderr, "Bad welcome from the remote server\n");
+ return 0;
+ }
+
+ do_byteswap = 0;
+
+ if (msg.p1 != OSSMIX_P1_MAGIC)
+ {
+ byteswap_msg (&msg);
+
+ if (msg.p1 != OSSMIX_P1_MAGIC)
+ {
+ fprintf (stderr, "Unrecognized endianess\n");
+ return 0;
+ }
+
+ fprintf (stderr, "Using alien endianess\n");
+ do_byteswap = 1;
+ }
+
+ return 1;
+}
+
+static int
+wait_payload (void *payload, int len, bswap_func_t swapper, int *truelen)
+{
+ ossmix_commad_packet_t msg;
+ int l;
+
+ if (sockfd == -1)
+ return -1;
+
+ while (1)
+ {
+ if ((l = read_all (sockfd, &msg, sizeof (msg))) != sizeof (msg))
+ {
+ if (l == 0) /* Connection closed */
+ return -1;
+
+ perror ("get response");
+ return -1;
+ }
+
+ if (do_byteswap)
+ byteswap_msg (&msg);
+
+ if (msg.payload_size > 0)
+ {
+ if ((l =
+ read_all (sockfd, payload,
+ msg.payload_size)) != msg.payload_size)
+ {
+ perror ("Get error message");
+ fprintf (stderr, "Payload size %d/%d\n", l, msg.payload_size);
+ return -1;
+ }
+ }
+
+ if (msg.cmd == OSSMIX_CMD_ERROR)
+ {
+ fprintf (stderr, "Remote error: %s\n", (char *)payload);
+ }
+
+ if (!msg.unsolicited)
+ if (truelen == NULL)
+ if (msg.payload_size != len)
+ {
+ fprintf (stderr, "Payload size mismatch (%d/%d)\n",
+ msg.payload_size, len);
+ return -1;
+ }
+
+ if (truelen != NULL)
+ *truelen = msg.payload_size;
+
+ /*
+ * Return if this was not an async notification message sent by the
+ * server.
+ */
+ if (!msg.unsolicited)
+ return msg.cmd;
+
+ handle_packet (&msg, payload, msg.payload_size);
+ }
+}
+
+int
+send_request (int cmd, int p1, int p2, int p3, int p4, int p5)
+{
+ ossmix_commad_packet_t msg;
+
+ memset (&msg, 0, sizeof (msg));
+
+ msg.cmd = cmd;
+ msg.p1 = p1;
+ msg.p2 = p2;
+ msg.p3 = p3;
+ msg.p4 = p4;
+ msg.p5 = p5;
+ msg.ack_rq = 1;
+
+ if (do_byteswap)
+ byteswap_msg (&msg);
+
+ if (write (sockfd, &msg, sizeof (msg)) != sizeof (msg))
+ {
+ fprintf (stderr, "Write to socket failed\n");
+ }
+ return get_response ();
+}
+
+void
+send_request_noreply (int cmd, int p1, int p2, int p3, int p4, int p5)
+{
+ ossmix_commad_packet_t msg;
+
+ memset (&msg, 0, sizeof (msg));
+
+ msg.cmd = cmd;
+ msg.p1 = p1;
+ msg.p2 = p2;
+ msg.p3 = p3;
+ msg.p4 = p4;
+ msg.p5 = p5;
+ msg.ack_rq = 0;
+
+ if (do_byteswap)
+ byteswap_msg (&msg);
+
+ if (write (sockfd, &msg, sizeof (msg)) != sizeof (msg))
+ {
+ fprintf (stderr, "Write to socket failed\n");
+ }
+ //send(sockfd, &msg, sizeof(msg), 0);
+}
+
+int
+send_request_long (int cmd, int p1, int p2, int p3, int p4, int p5,
+ const char *payload)
+{
+ ossmix_commad_packet_t msg;
+
+ memset (&msg, 0, sizeof (msg));
+
+ msg.cmd = cmd;
+ msg.p1 = p1;
+ msg.p2 = p2;
+ msg.p3 = p3;
+ msg.p4 = p4;
+ msg.p5 = p5;
+ msg.ack_rq = 1;
+ msg.payload_size = strlen (payload);
+
+ if (do_byteswap)
+ byteswap_msg (&msg);
+
+ if (write (sockfd, &msg, sizeof (msg)) != sizeof (msg))
+ {
+ fprintf (stderr, "Write to socket failed\n");
+ }
+ if (write (sockfd, payload, msg.payload_size) != msg.payload_size)
+ {
+ fprintf (stderr, "Write to socket failed\n");
+ }
+ return get_response ();
+}
+
+static int
+tcp_connect (const char *remotehost, int port)
+{
+ struct sockaddr_in sa;
+ struct hostent *he;
+
+ if (mixlib_trace > 0)
+ fprintf (stderr, "Entered tcp_connect(%s, %d)\n", remotehost, port);
+
+ if (port == 0)
+ port = 7777;
+
+ if (initialized)
+ {
+ fprintf (stderr, "Panic: ossmixlib already initialized\n");
+ exit (EXIT_FAILURE);
+ }
+
+ initialized = 1;
+
+ /*
+ * Open the network connection
+ */
+
+ if ((sockfd = socket (PF_INET, SOCK_STREAM, 0)) == -1)
+ {
+ perror ("socket");
+ return -1;
+ }
+
+ if ((he = gethostbyname (remotehost)) == NULL)
+ {
+ herror (remotehost);
+ fprintf (stderr, "Cannot find the OSSMIX server \"%s\"\n", remotehost);
+ return -1;
+ }
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (port);
+
+ memcpy ((void *) &sa.sin_addr, *he->h_addr_list, he->h_length);
+ if (connect (sockfd, (void *) &sa, sizeof (sa)) == -1)
+ {
+ switch (errno)
+ {
+ case ECONNREFUSED:
+ fprintf (stderr,
+ "Remote OSSMIX server is not running (Connection refused)\n");
+ break;
+
+ default:
+ perror ("connect");
+ }
+ fprintf (stderr, "Cannot connect OSSMIX server %s:%d\n", remotehost,
+ port);
+ return -1;
+ }
+#if 0
+// For some reason this doesn't work under Linux
+ atexit (tcp_disconnect);
+#endif
+
+ if (!check_welcome ())
+ return -1;
+ return send_request (OSSMIX_CMD_INIT, 0, 0, 0, 0, 0);
+}
+
+static int
+tcp_get_fd (ossmix_select_poll_t * cb)
+{
+ *cb = poll_callback;
+
+ return sockfd;
+}
+
+static void
+tcp_enable_events (void)
+{
+ send_request_noreply (OSSMIX_CMD_START_EVENTS, 0, 0, 0, 0, 0);
+}
+
+static void
+tcp_disconnect (void)
+{
+ if (mixlib_trace > 0)
+ fprintf (stderr, "Entered tcp_disconnect()\n");
+
+ if (sockfd < 0)
+ return;
+
+ send_request (OSSMIX_CMD_EXIT, 0, 0, 0, 0, 0);
+ close (sockfd);
+ sockfd = -1;
+}
+
+static int
+tcp_get_nmixers (void)
+{
+ int nmixers;
+
+ nmixers = send_request (OSSMIX_CMD_GET_NMIXERS, 0, 0, 0, 0, 0);
+
+ // TODO: num_mixers = nmixers;
+
+ return nmixers;
+}
+
+static int
+tcp_get_mixerinfo (int mixernum, oss_mixerinfo * mi)
+{
+ send_request_noreply (OSSMIX_CMD_GET_MIXERINFO, mixernum, 0, 0, 0, 0);
+ return wait_payload (mi, sizeof (*mi), bswap_mixerinfo, NULL);
+}
+
+static int
+tcp_open_mixer (int mixernum)
+{
+ int nrext, nrext2;
+ oss_mixext nodes[MAX_NODES];
+ value_packet_t value_packet;
+
+ if (send_request (OSSMIX_CMD_OPEN_MIXER, mixernum, 0, 0, 0, 0) < 0)
+ return -1;
+
+ if (mixernum >= MAX_TMP_MIXER)
+ {
+ fprintf (stderr, "tcp_open_mixer: Mixer number too large %d\n",
+ mixernum);
+ return -1;
+ }
+
+ if ((nrext = send_request (OSSMIX_CMD_GET_NREXT, mixernum, 0, 0, 0, 0)) < 0)
+ return -1;
+
+ nrext2 = nrext; // Save the value for the next step
+/*
+ * Load all node info records
+ */
+
+ send_request_noreply (OSSMIX_CMD_GET_NODEINFO, mixernum, 0, nrext - 1, 0,
+ 0);
+ while (nrext > 0)
+ {
+ int i;
+ int n;
+
+ n = nrext;
+ if (n > MAX_NODES)
+ n = MAX_NODES;
+
+ if (wait_payload
+ (nodes, n * sizeof (oss_mixext), bswap_nodeinfo_array, NULL) < 0)
+ return -1;
+
+ for (i = 0; i < n; i++)
+ {
+ oss_mixext *node;
+
+ node = &nodes[i];
+
+ mixc_add_node (mixernum, node->ctrl, node);
+ }
+
+ nrext -= n;
+ }
+
+ nrext = nrext2;
+
+/*
+ * Load all values
+ */
+ send_request_noreply (OSSMIX_CMD_GET_ALL_VALUES, mixernum, 0, 0, 0, 0);
+
+ if (wait_payload
+ (value_packet, nrext * sizeof (value_record_t), bswap_int_array,
+ &nrext2) < 0)
+ return -1;
+ else
+ {
+ int i;
+
+ for (i = 0; i < nrext2 / sizeof (value_record_t); i++)
+ {
+ mixc_set_value (mixernum, value_packet[i].node,
+ value_packet[i].value);
+ }
+ }
+
+ return 0;
+}
+
+static void
+tcp_close_mixer (int mixernum)
+{
+ send_request_noreply (OSSMIX_CMD_CLOSE_MIXER, mixernum, 0, 0, 0, 0);
+}
+
+static int
+tcp_get_nrext (int mixernum)
+{
+ // TODO: Cache this information locally
+ return send_request (OSSMIX_CMD_GET_NREXT, mixernum, 0, 0, 0, 0);
+}
+
+static int
+tcp_get_nodeinfo (int mixernum, int node, oss_mixext * ext)
+{
+ oss_mixext *lnode;
+
+ lnode = mixc_get_node (mixernum, node);
+
+ if (lnode == NULL)
+ {
+ send_request_noreply (OSSMIX_CMD_GET_NODEINFO, mixernum, node, 0, 0, 0);
+ if (wait_payload (ext, sizeof (*ext), bswap_nodeinfo, NULL) < 0)
+ {
+ fprintf (stderr,
+ "tcp_get_nodeinfo: Mixer %d: Cannot load nodeinfo for %d\n",
+ mixernum, node);
+ return -1;
+ }
+ mixc_add_node (mixernum, node, lnode);
+ }
+
+ memcpy (ext, lnode, sizeof (*ext));
+
+ return 0;
+}
+
+static int
+tcp_get_enuminfo (int mixernum, int node, oss_mixer_enuminfo * ei)
+{
+ send_request_noreply (OSSMIX_CMD_GET_ENUMINFO, mixernum, node, 0, 0, 0);
+ return wait_payload (ei, sizeof (*ei), bswap_enuminfo, NULL);
+}
+
+static int
+tcp_get_description (int mixernum, int node, oss_mixer_enuminfo * desc)
+{
+ send_request_noreply (OSSMIX_CMD_GET_DESCRIPTION, mixernum, node, 0, 0, 0);
+ return wait_payload (desc, sizeof (*desc), bswap_enuminfo, NULL);
+}
+
+static int
+tcp_get_value (int mixernum, int ctl, int timestamp)
+{
+ // return send_request(OSSMIX_CMD_GET_VALUE, mixernum, ctl, timestamp, 0, 0);
+ return mixc_get_value (mixernum, ctl);
+}
+
+static void
+tcp_set_value (int mixernum, int ctl, int timestamp, int value)
+{
+ send_request_noreply (OSSMIX_CMD_SET_VALUE, mixernum, ctl, timestamp, value,
+ 0);
+}
+
+static void
+tcp_timertick(void)
+{
+ // NOP
+}
+
+ossmix_driver_t ossmix_tcp_driver = {
+ tcp_connect,
+ tcp_get_fd,
+ tcp_disconnect,
+ tcp_enable_events,
+ tcp_get_nmixers,
+ tcp_get_mixerinfo,
+ tcp_open_mixer,
+ tcp_close_mixer,
+ tcp_get_nrext,
+ tcp_get_nodeinfo,
+ tcp_get_enuminfo,
+ tcp_get_description,
+ tcp_get_value,
+ tcp_set_value,
+ tcp_timertick
+};
+
+static void
+handle_values(int mixnum, int nvalues, value_record_t values[], int len)
+{
+ int i;
+
+ if (nvalues*sizeof(value_record_t) > len)
+ {
+ fprintf(stderr, "Short value record (%lu, %d)\n",
+ (unsigned long)(nvalues*sizeof(value_record_t)), len);
+ exit(EXIT_FAILURE);
+ }
+
+ for (i=0;i<nvalues;i++)
+ {
+ _client_event (OSSMIX_EVENT_VALUE, mixnum, values[i].node, values[i].value, 0, 0);
+ }
+
+}
+
+static void
+handle_packet (ossmix_commad_packet_t * msg, char *payload, int payload_size)
+{
+//printf("Got packet %d, p=0x%08x, %d, %d, %d, %d\n",
+// msg->cmd, msg->p1, msg->p2, msg->p3, msg->p4, msg->p5);
+
+// _client_event (msg->cmd, msg->p1, msg->p2, msg->p3, msg->p4, msg->p5);
+
+ switch(msg->cmd)
+ {
+ case OSSMIX_EVENT_VALUE:
+ handle_values(msg->p2, msg->p1, (value_record_t *)payload, payload_size);
+ break;
+
+ case OSSMIX_EVENT_NEWMIXER:
+ //num_mixers=msg->p1;
+ _client_event (OSSMIX_EVENT_NEWMIXER, msg->p1, 0, 0, 0, 0);
+ break;
+
+ default:
+ fprintf(stderr, "Unrecognized event packet %d\n", msg->cmd);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void
+poll_callback (void)
+{
+ ossmix_commad_packet_t msg;
+ char payload[4096];
+ int l;
+
+ if (sockfd == -1)
+ return;
+
+ payload[0] = 0;
+
+ if ((l = read_all (sockfd, &msg, sizeof (msg))) != sizeof (msg))
+ {
+ if (l == 0) /* Connection closed */
+ return;
+
+ perror ("get response");
+ return;
+ }
+
+ if (do_byteswap)
+ byteswap_msg (&msg);
+
+ if (msg.payload_size > 0)
+ {
+ if ((l =
+ read_all (sockfd, payload, msg.payload_size)) != msg.payload_size)
+ {
+ perror ("Get response payload");
+ fprintf (stderr, "Payload size %d/%d\n", l, msg.payload_size);
+ return;
+ }
+
+ payload[l] = 0;
+ }
+
+ handle_packet (&msg, payload, msg.payload_size);
+}