summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_midiloop/oss_midiloop.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drv/oss_midiloop/oss_midiloop.c')
-rw-r--r--kernel/drv/oss_midiloop/oss_midiloop.c412
1 files changed, 412 insertions, 0 deletions
diff --git a/kernel/drv/oss_midiloop/oss_midiloop.c b/kernel/drv/oss_midiloop/oss_midiloop.c
new file mode 100644
index 0000000..5c9082a
--- /dev/null
+++ b/kernel/drv/oss_midiloop/oss_midiloop.c
@@ -0,0 +1,412 @@
+/*
+ * Purpose: MIDI loopback driver
+ */
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "oss_midiloop_cfg.h"
+#include "midi_core.h"
+
+#define MIDI_SYNTH_NAME "OSS loopback MIDI"
+#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
+
+#define MAX_INSTANCES 8
+#define CLIENT_NAME "MIDI loopback"
+#define SERVER_NAME "MIDI loopback server side"
+
+#define POLL_HZ (OSS_HZ/100)
+
+static int open_clients = 0;
+static int open_servers = 0;
+
+typedef struct midiloop_devc
+{
+ oss_device_t *osdev;
+ oss_mutex_t mutex;
+
+ int instance_no;
+ int side;
+#define SIDE_SERVER 0
+#define SIDE_CLIENT 0
+ int midi_dev;
+ struct midiloop_devc *client, *server, *peer;
+
+ int open_mode;
+ oss_midi_inputbyte_t inputbyte_func;
+ oss_midi_inputbuf_t inputbuf_func;
+ oss_longname_t song_name;
+
+} midiloop_devc;
+
+static midiloop_devc midiloop_devs[2 * MAX_INSTANCES] = { {0} };
+static int ndevs = 0;
+
+static int
+midiloop_open_server (int dev, int mode, oss_midi_inputbyte_t inputbyte,
+ oss_midi_inputbuf_t inputbuf,
+ oss_midi_outputintr_t outputintr)
+{
+ oss_native_word flags;
+ midiloop_devc *devc, *client_devc;
+ char *cmd;
+ int client_dev;
+
+ devc = midi_devs[dev]->devc;
+
+ client_devc = devc->client;
+
+ client_dev = client_devc->midi_dev;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ if (devc->open_mode)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EBUSY;
+ }
+
+ devc->open_mode = mode;
+ devc->inputbyte_func = inputbyte;
+ devc->inputbuf_func = inputbuf;
+
+ open_servers++;
+
+ if ((cmd = midi_devs[dev]->cmd) != NULL)
+ {
+ sprintf (midi_devs[client_dev]->name, CLIENT_NAME " (%s)", cmd);
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 0;
+}
+
+static void
+midiloop_close_server (int dev, int mode)
+{
+ oss_native_word flags;
+ midiloop_devc *devc, *client_devc;
+ int client_dev;
+
+ devc = midi_devs[dev]->devc;
+ client_devc = devc->client;
+ client_dev = client_devc->midi_dev;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ devc->open_mode = 0;
+ devc->inputbyte_func = NULL;
+ devc->inputbuf_func = NULL;
+ strcpy (midi_devs[client_dev]->name, CLIENT_NAME);
+ midi_devs[client_dev]->latency = -1; /* Not indicated */
+
+ open_servers--;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+static int
+midiloop_open_client (int dev, int mode, oss_midi_inputbyte_t inputbyte,
+ oss_midi_inputbuf_t inputbuf,
+ oss_midi_outputintr_t outputintr)
+{
+ oss_native_word flags;
+ midiloop_devc *devc;
+
+ devc = midi_devs[dev]->devc;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ if (devc->open_mode)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EBUSY;
+ }
+
+ devc->open_mode = mode;
+ devc->inputbyte_func = inputbyte;
+ devc->inputbuf_func = inputbuf;
+ open_clients++;
+
+ /* Notify the server */
+ if (devc->server->open_mode & OPEN_READ)
+ {
+ if (devc->server->inputbyte_func != NULL)
+ devc->server->inputbyte_func (devc->server->midi_dev, 0xfa); /* Start */
+
+ /* Restart the MTC timer of the server side (if necessary) */
+ if (midi_devs[devc->server->midi_dev]->mtc_timebase > -1)
+ {
+ int timebase = 25;
+ oss_midi_ioctl (devc->server->midi_dev, NULL, SNDCTL_MIDI_MTCINPUT,
+ (ioctl_arg) & timebase);
+ }
+ }
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 0;
+}
+
+static void
+midiloop_close_client (int dev, int mode)
+{
+ oss_native_word flags;
+ midiloop_devc *devc;
+
+ devc = midi_devs[dev]->devc;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ devc->open_mode = 0;
+ devc->inputbyte_func = NULL;
+ devc->inputbuf_func = NULL;
+ open_clients--;
+
+ /* Notify the server */
+ if (devc->server->open_mode & OPEN_READ)
+ if (devc->server->inputbyte_func != NULL)
+ devc->server->inputbyte_func (devc->server->midi_dev, 0xfc); /* Stop */
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ /* Halt the MTC timer of the server side (if necessary) */
+ if (midi_devs[devc->server->midi_dev]->mtc_timebase > 0)
+ {
+ int timebase = 0;
+ oss_midi_ioctl (devc->server->midi_dev, NULL, SNDCTL_MIDI_MTCINPUT,
+ (ioctl_arg) & timebase);
+ }
+}
+
+static int
+midiloop_out (int dev, unsigned char midi_byte)
+{
+ midiloop_devc *devc;
+ oss_native_word flags;
+ int ok = 0;
+
+ devc = midi_devs[dev]->devc;
+
+ if (devc->peer->open_mode == 0)
+ return 1;
+ MUTEX_ENTER_IRQDISABLE (devc->peer->mutex, flags);
+ if (devc->peer->inputbyte_func != NULL)
+ {
+ ok = devc->peer->inputbyte_func (devc->peer->midi_dev, midi_byte);
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->peer->mutex, flags);
+ return ok;
+}
+
+static int
+midiloop_bulk_out (int dev, unsigned char *buf, int len)
+{
+ midiloop_devc *devc;
+ oss_native_word flags;
+ int ok = 0;
+
+ devc = midi_devs[dev]->devc;
+
+ MUTEX_ENTER_IRQDISABLE (devc->peer->mutex, flags);
+ if (devc->peer->open_mode == 0 || devc->peer->inputbuf_func == NULL)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->peer->mutex, flags);
+ return len;
+ }
+
+ ok = devc->peer->inputbuf_func (devc->peer->midi_dev, buf, len);
+ MUTEX_EXIT_IRQRESTORE (devc->peer->mutex, flags);
+ return ok;
+}
+
+static void
+midiloop_timer_setup (int dev /* Client dev */ )
+{
+ midiloop_devc *devc, *server_devc;
+ int client_dev;
+ int server_dev;
+
+ devc = midi_devs[dev]->devc;
+ server_devc = devc->server;
+ client_dev = devc->midi_dev;
+ server_dev = devc->server->midi_dev;
+
+ oss_midi_copy_timer (server_dev, client_dev);
+}
+
+static int
+midiloop_ioctl (int dev, unsigned cmd, ioctl_arg arg)
+{
+ char *song_name;
+ oss_native_word flags;
+ midiloop_devc *devc, *client_devc;
+ int client_dev;
+
+ devc = midi_devs[dev]->devc;
+ client_devc = devc->client;
+ client_dev = client_devc->midi_dev;
+
+ switch (cmd)
+ {
+ case SNDCTL_SETSONG:
+ if (devc->side != SIDE_CLIENT)
+ return 0;
+
+ song_name = (char *) arg;
+ song_name[OSS_LONGNAME_SIZE - 1] = 0;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ strcpy (devc->song_name, song_name);
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+ break;
+
+ case SNDCTL_SETNAME:
+ if (devc->side != SIDE_SERVER)
+ return 0;
+
+ song_name = (char *) arg;
+ song_name[OSS_LONGNAME_SIZE - 1] = 0;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ strcpy (midi_devs[devc->client->midi_dev]->name, song_name);
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+ break;
+
+ case SNDCTL_GETSONG:
+ song_name = (char *) arg;
+ memset (song_name, 0, OSS_LONGNAME_SIZE);
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ if (devc->side == SIDE_SERVER)
+ strcpy (song_name, devc->peer->song_name);
+ else
+ strcpy (song_name, devc->song_name);
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 0;
+ break;
+
+ case SNDCTL_MIDI_SET_LATENCY:
+ if (devc->side != SIDE_SERVER)
+ return OSS_EINVAL;
+ if (*arg < -1 || *arg > 10000000)
+ return OSS_EINVAL;
+ midi_devs[devc->client->midi_dev]->latency = *arg;
+ break;
+ }
+ return OSS_EINVAL;
+}
+
+static midi_driver_t midiloop_client_driver = {
+ midiloop_open_client,
+ midiloop_close_client,
+ midiloop_ioctl,
+ midiloop_out,
+ midiloop_bulk_out,
+ MIDI_PAYLOAD_SIZE,
+ NULL,
+ NULL,
+ midiloop_timer_setup
+};
+
+static midi_driver_t midiloop_server_driver = {
+ midiloop_open_server,
+ midiloop_close_server,
+ midiloop_ioctl,
+ midiloop_out,
+ midiloop_bulk_out,
+ MIDI_PAYLOAD_SIZE
+};
+
+static void
+attach_midiloop_dev (oss_device_t * osdev)
+{
+ midiloop_devc *server_devc = NULL, *client_devc = NULL;
+
+ if (POLL_HZ < 1)
+ {
+ cmn_err (CE_CONT, "midiloop: Too low system timer resolution\n");
+ return;
+ }
+
+ if (ndevs >= MAX_INSTANCES)
+ {
+ cmn_err (CE_CONT, "MidiLoop: Too many instances\n");
+ return;
+ }
+
+ client_devc = &midiloop_devs[ndevs * 2];
+ client_devc->osdev = osdev;
+ MUTEX_INIT (client_devc->osdev, client_devc->mutex, MH_DRV);
+ client_devc->instance_no = ndevs;
+
+ client_devc->midi_dev =
+ oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "LOOP", CLIENT_NAME,
+ &midiloop_client_driver, sizeof (midi_driver_t),
+ MFLAG_VIRTUAL | MFLAG_CLIENT, client_devc,
+ client_devc->osdev);
+
+ server_devc = &midiloop_devs[ndevs * 2 + 1];
+ server_devc->osdev = osdev;
+ MUTEX_INIT (server_devc->osdev, server_devc->mutex, MH_DRV);
+ server_devc->instance_no = ndevs;
+
+ midi_devs[client_devc->midi_dev]->latency = 1000000;
+
+ server_devc->midi_dev =
+ oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "LOOP_S", SERVER_NAME,
+ &midiloop_server_driver, sizeof (midi_driver_t),
+ MFLAG_NOSEQUENCER | MFLAG_VIRTUAL | MFLAG_SERVER |
+ MFLAG_SELFTIMING, server_devc, server_devc->osdev);
+
+ client_devc->server = client_devc->peer = server_devc;
+ client_devc->client = client_devc;
+ client_devc->side = SIDE_CLIENT;
+
+ server_devc->client = server_devc->peer = client_devc;
+ server_devc->server = server_devc;
+ server_devc->side = SIDE_SERVER;
+
+ ndevs++;
+}
+
+int
+oss_midiloop_attach (oss_device_t * osdev)
+{
+ extern int midiloop_instances;
+ int i;
+
+ oss_register_device (osdev, "OSS MIDI loopback driver");
+
+ if (midiloop_instances > MAX_INSTANCES)
+ midiloop_instances = MAX_INSTANCES;
+ for (i = 0; i < midiloop_instances; i++)
+ {
+ attach_midiloop_dev (osdev);
+ }
+
+ return 1;
+}
+
+int
+oss_midiloop_detach (oss_device_t * osdev)
+{
+ int i, err;
+
+ if ((err = oss_disable_device (osdev)) < 0)
+ return 0;
+
+ for (i = 0; i < 2 * ndevs; i++)
+ {
+ midiloop_devc *devc;
+
+ devc = &midiloop_devs[i];
+ }
+
+ oss_unregister_device (osdev);
+
+ return 1;
+}