summaryrefslogtreecommitdiff
path: root/lib/libossmix/libossmix_tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libossmix/libossmix_tcp.c')
-rw-r--r--lib/libossmix/libossmix_tcp.c813
1 files changed, 813 insertions, 0 deletions
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);
+}