summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2013-10-11 14:45:07 -0400
committerPaul Davis <paul@linuxaudiosystems.com>2013-10-11 14:45:07 -0400
commit8fbd62a39d1c0c776dbe90d2be1cb2364eb28747 (patch)
tree09ff456c3908c512e41af54afcfb2c1b4f983fa5 /drivers
parenta6fc200ac0c93f032de15f65f5ec8a1f9b5c7358 (diff)
downloadjack1-8fbd62a39d1c0c776dbe90d2be1cb2364eb28747.tar.gz
add ALSA MIDI code back as (slave) driver
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile.am4
-rw-r--r--drivers/alsa/Makefile.am5
-rw-r--r--drivers/alsa/alsa_driver.c581
-rw-r--r--drivers/alsa/alsa_driver.h3
-rw-r--r--drivers/alsa/usx2y.c12
-rw-r--r--drivers/alsa_midi/Makefile.am16
-rw-r--r--drivers/alsa_midi/a2j.h139
-rw-r--r--drivers/alsa_midi/alsa_midi.c864
-rw-r--r--drivers/alsa_midi/alsa_midi_driver.c48
-rw-r--r--drivers/alsa_midi/list.c147
-rw-r--r--drivers/alsa_midi/list.h903
-rw-r--r--drivers/alsa_midi/port.c217
-rw-r--r--drivers/alsa_midi/port.h29
-rw-r--r--drivers/alsa_midi/port_hash.c63
-rw-r--r--drivers/alsa_midi/port_hash.h35
-rw-r--r--drivers/alsa_midi/port_thread.c235
-rw-r--r--drivers/alsa_midi/port_thread.h31
-rw-r--r--drivers/am/Makefile.am (renamed from drivers/alsa-midi/Makefile.am)0
-rw-r--r--drivers/am/alsa_midi.h (renamed from drivers/alsa-midi/alsa_midi.h)0
-rw-r--r--drivers/am/alsa_midi_driver.c (renamed from drivers/alsa-midi/alsa_midi_driver.c)0
-rw-r--r--drivers/am/alsa_rawmidi.c (renamed from drivers/alsa-midi/alsa_rawmidi.c)0
-rw-r--r--drivers/am/alsa_seqmidi.c (renamed from drivers/alsa-midi/alsa_seqmidi.c)0
-rw-r--r--drivers/am/midi_pack.h (renamed from drivers/alsa-midi/midi_pack.h)0
-rw-r--r--drivers/am/midi_unpack.h (renamed from drivers/alsa-midi/midi_unpack.h)0
24 files changed, 3002 insertions, 330 deletions
diff --git a/drivers/Makefile.am b/drivers/Makefile.am
index 4d232c2..b159b19 100644
--- a/drivers/Makefile.am
+++ b/drivers/Makefile.am
@@ -7,7 +7,7 @@ ALSA_DIR =
endif
if HAVE_ALSA_MIDI
-ALSA_MIDI_DIR = alsa-midi
+ALSA_MIDI_DIR = alsa_midi
else
ALSA_MIDI_DIR =
endif
@@ -49,4 +49,4 @@ FIREWIRE_DIR =
endif
SUBDIRS = $(ALSA_MIDI_DIR) $(ALSA_DIR) dummy $(OSS_DIR) $(SUN_DIR) $(PA_DIR) $(CA_DIR) $(FREEBOB_DIR) $(FIREWIRE_DIR) netjack
-DIST_SUBDIRS = alsa alsa-midi dummy oss sun portaudio coreaudio freebob firewire netjack
+DIST_SUBDIRS = alsa alsa_midi dummy oss sun portaudio coreaudio freebob firewire netjack
diff --git a/drivers/alsa/Makefile.am b/drivers/alsa/Makefile.am
index 0b9bf1e..04b6284 100644
--- a/drivers/alsa/Makefile.am
+++ b/drivers/alsa/Makefile.am
@@ -8,10 +8,7 @@ plugin_LTLIBRARIES = jack_alsa.la
jack_alsa_la_LDFLAGS = -module -avoid-version
jack_alsa_la_SOURCES = alsa_driver.c generic_hw.c memops.c \
- hammerfall.c hdsp.c ice1712.c usx2y.c \
- ../alsa-midi/alsa_rawmidi.c ../alsa-midi/alsa_seqmidi.c
-
-jack_alsa_la_CFLAGS = -I../alsa-midi
+ hammerfall.c hdsp.c ice1712.c usx2y.c
noinst_HEADERS = alsa_driver.h \
generic.h \
diff --git a/drivers/alsa/alsa_driver.c b/drivers/alsa/alsa_driver.c
index bd8fd85..93b1633 100644
--- a/drivers/alsa/alsa_driver.c
+++ b/drivers/alsa/alsa_driver.c
@@ -1017,9 +1017,6 @@ alsa_driver_start (alsa_driver_t *driver)
malloc (sizeof (struct pollfd) *
(driver->playback_nfds + driver->capture_nfds + 2));
- if (driver->midi && !driver->xrun_recovery)
- (driver->midi->start)(driver->midi);
-
if (driver->playback_handle) {
/* fill playback buffer with zeroes, and mark
all fragments as having data.
@@ -1124,9 +1121,6 @@ alsa_driver_stop (alsa_driver_t *driver)
driver->hw->set_input_monitor_mask (driver->hw, 0);
}
- if (driver->midi && !driver->xrun_recovery)
- (driver->midi->stop)(driver->midi);
-
return 0;
}
@@ -1140,9 +1134,6 @@ alsa_driver_restart (alsa_driver_t *driver)
res = driver->nt_start((struct _jack_driver_nt *) driver);
driver->xrun_recovery = 0;
- if (res && driver->midi)
- (driver->midi->stop)(driver->midi);
-
return res;
}
@@ -1565,9 +1556,6 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes)
return 0;
}
- if (driver->midi)
- (driver->midi->read)(driver->midi, nframes);
-
if (!driver->capture_handle) {
return 0;
}
@@ -1640,9 +1628,6 @@ alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes)
return -1;
}
- if (driver->midi)
- (driver->midi->write)(driver->midi, nframes);
-
nwritten = 0;
contiguous = 0;
orig_nframes = nframes;
@@ -1852,13 +1837,6 @@ alsa_driver_attach (alsa_driver_t *driver)
}
}
- if (driver->midi) {
- int err = (driver->midi->attach)(driver->midi);
- if (err)
- jack_error("ALSA: cannot attach midi: %d", err);
- }
-
-
return jack_activate (driver->client);
}
@@ -1871,278 +1849,276 @@ alsa_driver_detach (alsa_driver_t *driver)
return 0;
}
- if (driver->midi)
- (driver->midi->detach)(driver->midi);
-
- for (node = driver->capture_ports; node;
- node = jack_slist_next (node)) {
- jack_port_unregister (driver->client,
- ((jack_port_t *) node->data));
- }
-
- jack_slist_free (driver->capture_ports);
- driver->capture_ports = 0;
-
- for (node = driver->playback_ports; node;
- node = jack_slist_next (node)) {
- jack_port_unregister (driver->client,
- ((jack_port_t *) node->data));
- }
-
- jack_slist_free (driver->playback_ports);
- driver->playback_ports = 0;
-
- if (driver->monitor_ports) {
- for (node = driver->monitor_ports; node;
- node = jack_slist_next (node)) {
- jack_port_unregister (driver->client,
- ((jack_port_t *) node->data));
- }
-
- jack_slist_free (driver->monitor_ports);
- driver->monitor_ports = 0;
- }
-
- return 0;
-}
-
-#if 0
-static int /* UNUSED */
-alsa_driver_change_sample_clock (alsa_driver_t *driver, SampleClockMode mode)
-
-{
- return driver->hw->change_sample_clock (driver->hw, mode);
-}
-
-static void /* UNUSED */
-alsa_driver_request_all_monitor_input (alsa_driver_t *driver, int yn)
-
-{
- if (driver->hw_monitoring) {
- if (yn) {
- driver->hw->set_input_monitor_mask (driver->hw, ~0U);
- } else {
- driver->hw->set_input_monitor_mask (
- driver->hw, driver->input_monitor_mask);
- }
- }
-
- driver->all_monitor_in = yn;
-}
-
-static void /* UNUSED */
-alsa_driver_set_hw_monitoring (alsa_driver_t *driver, int yn)
-{
- if (yn) {
- driver->hw_monitoring = TRUE;
-
- if (driver->all_monitor_in) {
- driver->hw->set_input_monitor_mask (driver->hw, ~0U);
- } else {
- driver->hw->set_input_monitor_mask (
- driver->hw, driver->input_monitor_mask);
- }
- } else {
- driver->hw_monitoring = FALSE;
- driver->hw->set_input_monitor_mask (driver->hw, 0);
- }
-}
-
-static ClockSyncStatus /* UNUSED */
-alsa_driver_clock_sync_status (channel_t chn)
-{
- return Lock;
-}
-#endif
-
-static void
-alsa_driver_delete (alsa_driver_t *driver)
-{
- JSList *node;
-
- if (driver->midi)
- (driver->midi->destroy)(driver->midi);
-
- for (node = driver->clock_sync_listeners; node;
- node = jack_slist_next (node)) {
- free (node->data);
- }
- jack_slist_free (driver->clock_sync_listeners);
-
- if (driver->ctl_handle) {
- snd_ctl_close (driver->ctl_handle);
- driver->ctl_handle = 0;
- }
-
- if (driver->capture_handle) {
- snd_pcm_close (driver->capture_handle);
- driver->capture_handle = 0;
- }
-
- if (driver->playback_handle) {
- snd_pcm_close (driver->playback_handle);
- driver->capture_handle = 0;
- }
-
- if (driver->capture_hw_params) {
- snd_pcm_hw_params_free (driver->capture_hw_params);
- driver->capture_hw_params = 0;
- }
-
- if (driver->playback_hw_params) {
- snd_pcm_hw_params_free (driver->playback_hw_params);
- driver->playback_hw_params = 0;
- }
-
- if (driver->capture_sw_params) {
- snd_pcm_sw_params_free (driver->capture_sw_params);
- driver->capture_sw_params = 0;
- }
-
- if (driver->playback_sw_params) {
- snd_pcm_sw_params_free (driver->playback_sw_params);
- driver->playback_sw_params = 0;
- }
-
- if (driver->pfd) {
- free (driver->pfd);
- }
-
- if (driver->hw) {
- driver->hw->release (driver->hw);
- driver->hw = 0;
- }
- free(driver->alsa_name_playback);
- free(driver->alsa_name_capture);
- free(driver->alsa_driver);
-
- alsa_driver_release_channel_dependent_memory (driver);
- jack_driver_nt_finish ((jack_driver_nt_t *) driver);
- free (driver);
-}
-
-static char*
-discover_alsa_using_apps ()
-{
- char found[2048];
- char command[5192];
- char* path = getenv ("PATH");
- char* dir;
- size_t flen = 0;
- int card;
- int device;
- size_t cmdlen = 0;
-
- if (!path) {
- return NULL;
- }
-
- /* look for lsof and give up if its not in PATH */
-
- path = strdup (path);
- dir = strtok (path, ":");
- while (dir) {
- char maybe[PATH_MAX+1];
- snprintf (maybe, sizeof(maybe), "%s/lsof", dir);
- if (access (maybe, X_OK) == 0) {
- break;
- }
- dir = strtok (NULL, ":");
- }
- free (path);
-
- if (!dir) {
- return NULL;
- }
-
- snprintf (command, sizeof (command), "lsof -Fc0 ");
- cmdlen = strlen (command);
-
- for (card = 0; card < 8; ++card) {
- for (device = 0; device < 8; ++device) {
- char buf[32];
-
- snprintf (buf, sizeof (buf), "/dev/snd/pcmC%dD%dp", card, device);
- if (access (buf, F_OK) == 0) {
- snprintf (command+cmdlen, sizeof(command)-cmdlen, "%s ", buf);
- }
- cmdlen = strlen (command);
-
- snprintf (buf, sizeof (buf), "/dev/snd/pcmC%dD%dc", card, device);
- if (access (buf, F_OK) == 0) {
- snprintf (command+cmdlen, sizeof(command)-cmdlen, "%s ", buf);
- }
- cmdlen = strlen (command);
- }
+ for (node = driver->capture_ports; node;
+ node = jack_slist_next (node)) {
+ jack_port_unregister (driver->client,
+ ((jack_port_t *) node->data));
}
- FILE* f = popen (command, "r");
-
- if (!f) {
- return NULL;
- }
-
- while (!feof (f)) {
- char buf[1024]; /* lsof doesn't output much */
-
- if (!fgets (buf, sizeof (buf), f)) {
- break;
- }
-
- if (*buf != 'p') {
- return NULL;
- }
-
- /* buf contains NULL as a separator between the process field and the command field */
- char *pid = buf;
- ++pid; /* skip leading 'p' */
- char *cmd = pid;
-
- /* skip to NULL */
- while (*cmd) {
- ++cmd;
- }
- ++cmd; /* skip to 'c' */
- ++cmd; /* skip to first character of command */
-
- snprintf (found+flen, sizeof (found)-flen, "%s (process ID %s)\n", cmd, pid);
- flen = strlen (found);
-
- if (flen >= sizeof (found)) {
- break;
- }
- }
-
- pclose (f);
-
- if (flen) {
- return strdup (found);
- } else {
- return NULL;
- }
-}
-
-
-static jack_driver_t *
-alsa_driver_new (char *name, char *playback_alsa_device,
- char *capture_alsa_device,
- jack_client_t *client,
- jack_nframes_t frames_per_cycle,
- jack_nframes_t user_nperiods,
- jack_nframes_t rate,
- int hw_monitoring,
- int hw_metering,
- int capturing,
- int playing,
- DitherAlgorithm dither,
- int soft_mode,
- int monitor,
- int user_capture_nchnls,
- int user_playback_nchnls,
- int shorts_first,
- jack_nframes_t capture_latency,
- jack_nframes_t playback_latency,
- alsa_midi_t *midi_driver
+ jack_slist_free (driver->capture_ports);
+ driver->capture_ports = 0;
+
+ for (node = driver->playback_ports; node;
+ node = jack_slist_next (node)) {
+ jack_port_unregister (driver->client,
+ ((jack_port_t *) node->data));
+ }
+
+ jack_slist_free (driver->playback_ports);
+ driver->playback_ports = 0;
+
+ if (driver->monitor_ports) {
+ for (node = driver->monitor_ports; node;
+ node = jack_slist_next (node)) {
+ jack_port_unregister (driver->client,
+ ((jack_port_t *) node->data));
+ }
+
+ jack_slist_free (driver->monitor_ports);
+ driver->monitor_ports = 0;
+ }
+
+ return 0;
+ }
+
+ #if 0
+ static int /* UNUSED */
+ alsa_driver_change_sample_clock (alsa_driver_t *driver, SampleClockMode mode)
+
+ {
+ return driver->hw->change_sample_clock (driver->hw, mode);
+ }
+
+ static void /* UNUSED */
+ alsa_driver_request_all_monitor_input (alsa_driver_t *driver, int yn)
+
+ {
+ if (driver->hw_monitoring) {
+ if (yn) {
+ driver->hw->set_input_monitor_mask (driver->hw, ~0U);
+ } else {
+ driver->hw->set_input_monitor_mask (
+ driver->hw, driver->input_monitor_mask);
+ }
+ }
+
+ driver->all_monitor_in = yn;
+ }
+
+ static void /* UNUSED */
+ alsa_driver_set_hw_monitoring (alsa_driver_t *driver, int yn)
+ {
+ if (yn) {
+ driver->hw_monitoring = TRUE;
+
+ if (driver->all_monitor_in) {
+ driver->hw->set_input_monitor_mask (driver->hw, ~0U);
+ } else {
+ driver->hw->set_input_monitor_mask (
+ driver->hw, driver->input_monitor_mask);
+ }
+ } else {
+ driver->hw_monitoring = FALSE;
+ driver->hw->set_input_monitor_mask (driver->hw, 0);
+ }
+ }
+
+ static ClockSyncStatus /* UNUSED */
+ alsa_driver_clock_sync_status (channel_t chn)
+ {
+ return Lock;
+ }
+ #endif
+
+ static void
+ alsa_driver_delete (alsa_driver_t *driver)
+ {
+ JSList *node;
+
+ /*
+ if (driver->midi)
+ (driver->midi->destroy)((jack_driver_t*) driver->midi);
+ */
+
+ for (node = driver->clock_sync_listeners; node;
+ node = jack_slist_next (node)) {
+ free (node->data);
+ }
+ jack_slist_free (driver->clock_sync_listeners);
+
+ if (driver->ctl_handle) {
+ snd_ctl_close (driver->ctl_handle);
+ driver->ctl_handle = 0;
+ }
+
+ if (driver->capture_handle) {
+ snd_pcm_close (driver->capture_handle);
+ driver->capture_handle = 0;
+ }
+
+ if (driver->playback_handle) {
+ snd_pcm_close (driver->playback_handle);
+ driver->capture_handle = 0;
+ }
+
+ if (driver->capture_hw_params) {
+ snd_pcm_hw_params_free (driver->capture_hw_params);
+ driver->capture_hw_params = 0;
+ }
+
+ if (driver->playback_hw_params) {
+ snd_pcm_hw_params_free (driver->playback_hw_params);
+ driver->playback_hw_params = 0;
+ }
+
+ if (driver->capture_sw_params) {
+ snd_pcm_sw_params_free (driver->capture_sw_params);
+ driver->capture_sw_params = 0;
+ }
+
+ if (driver->playback_sw_params) {
+ snd_pcm_sw_params_free (driver->playback_sw_params);
+ driver->playback_sw_params = 0;
+ }
+
+ if (driver->pfd) {
+ free (driver->pfd);
+ }
+
+ if (driver->hw) {
+ driver->hw->release (driver->hw);
+ driver->hw = 0;
+ }
+ free(driver->alsa_name_playback);
+ free(driver->alsa_name_capture);
+ free(driver->alsa_driver);
+
+ alsa_driver_release_channel_dependent_memory (driver);
+ jack_driver_nt_finish ((jack_driver_nt_t *) driver);
+ free (driver);
+ }
+
+ static char*
+ discover_alsa_using_apps ()
+ {
+ char found[2048];
+ char command[5192];
+ char* path = getenv ("PATH");
+ char* dir;
+ size_t flen = 0;
+ int card;
+ int device;
+ size_t cmdlen = 0;
+
+ if (!path) {
+ return NULL;
+ }
+
+ /* look for lsof and give up if its not in PATH */
+
+ path = strdup (path);
+ dir = strtok (path, ":");
+ while (dir) {
+ char maybe[PATH_MAX+1];
+ snprintf (maybe, sizeof(maybe), "%s/lsof", dir);
+ if (access (maybe, X_OK) == 0) {
+ break;
+ }
+ dir = strtok (NULL, ":");
+ }
+ free (path);
+
+ if (!dir) {
+ return NULL;
+ }
+
+ snprintf (command, sizeof (command), "lsof -Fc0 ");
+ cmdlen = strlen (command);
+
+ for (card = 0; card < 8; ++card) {
+ for (device = 0; device < 8; ++device) {
+ char buf[32];
+
+ snprintf (buf, sizeof (buf), "/dev/snd/pcmC%dD%dp", card, device);
+ if (access (buf, F_OK) == 0) {
+ snprintf (command+cmdlen, sizeof(command)-cmdlen, "%s ", buf);
+ }
+ cmdlen = strlen (command);
+
+ snprintf (buf, sizeof (buf), "/dev/snd/pcmC%dD%dc", card, device);
+ if (access (buf, F_OK) == 0) {
+ snprintf (command+cmdlen, sizeof(command)-cmdlen, "%s ", buf);
+ }
+ cmdlen = strlen (command);
+ }
+ }
+
+ FILE* f = popen (command, "r");
+
+ if (!f) {
+ return NULL;
+ }
+
+ while (!feof (f)) {
+ char buf[1024]; /* lsof doesn't output much */
+
+ if (!fgets (buf, sizeof (buf), f)) {
+ break;
+ }
+
+ if (*buf != 'p') {
+ return NULL;
+ }
+
+ /* buf contains NULL as a separator between the process field and the command field */
+ char *pid = buf;
+ ++pid; /* skip leading 'p' */
+ char *cmd = pid;
+
+ /* skip to NULL */
+ while (*cmd) {
+ ++cmd;
+ }
+ ++cmd; /* skip to 'c' */
+ ++cmd; /* skip to first character of command */
+
+ snprintf (found+flen, sizeof (found)-flen, "%s (process ID %s)\n", cmd, pid);
+ flen = strlen (found);
+
+ if (flen >= sizeof (found)) {
+ break;
+ }
+ }
+
+ pclose (f);
+
+ if (flen) {
+ return strdup (found);
+ } else {
+ return NULL;
+ }
+ }
+
+
+ static jack_driver_t *
+ alsa_driver_new (char *name, char *playback_alsa_device,
+ char *capture_alsa_device,
+ jack_client_t *client,
+ jack_nframes_t frames_per_cycle,
+ jack_nframes_t user_nperiods,
+ jack_nframes_t rate,
+ int hw_monitoring,
+ int hw_metering,
+ int capturing,
+ int playing,
+ DitherAlgorithm dither,
+ int soft_mode,
+ int monitor,
+ int user_capture_nchnls,
+ int user_playback_nchnls,
+ int shorts_first,
+ jack_nframes_t capture_latency,
+ jack_nframes_t playback_latency
)
{
int err;
@@ -2225,7 +2201,6 @@ alsa_driver_new (char *name, char *playback_alsa_device,
driver->alsa_name_playback = strdup (playback_alsa_device);
driver->alsa_name_capture = strdup (capture_alsa_device);
- driver->midi = midi_driver;
driver->xrun_recovery = 0;
if (alsa_driver_check_card_type (driver)) {
@@ -2684,12 +2659,8 @@ driver_get_descriptor ()
params[i].character = 'X';
params[i].type = JackDriverParamString;
strcpy (params[i].value.str, "none");
- strcpy (params[i].short_desc, "ALSA MIDI driver (seq|raw)");
- strcpy (params[i].long_desc,
- "ALSA MIDI driver:\n"
- " none - no MIDI driver\n"
- " seq - ALSA Sequencer driver\n"
- " raw - ALSA RawMIDI driver\n");
+ strcpy (params[i].short_desc, "legacy");
+ strcpy (params[i].long_desc, "legacy option - do not use");
desc->params = params;
@@ -2716,8 +2687,6 @@ driver_initialize (jack_client_t *client, const JSList * params)
int shorts_first = FALSE;
jack_nframes_t systemic_input_latency = 0;
jack_nframes_t systemic_output_latency = 0;
- char *midi_driver_name = "none";
- alsa_midi_t *midi = NULL;
const JSList * node;
const jack_driver_param_t * param;
@@ -2807,7 +2776,7 @@ driver_initialize (jack_client_t *client, const JSList * params)
break;
case 'X':
- midi_driver_name = strdup (param->value.str);
+ /* ignored, legacy option */
break;
}
@@ -2819,12 +2788,6 @@ driver_initialize (jack_client_t *client, const JSList * params)
playback = TRUE;
}
- if (strcmp(midi_driver_name, "seq")==0) {
- midi = alsa_seqmidi_new(client, NULL);
- } else if (strcmp(midi_driver_name, "raw")==0) {
- midi = alsa_rawmidi_new(client);
- }
-
return alsa_driver_new ("alsa_pcm", playback_pcm_name,
capture_pcm_name, client,
frames_per_interrupt,
@@ -2834,7 +2797,7 @@ driver_initialize (jack_client_t *client, const JSList * params)
user_capture_nchnls, user_playback_nchnls,
shorts_first,
systemic_input_latency,
- systemic_output_latency, midi);
+ systemic_output_latency);
}
void
diff --git a/drivers/alsa/alsa_driver.h b/drivers/alsa/alsa_driver.h
index 67bbc89..074ac69 100644
--- a/drivers/alsa/alsa_driver.h
+++ b/drivers/alsa/alsa_driver.h
@@ -38,8 +38,6 @@
#include "driver.h"
#include "memops.h"
-#include "../alsa-midi/alsa_midi.h"
-
typedef void (*ReadCopyFunction) (jack_default_audio_sample_t *dst, char *src,
unsigned long src_bytes,
unsigned long src_skip_bytes);
@@ -136,7 +134,6 @@ typedef struct _alsa_driver {
int xrun_count;
int process_count;
- alsa_midi_t *midi;
int xrun_recovery;
int previously_successfully_configured;
diff --git a/drivers/alsa/usx2y.c b/drivers/alsa/usx2y.c
index 41960ef..23efeb3 100644
--- a/drivers/alsa/usx2y.c
+++ b/drivers/alsa/usx2y.c
@@ -229,9 +229,6 @@ usx2y_driver_start (alsa_driver_t *driver)
return -1;
}
- if (driver->midi && !driver->xrun_recovery)
- (driver->midi->start)(driver->midi);
-
if (driver->playback_handle) {
/* int i, j; */
/* char buffer[2000]; */
@@ -382,9 +379,6 @@ usx2y_driver_stop (alsa_driver_t *driver)
munmap(h->hwdep_pcm_shm, sizeof(snd_usX2Y_hwdep_pcm_shm_t));
- if (driver->midi && !driver->xrun_recovery)
- (driver->midi->stop)(driver->midi);
-
return 0;
}
@@ -487,9 +481,6 @@ usx2y_driver_read (alsa_driver_t *driver, jack_nframes_t nframes)
return 0;
}
- if (driver->midi)
- (driver->midi->read)(driver->midi, nframes);
-
nread = 0;
if (snd_pcm_mmap_begin (driver->capture_handle,
@@ -564,9 +555,6 @@ usx2y_driver_write (alsa_driver_t* driver, jack_nframes_t nframes)
return 0;
}
- if (driver->midi)
- (driver->midi->write)(driver->midi, nframes);
-
nwritten = 0;
/* check current input monitor request status */
diff --git a/drivers/alsa_midi/Makefile.am b/drivers/alsa_midi/Makefile.am
new file mode 100644
index 0000000..e7ebc86
--- /dev/null
+++ b/drivers/alsa_midi/Makefile.am
@@ -0,0 +1,16 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+#
+# slave driver to bridge to ALSA (sequencer) MIDI
+#
+
+driverdir = $(ADDON_DIR)
+
+driver_LTLIBRARIES = jack_alsa_midi.la
+
+jack_alsa_midi_la_LDFLAGS = -module -avoid-version @OS_LDFLAGS@
+jack_alsa_midi_la_SOURCES = alsa_midi.c port.c port_hash.c port_thread.c list.c
+jack_alsa_midi_la_LIBADD = $(ALSA_LIBS)
+
+noinst_HEADERS = a2j.h list.h port.h port_hash.h port_thread.h
+
diff --git a/drivers/alsa_midi/a2j.h b/drivers/alsa_midi/a2j.h
new file mode 100644
index 0000000..c4428b3
--- /dev/null
+++ b/drivers/alsa_midi/a2j.h
@@ -0,0 +1,139 @@
+/* -*- Mode: C ; c-basic-offset: 2 -*- */
+/*
+ * ALSA SEQ < - > JACK MIDI bridge
+ *
+ * Copyright (c) 2006,2007 Dmitry S. Baikov <c0ff@konstruktiv.org>
+ * Copyright (c) 2007,2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __jack_alsa_midi_h__
+#define __jack_alsa_midi_h__
+
+#include <stdbool.h>
+#include <semaphore.h>
+#include <jack/midiport.h>
+#include <jack/ringbuffer.h>
+
+#include "driver.h"
+#include "list.h"
+
+#define JACK_INVALID_PORT NULL
+
+#define MAX_PORTS 2048
+#define MAX_EVENT_SIZE 1024
+
+#define PORT_HASH_BITS 4
+#define PORT_HASH_SIZE (1 << PORT_HASH_BITS)
+
+/* Beside enum use, these are indeces for (struct a2j).stream array */
+#define A2J_PORT_CAPTURE 0 // ALSA playback port -> JACK capture port
+#define A2J_PORT_PLAYBACK 1 // JACK playback port -> ALSA capture port
+
+typedef struct a2j_port * a2j_port_hash_t[PORT_HASH_SIZE];
+
+struct alsa_midi_driver;
+
+struct a2j_port
+{
+ struct a2j_port * next; /* hash - jack */
+ struct list_head siblings; /* list - main loop */
+ struct alsa_midi_driver * driver_ptr;
+ bool is_dead;
+ char name[64];
+ snd_seq_addr_t remote;
+ jack_port_t * jack_port;
+
+ jack_ringbuffer_t * inbound_events; // alsa_midi_event_t + data
+ int64_t last_out_time;
+
+ void * jack_buf;
+};
+
+struct a2j_stream
+{
+ snd_midi_event_t *codec;
+
+ jack_ringbuffer_t *new_ports;
+
+ a2j_port_hash_t port_hash;
+ struct list_head list;
+};
+
+typedef struct alsa_midi_driver
+{
+ JACK_DRIVER_DECL;
+
+ jack_client_t * jack_client;
+
+ snd_seq_t *seq;
+ pthread_t alsa_input_thread;
+ pthread_t alsa_output_thread;
+ int client_id;
+ int port_id;
+ int queue;
+ bool freewheeling;
+ bool running;
+ bool finishing;
+
+ jack_ringbuffer_t* port_add; // snd_seq_addr_t
+ jack_ringbuffer_t* port_del; // struct a2j_port*
+ jack_ringbuffer_t* outbound_events; // struct a2j_delivery_event
+ jack_nframes_t cycle_start;
+
+ sem_t output_semaphore;
+
+ struct a2j_stream stream[2];
+
+} alsa_midi_driver_t;
+
+#define NSEC_PER_SEC ((int64_t)1000*1000*1000)
+
+struct a2j_alsa_midi_event
+{
+ int64_t time;
+ int size;
+};
+
+#define MAX_JACKMIDI_EV_SIZE 16
+
+struct a2j_delivery_event
+{
+ struct list_head siblings;
+
+ /* a jack MIDI event, plus the port its destined for: everything
+ the ALSA output thread needs to deliver the event. time is
+ part of the jack_event.
+ */
+ jack_midi_event_t jack_event;
+ jack_nframes_t time; /* realtime, not offset time */
+ struct a2j_port* port;
+ char midistring[MAX_JACKMIDI_EV_SIZE];
+};
+
+void a2j_error (const char* fmt, ...);
+
+#define A2J_DEBUG
+/*#undef A2J_DEBUG*/
+
+#ifdef A2J_DEBUG
+extern bool a2j_do_debug;
+extern void _a2j_debug (const char* fmt, ...);
+#define a2j_debug(fmt, ...) if (a2j_do_debug) { _a2j_debug ((fmt), ##__VA_ARGS__); }
+#else
+#define a2j_debug(fmt,...)
+#endif
+
+#endif /* __jack_alsa_midi_h__ */
diff --git a/drivers/alsa_midi/alsa_midi.c b/drivers/alsa_midi/alsa_midi.c
new file mode 100644
index 0000000..b0a8cc8
--- /dev/null
+++ b/drivers/alsa_midi/alsa_midi.c
@@ -0,0 +1,864 @@
+/* -*- Mode: C ; c-basic-offset: 2 -*- */
+/*
+ * ALSA SEQ < - > JACK MIDI bridge
+ *
+ * Copyright (c) 2006,2007 Dmitry S. Baikov <c0ff@konstruktiv.org>
+ * Copyright (c) 2007,2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
+ * Copyright (c) 2009,2010,2013 Paul Davis <paul@linuxaudiosystems.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdbool.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <alsa/asoundlib.h>
+#include <jack/jack.h>
+#include <jack/midiport.h>
+#include <jack/ringbuffer.h>
+
+#include "list.h"
+#include "a2j.h"
+#include "port_hash.h"
+#include "port.h"
+#include "port_thread.h"
+
+#ifdef A2J_DEBUG
+bool a2j_do_debug = false;
+
+void
+_a2j_debug (const char* fmt, ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ fputc ('\n', stdout);
+}
+#endif
+
+void
+a2j_error (const char* fmt, ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ vfprintf (stdout, fmt, ap);
+ fputc ('\n', stdout);
+}
+
+static bool
+a2j_stream_init(alsa_midi_driver_t* driver, int which)
+{
+ struct a2j_stream *str = &driver->stream[which];
+
+ str->new_ports = jack_ringbuffer_create (MAX_PORTS * sizeof(struct a2j_port *));
+ if (str->new_ports == NULL) {
+ return false;
+ }
+
+ snd_midi_event_new (MAX_EVENT_SIZE, &str->codec);
+ INIT_LIST_HEAD (&str->list);
+
+ return true;
+}
+
+static void
+a2j_stream_detach (struct a2j_stream * stream_ptr)
+{
+ struct a2j_port * port_ptr;
+ struct list_head * node_ptr;
+
+ if (!stream_ptr) {
+ return;
+ }
+
+ while (!list_empty (&stream_ptr->list)) {
+ node_ptr = stream_ptr->list.next;
+ list_del (node_ptr);
+ port_ptr = list_entry (node_ptr, struct a2j_port, siblings);
+ a2j_debug ("port deleted: %s", port_ptr->name);
+ a2j_port_free (port_ptr);
+ }
+}
+
+static
+void
+a2j_stream_close (alsa_midi_driver_t* driver, int which)
+{
+ struct a2j_stream *str = &driver->stream[which];
+
+ if (!str) {
+ return;
+ }
+
+ if (str->codec)
+ snd_midi_event_free (str->codec);
+ if (str->new_ports)
+ jack_ringbuffer_free (str->new_ports);
+}
+
+static void
+stop_threads (alsa_midi_driver_t* driver)
+{
+ if (driver->running) {
+ void* thread_status;
+
+ driver->running = false; /* tell alsa io thread to stop, whenever they wake up */
+ /* do something that we need to do anyway and will wake the io thread, then join */
+ snd_seq_disconnect_from (driver->seq, driver->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE);
+ a2j_debug ("wait for ALSA input thread\n");
+ pthread_join (driver->alsa_input_thread, &thread_status);
+ a2j_debug ("input thread done\n");
+
+ /* wake output thread and join */
+ sem_post (&driver->output_semaphore);
+ pthread_join (driver->alsa_output_thread, &thread_status);
+ a2j_debug ("output thread done\n");
+ }
+}
+
+/*
+ * =================== Input/output port handling =========================
+ */
+
+void a2j_add_ports (struct a2j_stream * str)
+{
+ struct a2j_port * port_ptr;
+ while (jack_ringbuffer_read (str->new_ports, (char *)&port_ptr, sizeof(port_ptr))) {
+ a2j_debug("jack: inserted port %s", port_ptr->name);
+ a2j_port_insert (str->port_hash, port_ptr);
+ }
+}
+
+static
+void
+a2j_port_event (alsa_midi_driver_t* driver, snd_seq_event_t * ev)
+{
+ const snd_seq_addr_t addr = ev->data.addr;
+
+ if (addr.client == driver->client_id)
+ return;
+
+ if (ev->type == SND_SEQ_EVENT_PORT_START || ev->type == SND_SEQ_EVENT_PORT_CHANGE) {
+ if (jack_ringbuffer_write_space(driver->port_add) >= sizeof(addr)) {
+ a2j_debug("port_event: add/change %d:%d", addr.client, addr.port);
+ jack_ringbuffer_write(driver->port_add, (char*)&addr, sizeof(addr));
+ } else {
+ a2j_error("dropping port_event: add/change %d:%d", addr.client, addr.port);
+ }
+ } else if (ev->type == SND_SEQ_EVENT_PORT_EXIT) {
+ a2j_debug("port_event: del %d:%d", addr.client, addr.port);
+ a2j_port_setdead(driver->stream[A2J_PORT_CAPTURE].port_hash, addr);
+ a2j_port_setdead(driver->stream[A2J_PORT_PLAYBACK].port_hash, addr);
+ }
+}
+
+/* --- INBOUND FROM ALSA TO JACK ---- */
+
+static void
+a2j_input_event (alsa_midi_driver_t* driver, snd_seq_event_t * alsa_event)
+{
+ jack_midi_data_t data[MAX_EVENT_SIZE];
+ struct a2j_stream *str = &driver->stream[A2J_PORT_CAPTURE];
+ long size;
+ struct a2j_port *port;
+ jack_nframes_t now;
+
+ now = jack_frame_time (driver->jack_client);
+
+ if ((port = a2j_port_get(str->port_hash, alsa_event->source)) == NULL) {
+ return;
+ }
+
+ /*
+ * RPNs, NRPNs, Bank Change, etc. need special handling
+ * but seems, ALSA does it for us already.
+ */
+ snd_midi_event_reset_decode(str->codec);
+ if ((size = snd_midi_event_decode(str->codec, data, sizeof(data), alsa_event))<0) {
+ return;
+ }
+
+ // fixup NoteOn with vel 0
+ if ((data[0] & 0xF0) == 0x90 && data[2] == 0x00) {
+ data[0] = 0x80 + (data[0] & 0x0F);
+ data[2] = 0x40;
+ }
+
+ a2j_debug("input: %d bytes at event_frame=%u", (int)size, now);
+
+ if (jack_ringbuffer_write_space(port->inbound_events) >= (sizeof(struct a2j_alsa_midi_event) + size)) {
+ struct a2j_alsa_midi_event ev;
+ char *ev_charp = (char*) &ev;
+ size_t limit;
+ size_t to_write = sizeof(ev);
+
+ jack_ringbuffer_data_t vec[2];
+ jack_ringbuffer_get_write_vector( port->inbound_events, vec );
+ ev.time = now;
+ ev.size = size;
+
+
+ limit = (to_write > vec[0].len ? vec[0].len : to_write);
+ if (limit) {
+ memcpy( vec[0].buf, ev_charp, limit );
+ to_write -= limit;
+ ev_charp += limit;
+ vec[0].buf += limit;
+ vec[0].len -= limit;
+ }
+ if (to_write) {
+ memcpy( vec[1].buf, ev_charp, to_write );
+ vec[1].buf += to_write;
+ vec[1].len -= to_write;
+ }
+
+ to_write = size;
+ ev_charp = (char *)data;
+ limit = (to_write > vec[0].len ? vec[0].len : to_write);
+ if (limit) {
+ memcpy (vec[0].buf, ev_charp, limit);
+ }
+ to_write -= limit;
+ ev_charp += limit;
+ if (to_write) {
+ memcpy (vec[1].buf, ev_charp, to_write);
+ }
+
+ jack_ringbuffer_write_advance( port->inbound_events, sizeof(ev) + size );
+ } else {
+ a2j_error ("MIDI data lost (incoming event buffer full): %ld bytes lost", size);
+ }
+
+}
+
+static int
+a2j_process_incoming (alsa_midi_driver_t* driver, struct a2j_port* port, jack_nframes_t nframes)
+{
+ jack_nframes_t one_period;
+ struct a2j_alsa_midi_event ev;
+ char *ev_buf;
+
+ /* grab data queued by the ALSA input thread and write it into the JACK
+ port buffer. it will delivered during the JACK period that this
+ function is called from.
+ */
+
+ /* first clear the JACK port buffer in preparation for new data
+ */
+
+ a2j_debug ("PORT: %s process input", jack_port_name (port->jack_port));
+
+ jack_midi_clear_buffer (port->jack_buf);
+
+ one_period = jack_get_buffer_size (driver->jack_client);
+
+ while (jack_ringbuffer_peek (port->inbound_events, (char*)&ev, sizeof(ev) ) == sizeof(ev) ) {
+
+ jack_midi_data_t* buf;
+ jack_nframes_t offset;
+
+ a2j_debug ("Seen inbound event from read callback\n");
+
+ if (ev.time >= driver->cycle_start) {
+ a2j_debug ("event is too late\n");
+ break;
+ }
+
+ //jack_ringbuffer_read_advance (port->inbound_events, sizeof (ev));
+ ev_buf = (char *) alloca( sizeof(ev) + ev.size );
+
+ if (jack_ringbuffer_peek (port->inbound_events, ev_buf, sizeof(ev) + ev.size ) != sizeof(ev) + ev.size) {
+ break;
+ }
+
+ offset = driver->cycle_start - ev.time;
+ if (offset > one_period) {
+ /* from a previous cycle, somehow. cram it in at the front */
+ offset = 0;
+ } else {
+ /* offset from start of the current cycle */
+ offset = one_period - offset;
+ }
+
+ a2j_debug ("event at %d offset %d", ev.time, offset);
+
+ /* make sure there is space for it */
+
+ buf = jack_midi_event_reserve (port->jack_buf, offset, ev.size);
+
+ if (buf) {
+ /* grab the event */
+ memcpy( buf, ev_buf + sizeof(ev), ev.size );
+ } else {
+ /* throw it away (no space) */
+ a2j_error ("threw away MIDI event - not reserved at time %d", ev.time);
+ }
+ jack_ringbuffer_read_advance (port->inbound_events, sizeof(ev) + ev.size);
+
+ a2j_debug("input on %s: sucked %d bytes from inbound at %d", jack_port_name (port->jack_port), ev.size, ev.time);
+ }
+
+ return 0;
+}
+
+void*
+alsa_input_thread (void* arg)
+{
+ alsa_midi_driver_t* driver = arg;
+ int npfd;
+ struct pollfd * pfd;
+ snd_seq_addr_t addr;
+ snd_seq_client_info_t * client_info;
+ snd_seq_port_info_t * port_info;
+ bool initial;
+ snd_seq_event_t * event;
+ int ret;
+
+ npfd = snd_seq_poll_descriptors_count(driver->seq, POLLIN);
+ pfd = (struct pollfd *)alloca(npfd * sizeof(struct pollfd));
+ snd_seq_poll_descriptors(driver->seq, pfd, npfd, POLLIN);
+
+ initial = true;
+
+ while (driver->running) {
+ if ((ret = poll(pfd, npfd, 1000)) > 0) {
+
+ while (snd_seq_event_input (driver->seq, &event) > 0) {
+ if (initial) {
+ snd_seq_client_info_alloca(&client_info);
+ snd_seq_port_info_alloca(&port_info);
+ snd_seq_client_info_set_client(client_info, -1);
+ while (snd_seq_query_next_client(driver->seq, client_info) >= 0) {
+ addr.client = snd_seq_client_info_get_client(client_info);
+ if (addr.client == SND_SEQ_CLIENT_SYSTEM || addr.client == driver->client_id) {
+ continue;
+ }
+ snd_seq_port_info_set_client(port_info, addr.client);
+ snd_seq_port_info_set_port(port_info, -1);
+ while (snd_seq_query_next_port(driver->seq, port_info) >= 0) {
+ addr.port = snd_seq_port_info_get_port(port_info);
+ a2j_update_port(driver, addr, port_info);
+ }
+ }
+
+ initial = false;
+ }
+
+ if (event->source.client == SND_SEQ_CLIENT_SYSTEM) {
+ a2j_port_event(driver, event);
+ } else {
+ a2j_input_event(driver, event);
+ }
+
+ snd_seq_free_event (event);
+ }
+ }
+ }
+
+ return (void*) 0;
+}
+
+/* --- OUTBOUND FROM JACK TO ALSA ---- */
+
+int
+a2j_process_outgoing (
+ alsa_midi_driver_t* driver,
+ struct a2j_port * port)
+{
+ /* collect data from JACK port buffer and queue it for delivery by ALSA output thread */
+
+ int nevents;
+ jack_ringbuffer_data_t vec[2];
+ int i;
+ int written = 0;
+ size_t limit;
+ struct a2j_delivery_event* dev;
+ size_t gap = 0;
+
+ jack_ringbuffer_get_write_vector (driver->outbound_events, vec);
+
+ dev = (struct a2j_delivery_event*) vec[0].buf;
+ limit = vec[0].len / sizeof (struct a2j_delivery_event);
+ nevents = jack_midi_get_event_count (port->jack_buf);
+
+ for (i = 0; (i < nevents) && (written < limit); ++i) {
+
+ jack_midi_event_get (&dev->jack_event, port->jack_buf, i);
+ if (dev->jack_event.size <= MAX_JACKMIDI_EV_SIZE)
+ {
+ dev->time = dev->jack_event.time;
+ dev->port = port;
+ memcpy( dev->midistring, dev->jack_event.buffer, dev->jack_event.size );
+ written++;
+ ++dev;
+ }
+ }
+
+ /* anything left? use the second part of the vector, as much as possible */
+
+ if (i < nevents)
+ {
+ if (vec[0].len)
+ {
+ gap = vec[0].len - written * sizeof(struct a2j_delivery_event);
+ }
+
+ dev = (struct a2j_delivery_event*) vec[1].buf;
+
+ limit += (vec[1].len / sizeof (struct a2j_delivery_event));
+
+ while ((i < nevents) && (written < limit))
+ {
+ jack_midi_event_get(&dev->jack_event, port->jack_buf, i);
+ if (dev->jack_event.size <= MAX_JACKMIDI_EV_SIZE)
+ {
+ dev->time = dev->jack_event.time;
+ dev->port = port;
+ memcpy(dev->midistring, dev->jack_event.buffer, dev->jack_event.size);
+ written++;
+ ++dev;
+ }
+ ++i;
+ }
+ }
+
+ a2j_debug( "done pushing events: %d ... gap: %d ", (int)written, (int)gap );
+ /* clear JACK port buffer; advance ring buffer ptr */
+
+ jack_ringbuffer_write_advance (driver->outbound_events, written * sizeof (struct a2j_delivery_event) + gap);
+
+ return nevents;
+}
+
+static int
+time_sorter (struct a2j_delivery_event * a, struct a2j_delivery_event * b)
+{
+ if (a->time < b->time) {
+ return -1;
+ } else if (a->time > b->time) {
+ return 1;
+ }
+ return 0;
+}
+
+static void*
+alsa_output_thread(void * arg)
+{
+ alsa_midi_driver_t * driver = (alsa_midi_driver_t*) arg;
+ struct a2j_stream *str = &driver->stream[A2J_PORT_PLAYBACK];
+ int i;
+ struct list_head evlist;
+ struct list_head * node_ptr;
+ jack_ringbuffer_data_t vec[2];
+ snd_seq_event_t alsa_event;
+ struct a2j_delivery_event* ev;
+ float sr;
+ jack_nframes_t now;
+ int err;
+ int limit;
+
+ while (driver->running) {
+ /* first, make a list of all events in the outbound_events FIFO */
+
+ INIT_LIST_HEAD(&evlist);
+
+ jack_ringbuffer_get_read_vector (driver->outbound_events, vec);
+
+ a2j_debug ("output thread: got %d+%d events",
+ (vec[0].len / sizeof (struct a2j_delivery_event)),
+ (vec[1].len / sizeof (struct a2j_delivery_event)));
+
+ ev = (struct a2j_delivery_event*) vec[0].buf;
+ limit = vec[0].len / sizeof (struct a2j_delivery_event);
+ for (i = 0; i < limit; ++i) {
+ list_add_tail(&ev->siblings, &evlist);
+ ev++;
+ }
+
+ ev = (struct a2j_delivery_event*) vec[1].buf;
+ limit = vec[1].len / sizeof (struct a2j_delivery_event);
+ for (i = 0; i < limit; ++i) {
+ list_add_tail(&ev->siblings, &evlist);
+ ev++;
+ }
+
+ if (vec[0].len < sizeof(struct a2j_delivery_event) && (vec[1].len == 0)) {
+ /* no events: wait for some */
+ a2j_debug ("output thread: wait for events");
+ sem_wait (&driver->output_semaphore);
+ a2j_debug ("output thread: AWAKE ... loop back for events");
+ continue;
+ }
+
+ /* now sort this list by time */
+
+ list_sort(&evlist, struct a2j_delivery_event, siblings, time_sorter);
+
+ /* now deliver */
+
+ sr = jack_get_sample_rate (driver->jack_client);
+
+ list_for_each(node_ptr, &evlist)
+ {
+ ev = list_entry(node_ptr, struct a2j_delivery_event, siblings);
+
+ snd_seq_ev_clear(&alsa_event);
+ snd_midi_event_reset_encode(str->codec);
+ if (!snd_midi_event_encode(str->codec, (const unsigned char *)ev->midistring, ev->jack_event.size, &alsa_event))
+ {
+ continue; // invalid event
+ }
+
+ snd_seq_ev_set_source(&alsa_event, driver->port_id);
+ snd_seq_ev_set_dest(&alsa_event, ev->port->remote.client, ev->port->remote.port);
+ snd_seq_ev_set_direct (&alsa_event);
+
+ now = jack_frame_time (driver->jack_client);
+
+ ev->time += driver->cycle_start;
+
+ a2j_debug ("@ %d, next event @ %d", now, ev->time);
+
+ /* do we need to wait a while before delivering? */
+
+ if (ev->time > now) {
+ struct timespec nanoseconds;
+ jack_nframes_t sleep_frames = ev->time - now;
+ float seconds = sleep_frames / sr;
+
+ /* if the gap is long enough, sleep */
+
+ if (seconds > 0.001) {
+ nanoseconds.tv_sec = (time_t) seconds;
+ nanoseconds.tv_nsec = (long) NSEC_PER_SEC * (seconds - nanoseconds.tv_sec);
+
+ a2j_debug ("output thread sleeps for %.2f msec", ((double) nanoseconds.tv_nsec / NSEC_PER_SEC) * 1000.0);
+
+ if (nanosleep (&nanoseconds, NULL) < 0) {
+ fprintf (stderr, "BAD SLEEP\n");
+ /* do something ? */
+ }
+ }
+ }
+
+ /* its time to deliver */
+ err = snd_seq_event_output(driver->seq, &alsa_event);
+ snd_seq_drain_output (driver->seq);
+ now = jack_frame_time (driver->jack_client);
+ a2j_debug("alsa_out: written %d bytes to %s at %d, DELTA = %d", ev->jack_event.size, ev->port->name, now,
+ (int32_t) (now - ev->time));
+ }
+
+ /* free up space in the FIFO */
+
+ jack_ringbuffer_read_advance (driver->outbound_events, vec[0].len + vec[1].len);
+
+ /* and head back for more */
+ }
+
+ return (void*) 0;
+}
+
+/** CORE JACK PROCESSING */
+
+
+/* ALSA */
+
+static void
+a2j_jack_process_internal (alsa_midi_driver_t* driver, int dir, jack_nframes_t nframes)
+{
+ struct a2j_stream * stream_ptr;
+ int i;
+ struct a2j_port ** port_ptr_ptr;
+ struct a2j_port * port_ptr;
+ int nevents = 0;
+
+ stream_ptr = &driver->stream[dir];
+ a2j_add_ports(stream_ptr);
+
+ // process ports
+ for (i = 0 ; i < PORT_HASH_SIZE ; i++)
+ {
+ port_ptr_ptr = &stream_ptr->port_hash[i];
+ while (*port_ptr_ptr != NULL)
+ {
+ port_ptr = *port_ptr_ptr;
+
+ if (!port_ptr->is_dead) {
+ port_ptr->jack_buf = jack_port_get_buffer(port_ptr->jack_port, nframes);
+
+ if (dir == A2J_PORT_CAPTURE) {
+ a2j_process_incoming (driver, port_ptr, nframes);
+ } else {
+ nevents += a2j_process_outgoing (driver, port_ptr);
+ }
+
+ } else if (jack_ringbuffer_write_space (driver->port_del) >= sizeof(port_ptr)) {
+
+ a2j_debug("jack: removed port %s", port_ptr->name);
+ *port_ptr_ptr = port_ptr->next;
+ jack_ringbuffer_write(driver->port_del, (char*)&port_ptr, sizeof(port_ptr));
+ continue;
+
+ }
+
+ port_ptr_ptr = &port_ptr->next;
+ }
+ }
+
+ if (dir == A2J_PORT_PLAYBACK && nevents > 0) {
+ int sv;
+
+ /* if we queued up anything for output, tell the output thread in
+ case its waiting for us.
+ */
+
+ sem_getvalue (&driver->output_semaphore, &sv);
+ sem_post (&driver->output_semaphore);
+ }
+}
+
+/* JACK DRIVER FUNCTIONS */
+
+static int
+alsa_midi_read (alsa_midi_driver_t* driver, jack_nframes_t nframes)
+{
+ driver->cycle_start = jack_last_frame_time (driver->jack_client);
+ a2j_jack_process_internal (driver, A2J_PORT_CAPTURE, nframes);
+ return 0;
+}
+
+static int
+alsa_midi_write (alsa_midi_driver_t* driver, jack_nframes_t nframes)
+{
+ driver->cycle_start = jack_last_frame_time (driver->jack_client);
+ a2j_jack_process_internal (driver, A2J_PORT_PLAYBACK, nframes);
+ return 0;
+}
+
+
+static int
+alsa_midi_start (alsa_midi_driver_t* driver)
+{
+ int error;
+
+ snd_seq_start_queue (driver->seq, driver->queue, 0);
+ snd_seq_drop_input (driver->seq);
+
+ a2j_add_ports(&driver->stream[A2J_PORT_CAPTURE]);
+ a2j_add_ports(&driver->stream[A2J_PORT_PLAYBACK]);
+
+ driver->running = true;
+
+ if (pthread_create(&driver->alsa_input_thread, NULL, alsa_input_thread, driver) < 0) {
+ a2j_error("cannot start ALSA input thread");
+ return -1;
+ }
+
+ /* wake the poll loop in the alsa input thread so initial ports are fetched */
+ if ((error = snd_seq_connect_from (driver->seq, driver->port_id, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE)) < 0) {
+ a2j_error("snd_seq_connect_from() failed");
+ return -1;
+ }
+
+ if (pthread_create(&driver->alsa_output_thread, NULL, alsa_output_thread, driver) < 0) {
+ a2j_error("cannot start ALSA input thread");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+alsa_midi_stop (alsa_midi_driver_t* driver)
+{
+ (void) snd_seq_stop_queue (driver->seq, driver->queue, 0);
+ return 0;
+}
+
+static int
+alsa_midi_attach (alsa_midi_driver_t* driver, jack_engine_t* engine)
+{
+ int error;
+
+ driver->port_add = jack_ringbuffer_create (2 * MAX_PORTS * sizeof(snd_seq_addr_t));
+
+ if (driver->port_add == NULL) {
+ return -1;
+
+ }
+
+ driver->port_del = jack_ringbuffer_create(2 * MAX_PORTS * sizeof(struct a2j_port *));
+ if (driver->port_del == NULL) {
+ return -1;
+ }
+
+ driver->outbound_events = jack_ringbuffer_create (MAX_EVENT_SIZE * 16 * sizeof(struct a2j_delivery_event));
+ if (driver->outbound_events == NULL) {
+ return -1;
+ }
+
+ if (!a2j_stream_init (driver, A2J_PORT_CAPTURE)) {
+ return -1;
+ }
+
+ if (!a2j_stream_init (driver, A2J_PORT_PLAYBACK)) {
+ return -1;
+ }
+
+ if ((error = snd_seq_open(&driver->seq, "hw", SND_SEQ_OPEN_DUPLEX, 0)) < 0) {
+ a2j_error("failed to open alsa seq");
+ return -1;
+ }
+
+ if ((error = snd_seq_set_client_name(driver->seq, "jackmidi")) < 0) {
+ a2j_error("snd_seq_set_client_name() failed");
+ return -1;
+ }
+
+ if ((driver->port_id = snd_seq_create_simple_port(
+ driver->seq,
+ "port",
+ SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE
+#ifndef DEBUG
+ |SND_SEQ_PORT_CAP_NO_EXPORT
+#endif
+ ,SND_SEQ_PORT_TYPE_APPLICATION)) < 0) {
+
+ a2j_error("snd_seq_create_simple_port() failed");
+ return -1;
+ }
+
+ if ((driver->client_id = snd_seq_client_id(driver->seq)) < 0) {
+ a2j_error("snd_seq_client_id() failed");
+ return -1;
+ }
+
+ if ((driver->queue = snd_seq_alloc_queue(driver->seq)) < 0) {
+ a2j_error("snd_seq_alloc_queue() failed");
+ return -1;
+ }
+
+ if ((error = snd_seq_nonblock(driver->seq, 1)) < 0) {
+ a2j_error("snd_seq_nonblock() failed");
+ return -1;
+ }
+
+ return jack_activate (driver->jack_client);
+}
+
+static int
+alsa_midi_detach (alsa_midi_driver_t* driver, jack_engine_t* engine)
+{
+ driver->finishing = true;
+
+ stop_threads (driver);
+ snd_seq_close (driver->seq);
+ driver->seq = NULL;
+ return 0;
+}
+
+static jack_driver_t *
+alsa_midi_driver_new (jack_client_t *client, const char *name)
+{
+ alsa_midi_driver_t* driver = calloc(1, sizeof(alsa_midi_driver_t));
+
+ jack_info ("creating alsa_midi driver ...");
+
+ if (!driver) {
+ return NULL;
+ }
+
+ jack_driver_init ((jack_driver_t *) driver);
+
+ driver->attach = (JackDriverAttachFunction) alsa_midi_attach;
+ driver->detach = (JackDriverDetachFunction) alsa_midi_detach;
+ driver->read = (JackDriverReadFunction) alsa_midi_read;
+ driver->write = (JackDriverWriteFunction) alsa_midi_write;
+ driver->start = (JackDriverStartFunction) alsa_midi_start;
+ driver->stop = (JackDriverStartFunction) alsa_midi_stop;
+
+ driver->jack_client = client;
+
+ if (sem_init(&driver->output_semaphore, 0, 0) < 0) {
+ a2j_error ("can't create IO semaphore");
+ free (driver);
+ return NULL;
+ }
+
+ return (jack_driver_t *) driver;
+}
+
+static void
+alsa_midi_driver_delete (alsa_midi_driver_t* driver)
+{
+ a2j_stream_detach (&driver->stream[A2J_PORT_CAPTURE]);
+ a2j_stream_detach (&driver->stream[A2J_PORT_PLAYBACK]);
+ a2j_stream_close (driver, A2J_PORT_CAPTURE);
+ a2j_stream_close (driver, A2J_PORT_PLAYBACK);
+
+ sem_destroy (&driver->output_semaphore);
+
+ jack_ringbuffer_free (driver->outbound_events);
+ jack_ringbuffer_free (driver->port_add);
+ jack_ringbuffer_free (driver->port_del);
+}
+
+/* DRIVER "PLUGIN" INTERFACE */
+
+const char driver_client_name[] = "alsa_midi";
+
+const jack_driver_desc_t *
+driver_get_descriptor ()
+{
+ jack_driver_desc_t * desc;
+ jack_driver_param_desc_t * params;
+ //unsigned int i;
+
+ desc = calloc (1, sizeof (jack_driver_desc_t));
+
+ strcpy (desc->name,"alsa_midi");
+ desc->nparams = 0;
+
+ params = calloc (desc->nparams, sizeof (jack_driver_param_desc_t));
+
+ desc->params = params;
+
+ return desc;
+}
+
+jack_driver_t *
+driver_initialize (jack_client_t *client, const JSList * params)
+{
+ const JSList * node;
+ const jack_driver_param_t * param;
+
+ for (node = params; node; node = jack_slist_next (node)) {
+ param = (const jack_driver_param_t *) node->data;
+
+ switch (param->character) {
+ default:
+ break;
+ }
+ }
+
+ return alsa_midi_driver_new (client, NULL);
+}
+
+void
+driver_finish (jack_driver_t *driver)
+{
+ alsa_midi_driver_delete ((alsa_midi_driver_t *) driver);
+}
diff --git a/drivers/alsa_midi/alsa_midi_driver.c b/drivers/alsa_midi/alsa_midi_driver.c
new file mode 100644
index 0000000..e25479f
--- /dev/null
+++ b/drivers/alsa_midi/alsa_midi_driver.c
@@ -0,0 +1,48 @@
+
+#include "alsa_midi.h"
+#include <string.h>
+
+static int
+alsa_midi_driver_attach( alsa_midi_driver_t *driver, jack_engine_t *engine )
+{
+ return driver->midi->attach(driver->midi);
+}
+
+static int
+alsa_midi_driver_detach( alsa_midi_driver_t *driver, jack_engine_t *engine )
+{
+ return driver->midi->detach(driver->midi);
+}
+
+static int
+alsa_midi_driver_read( alsa_midi_driver_t *driver, jack_nframes_t nframes )
+{
+ driver->midi->read(driver->midi, nframes);
+ return 0;
+}
+
+static int
+alsa_midi_driver_write( alsa_midi_driver_t *driver, jack_nframes_t nframes )
+{
+ driver->midi->write(driver->midi, nframes);
+ return 0;
+}
+
+static int
+alsa_midi_driver_start( alsa_midi_driver_t *driver )
+{
+ return driver->midi->start(driver->midi);
+}
+
+static int
+alsa_midi_driver_stop( alsa_midi_driver_t *driver )
+{
+ return driver->midi->stop(driver->midi);
+}
+
+static void
+alsa_midi_driver_delete( alsa_midi_driver_t *driver )
+{
+
+}
+
diff --git a/drivers/alsa_midi/list.c b/drivers/alsa_midi/list.c
new file mode 100644
index 0000000..438066c
--- /dev/null
+++ b/drivers/alsa_midi/list.c
@@ -0,0 +1,147 @@
+/* -*- Mode: C ; c-basic-offset: 2 -*- */
+/*****************************************************************************
+ *
+ * list_sort() adapted from linux kernel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *****************************************************************************/
+
+#include <assert.h>
+
+#include "list.h"
+
+/* list sort from Mark J Roberts (mjr@znex.org) */
+void
+__list_sort(
+ struct list_head *head,
+ int member_offset,
+ int (*cmp)(void * a, void * b))
+{
+ struct list_head *p, *q, *e, *list, *tail, *oldhead;
+ int insize, nmerges, psize, qsize, i;
+
+ list = head->next;
+ list_del(head);
+ insize = 1;
+ for (;;) {
+ p = oldhead = list;
+ list = tail = NULL;
+ nmerges = 0;
+
+ while (p) {
+ nmerges++;
+ q = p;
+ psize = 0;
+ for (i = 0; i < insize; i++) {
+ psize++;
+ q = q->next == oldhead ? NULL : q->next;
+ if (!q)
+ break;
+ }
+
+ qsize = insize;
+ while (psize > 0 || (qsize > 0 && q)) {
+ if (!psize) {
+ e = q;
+ q = q->next;
+ qsize--;
+ if (q == oldhead)
+ q = NULL;
+ } else if (!qsize || !q) {
+ e = p;
+ p = p->next;
+ psize--;
+ if (p == oldhead)
+ p = NULL;
+ } else if (cmp((void *)p - member_offset, (void *)q - member_offset) <= 0) {
+ e = p;
+ p = p->next;
+ psize--;
+ if (p == oldhead)
+ p = NULL;
+ } else {
+ e = q;
+ q = q->next;
+ qsize--;
+ if (q == oldhead)
+ q = NULL;
+ }
+ if (tail)
+ tail->next = e;
+ else
+ list = e;
+ e->prev = tail;
+ tail = e;
+ }
+ p = q;
+ }
+
+ tail->next = list;
+ list->prev = tail;
+
+ if (nmerges <= 1)
+ break;
+
+ insize *= 2;
+ }
+
+ head->next = list;
+ head->prev = list->prev;
+ list->prev->next = head;
+ list->prev = head;
+}
+
+struct test_list_el {
+ int value;
+ struct list_head test_list_node;
+};
+
+int test_list_sort_comparator(struct test_list_el * e1, struct test_list_el * e2)
+{
+ return e1->value - e2->value;
+}
+
+void test_list_sort(void)
+{
+ struct list_head test_list;
+ struct test_list_el *el, *next;
+ struct test_list_el te1 = {.value = 1};
+ struct test_list_el te2 = {.value = 2};
+ struct test_list_el te3 = {.value = 3};
+ struct test_list_el te4 = {.value = 4};
+ struct test_list_el te5 = {.value = 5};
+ struct test_list_el te6 = {.value = 6};
+ struct test_list_el te7 = {.value = 7};
+
+ const int expected[] = {1, 2, 3, 4, 5, 6, 7};
+ int i;
+
+ INIT_LIST_HEAD(&test_list);
+ list_add_tail(&te2.test_list_node, &test_list);
+ list_add_tail(&te6.test_list_node, &test_list);
+ list_add_tail(&te4.test_list_node, &test_list);
+ list_add_tail(&te5.test_list_node, &test_list);
+ list_add_tail(&te7.test_list_node, &test_list);
+ list_add_tail(&te1.test_list_node, &test_list);
+ list_add_tail(&te3.test_list_node, &test_list);
+
+ list_sort(&test_list, struct test_list_el, test_list_node, test_list_sort_comparator);
+
+ i = 0;
+ list_for_each_entry_safe(el, next, &test_list, test_list_node) {
+ assert(el->value == expected[i]);
+ i++;
+ }
+}
diff --git a/drivers/alsa_midi/list.h b/drivers/alsa_midi/list.h
new file mode 100644
index 0000000..5b7f4d4
--- /dev/null
+++ b/drivers/alsa_midi/list.h
@@ -0,0 +1,903 @@
+/* -*- Mode: C ; c-basic-offset: 2 -*- */
+/*****************************************************************************
+ *
+ * Linux kernel header adapted for user-mode
+ * The 2.6.17-rt1 version was used.
+ *
+ * Original copyright holders of this code are unknown, they were not
+ * mentioned in the original file.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *****************************************************************************/
+
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#include <stddef.h>
+
+#if !defined(offsetof)
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#define prefetch(x) (x = x)
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1 ((void *) 0x00100100)
+#define LIST_POISON2 ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add_rcu(struct list_head * new,
+ struct list_head * prev, struct list_head * next)
+{
+ new->next = next;
+ new->prev = prev;
+// smp_wmb();
+ next->prev = new;
+ prev->next = new;
+}
+
+/**
+ * list_add_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_rcu(struct list_head *new, struct list_head *head)
+{
+ __list_add_rcu(new, head, head->next);
+}
+
+/**
+ * list_add_tail_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_tail_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_tail_rcu(struct list_head *new,
+ struct list_head *head)
+{
+ __list_add_rcu(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_rcu - deletes entry from list without re-initialization
+ * @entry: the element to delete from the list.
+ *
+ * Note: list_empty on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_del_rcu()
+ * or list_add_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ *
+ * Note that the caller is not permitted to immediately free
+ * the newly deleted entry. Instead, either synchronize_rcu()
+ * or call_rcu() must be used to defer freeing until an RCU
+ * grace period has elapsed.
+ */
+static inline void list_del_rcu(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->prev = LIST_POISON2;
+}
+
+/*
+ * list_replace_rcu - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * The old entry will be replaced with the new entry atomically.
+ */
+static inline void list_replace_rcu(struct list_head *old,
+ struct list_head *new)
+{
+ new->next = old->next;
+ new->prev = old->prev;
+// smp_wmb();
+ new->next->prev = new;
+ new->prev->next = new;
+ old->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is
+ * empty _and_ checks that no other CPU might be
+ * in the process of still modifying either member
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ *
+ * @head: the list to test.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+ struct list_head *next = head->next;
+ return (next == head) && (next == head->prev);
+}
+
+static inline void __list_splice(struct list_head *list,
+ struct list_head *head)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; prefetch(pos->next), pos != (head); \
+ pos = pos->next)
+
+/**
+ * __list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
+ pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ prefetch(pos->member.next), &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member); \
+ prefetch(pos->member.prev), &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use as a start point in
+ * list_for_each_entry_continue
+ * @pos: the type * to use as a start point
+ * @head: the head of the list
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_prepare_entry(pos, head, member) \
+ ((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - iterate over list of given type
+ * continuing after existing point
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_continue(pos, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member); \
+ prefetch(pos->member.next), &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_from - iterate over list of given type
+ * continuing from existing point
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_from(pos, head, member) \
+ for (; prefetch(pos->member.next), &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_continue - iterate over list of given type
+ * continuing after existing point safe against removal of list entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_from - iterate over list of given type
+ * from existing point safe against removal of list entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe_from(pos, n, head, member) \
+ for (n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_reverse - iterate backwards over list of given type safe against
+ * removal of list entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member), \
+ n = list_entry(pos->member.prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+/**
+ * list_for_each_rcu - iterate over an rcu-protected list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_rcu(pos, head) \
+ for (pos = (head)->next; \
+ prefetch(rcu_dereference(pos)->next), pos != (head); \
+ pos = pos->next)
+
+#define __list_for_each_rcu(pos, head) \
+ for (pos = (head)->next; \
+ rcu_dereference(pos) != (head); \
+ pos = pos->next)
+
+/**
+ * list_for_each_safe_rcu - iterate over an rcu-protected list safe
+ * against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_safe_rcu(pos, n, head) \
+ for (pos = (head)->next; \
+ n = rcu_dereference(pos)->next, pos != (head); \
+ pos = n)
+
+/**
+ * list_for_each_entry_rcu - iterate over rcu list of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_entry_rcu(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ prefetch(rcu_dereference(pos)->member.next), \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+
+/**
+ * list_for_each_continue_rcu - iterate over an rcu-protected list
+ * continuing after existing point.
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_continue_rcu(pos, head) \
+ for ((pos) = (pos)->next; \
+ prefetch(rcu_dereference((pos))->next), (pos) != (head); \
+ (pos) = (pos)->next)
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct hlist_node {
+ struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+static inline void INIT_HLIST_NODE(struct hlist_node *h)
+{
+ h->next = NULL;
+ h->pprev = NULL;
+}
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+ return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+ return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+ struct hlist_node *next = n->next;
+ struct hlist_node **pprev = n->pprev;
+ *pprev = next;
+ if (next)
+ next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+ __hlist_del(n);
+ n->next = LIST_POISON1;
+ n->pprev = LIST_POISON2;
+}
+
+/**
+ * hlist_del_rcu - deletes entry from hash list without re-initialization
+ * @n: the element to delete from the hash list.
+ *
+ * Note: list_unhashed() on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the hash list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry().
+ */
+static inline void hlist_del_rcu(struct hlist_node *n)
+{
+ __hlist_del(n);
+ n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+ if (!hlist_unhashed(n)) {
+ __hlist_del(n);
+ INIT_HLIST_NODE(n);
+ }
+}
+
+/*
+ * hlist_replace_rcu - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * The old entry will be replaced with the new entry atomically.
+ */
+static inline void hlist_replace_rcu(struct hlist_node *old,
+ struct hlist_node *new)
+{
+ struct hlist_node *next = old->next;
+
+ new->next = next;
+ new->pprev = old->pprev;
+// smp_wmb();
+ if (next)
+ new->next->pprev = &new->next;
+ *new->pprev = new;
+ old->pprev = LIST_POISON2;
+}
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+ n->next = first;
+ if (first)
+ first->pprev = &n->next;
+ h->first = n;
+ n->pprev = &h->first;
+}
+
+
+/**
+ * hlist_add_head_rcu - adds the specified element to the specified hlist,
+ * while permitting racing traversals.
+ * @n: the element to add to the hash list.
+ * @h: the list to add to.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry_rcu(), used to prevent memory-consistency
+ * problems on Alpha CPUs. Regardless of the type of CPU, the
+ * list-traversal primitive must be guarded by rcu_read_lock().
+ */
+static inline void hlist_add_head_rcu(struct hlist_node *n,
+ struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+ n->next = first;
+ n->pprev = &h->first;
+// smp_wmb();
+ if (first)
+ first->pprev = &n->next;
+ h->first = n;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ n->pprev = next->pprev;
+ n->next = next;
+ next->pprev = &n->next;
+ *(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ next->next = n->next;
+ n->next = next;
+ next->pprev = &n->next;
+
+ if(next->next)
+ next->next->pprev = &next->next;
+}
+
+/**
+ * hlist_add_before_rcu - adds the specified element to the specified hlist
+ * before the specified node while permitting racing traversals.
+ * @n: the new element to add to the hash list.
+ * @next: the existing element to add the new element before.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry_rcu(), used to prevent memory-consistency
+ * problems on Alpha CPUs.
+ */
+static inline void hlist_add_before_rcu(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ n->pprev = next->pprev;
+ n->next = next;
+// smp_wmb();
+ next->pprev = &n->next;
+ *(n->pprev) = n;
+}
+
+/**
+ * hlist_add_after_rcu - adds the specified element to the specified hlist
+ * after the specified node while permitting racing traversals.
+ * @prev: the existing element to add the new element after.
+ * @n: the new element to add to the hash list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry_rcu(), used to prevent memory-consistency
+ * problems on Alpha CPUs.
+ */
+static inline void hlist_add_after_rcu(struct hlist_node *prev,
+ struct hlist_node *n)
+{
+ n->next = prev->next;
+ n->pprev = &prev->next;
+// smp_wmb();
+ prev->next = n;
+ if (n->next)
+ n->next->pprev = &n->next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+ for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+ pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+ pos = n)
+
+/**
+ * hlist_for_each_entry - iterate over list of given type
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member) \
+ for (pos = (pos)->next; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member) \
+ for (; pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @n: another &struct hlist_node to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ n = pos->next; 1; }) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = n)
+
+/**
+ * hlist_for_each_entry_rcu - iterate over rcu list of given type
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as hlist_add_head_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define hlist_for_each_entry_rcu(tpos, pos, head, member) \
+ for (pos = (head)->first; \
+ rcu_dereference(pos) && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+#endif
+
+/**
+ * __list_sort - sort the list using given comparator with merge-sort algorithm
+ * @head: is a head of the list to be sorted
+ * @member_offset: is machine offset inside the list entry structure to the
+ * field of type struct list_head which links that entry with
+ * the list.
+ */
+extern void __list_sort(struct list_head * head,
+ int member_offset,
+ int (*comparator)(void*,void*));
+
+/**
+ * list_sort - wrapper for __list_sort
+ * @head: is a head of the list to be sorted
+ * @type: is the type of list entry
+ * @member: is the name of the field inside entry that links that entry with
+ * other entries in the list.
+ * @comaprator: function comparing two entries, should return value lesser
+ * than 0 when the first argument is lesser than the second one.
+ */
+#define list_sort(head,type,member,comparator) \
+ ({ \
+ __list_sort(head, \
+ offsetof(type, member), \
+ (int (*)(void*, void*)) comparator); \
+ })
+
+void test_list_sort(void);
diff --git a/drivers/alsa_midi/port.c b/drivers/alsa_midi/port.c
new file mode 100644
index 0000000..fac5eee
--- /dev/null
+++ b/drivers/alsa_midi/port.c
@@ -0,0 +1,217 @@
+/*
+ * ALSA SEQ < - > JACK MIDI bridge
+ *
+ * Copyright (c) 2006,2007 Dmitry S. Baikov <c0ff@konstruktiv.org>
+ * Copyright (c) 2007,2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
+ * Copyright (c) 2009,2010 Paul Davis <paul@linuxaudiosystems.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdbool.h>
+#include <ctype.h>
+#include <semaphore.h>
+#include <alsa/asoundlib.h>
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+
+#include "list.h"
+#include "a2j.h"
+#include "port_hash.h"
+#include "port.h"
+
+/* This should be part of JACK API */
+#define JACK_IS_VALID_PORT_NAME_CHAR(c) \
+ (isalnum(c) || \
+ (c) == '/' || \
+ (c) == '_' || \
+ (c) == '(' || \
+ (c) == ')' || \
+ (c) == '-' || \
+ (c) == '[' || \
+ (c) == ']')
+
+static
+int
+a2j_alsa_connect_from (alsa_midi_driver_t * driver, int client, int port)
+{
+ snd_seq_port_subscribe_t* sub;
+ snd_seq_addr_t seq_addr;
+ int err;
+
+ snd_seq_port_subscribe_alloca (&sub);
+ seq_addr.client = client;
+ seq_addr.port = port;
+ snd_seq_port_subscribe_set_sender (sub, &seq_addr);
+ seq_addr.client = driver->client_id;
+ seq_addr.port = driver->port_id;
+ snd_seq_port_subscribe_set_dest (sub, &seq_addr);
+
+ snd_seq_port_subscribe_set_time_update (sub, 1);
+ snd_seq_port_subscribe_set_queue (sub, driver->queue);
+ snd_seq_port_subscribe_set_time_real (sub, 1);
+
+ if ((err = snd_seq_subscribe_port (driver->seq, sub))) {
+ a2j_error ("can't subscribe to %d:%d - %s", client, port, snd_strerror(err));
+ }
+
+ return err;
+}
+
+void
+a2j_port_setdead (a2j_port_hash_t hash, snd_seq_addr_t addr)
+{
+ struct a2j_port *port = a2j_port_get(hash, addr);
+
+ if (port) {
+ port->is_dead = true; // see jack_process_internal
+ } else {
+ a2j_debug("port_setdead: not found (%d:%d)", addr.client, addr.port);
+ }
+}
+
+void
+a2j_port_free (struct a2j_port * port)
+{
+ // snd_seq_disconnect_from (driver->seq, driver->port_id, port->remote.client, port->remote.port);
+ // snd_seq_disconnect_to (driver->seq, driver->port_id, port->remote.client, port->remote.port);
+
+ if (port->inbound_events) {
+ jack_ringbuffer_free (port->inbound_events);
+ }
+
+ if (port->jack_port != JACK_INVALID_PORT && !port->driver_ptr->finishing) {
+ jack_port_unregister (port->driver_ptr->jack_client, port->jack_port);
+ }
+
+ free (port);
+}
+
+void
+a2j_port_fill_name (struct a2j_port * port_ptr, int dir, snd_seq_client_info_t * client_info_ptr,
+ const snd_seq_port_info_t * port_info_ptr, bool make_unique)
+{
+ char *c;
+
+ if (make_unique) {
+ snprintf (port_ptr->name,
+ sizeof(port_ptr->name),
+ "%s [%d] %s %s",
+ snd_seq_client_info_get_name(client_info_ptr),
+ snd_seq_client_info_get_client(client_info_ptr),
+ snd_seq_port_info_get_name(port_info_ptr),
+ (dir == A2J_PORT_CAPTURE ? "in" : "out"));
+ } else {
+ snprintf (port_ptr->name,
+ sizeof(port_ptr->name),
+ "%s %s %s",
+ snd_seq_client_info_get_name(client_info_ptr),
+ snd_seq_port_info_get_name(port_info_ptr),
+ (dir == A2J_PORT_CAPTURE ? "in" : "out"));
+ }
+
+ // replace all offending characters with ' '
+ for (c = port_ptr->name; *c; ++c) {
+ if (!JACK_IS_VALID_PORT_NAME_CHAR(*c)) {
+ *c = ' ';
+ }
+ }
+}
+
+struct a2j_port *
+a2j_port_create (alsa_midi_driver_t * driver, int dir, snd_seq_addr_t addr, const snd_seq_port_info_t * info)
+{
+ struct a2j_port *port;
+ int err;
+ int client;
+ snd_seq_client_info_t * client_info_ptr;
+ int jack_caps;
+ struct a2j_stream * stream_ptr;
+
+ stream_ptr = &driver->stream[dir];
+
+ if ((err = snd_seq_client_info_malloc (&client_info_ptr)) != 0) {
+ a2j_error("Failed to allocate client info");
+ goto fail;
+ }
+
+ client = snd_seq_port_info_get_client (info);
+
+ err = snd_seq_get_any_client_info (driver->seq, client, client_info_ptr);
+ if (err != 0) {
+ a2j_error("Failed to get client info");
+ goto fail_free_client_info;
+ }
+
+ a2j_debug ("client name: '%s'", snd_seq_client_info_get_name(client_info_ptr));
+ a2j_debug ("port name: '%s'", snd_seq_port_info_get_name(info));
+
+ port = calloc (1, sizeof(struct a2j_port));
+ if (!port) {
+ goto fail_free_client_info;
+ }
+
+ port->driver_ptr = driver;
+ port->jack_port = JACK_INVALID_PORT;
+ port->remote = addr;
+
+ a2j_port_fill_name (port, dir, client_info_ptr, info, false);
+
+ /* Add port to list early, before registering to JACK, so map functionality is guaranteed to work during port registration */
+ list_add_tail (&port->siblings, &stream_ptr->list);
+
+ if (dir == A2J_PORT_CAPTURE) {
+ jack_caps = JackPortIsOutput;
+ } else {
+ jack_caps = JackPortIsInput;
+ }
+
+ /* mark anything that looks like a hardware port as physical&terminal */
+ if (snd_seq_port_info_get_type (info) & (SND_SEQ_PORT_TYPE_HARDWARE|SND_SEQ_PORT_TYPE_PORT|SND_SEQ_PORT_TYPE_SPECIFIC)) {
+ jack_caps |= JackPortIsPhysical|JackPortIsTerminal;
+ }
+
+ port->jack_port = jack_port_register (driver->jack_client, port->name, JACK_DEFAULT_MIDI_TYPE, jack_caps, 0);
+ if (port->jack_port == JACK_INVALID_PORT) {
+ a2j_error("jack_port_register() failed for '%s'", port->name);
+ goto fail_free_port;
+ }
+
+ if (dir == A2J_PORT_CAPTURE) {
+ err = a2j_alsa_connect_from (driver, port->remote.client, port->remote.port);
+ } else {
+ err = snd_seq_connect_to (driver->seq, driver->port_id, port->remote.client, port->remote.port);
+ }
+
+ if (err) {
+ a2j_debug("port skipped: %s", port->name);
+ goto fail_free_port;
+ }
+
+ port->inbound_events = jack_ringbuffer_create(MAX_EVENT_SIZE*16);
+
+ a2j_debug("port created: %s", port->name);
+ return port;
+
+ fail_free_port:
+ list_del (&port->siblings);
+
+ a2j_port_free (port);
+
+ fail_free_client_info:
+ snd_seq_client_info_free (client_info_ptr);
+
+ fail:
+ return NULL;
+}
diff --git a/drivers/alsa_midi/port.h b/drivers/alsa_midi/port.h
new file mode 100644
index 0000000..2c8daf4
--- /dev/null
+++ b/drivers/alsa_midi/port.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C ; c-basic-offset: 2 -*- */
+/*
+ * ALSA SEQ < - > JACK MIDI bridge
+ *
+ * Copyright (c) 2006,2007 Dmitry S. Baikov <c0ff@konstruktiv.org>
+ * Copyright (c) 2007,2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PORT_H__757ADD0F_5E53_41F7_8B7F_8119C5E8A9F1__INCLUDED
+#define PORT_H__757ADD0F_5E53_41F7_8B7F_8119C5E8A9F1__INCLUDED
+
+struct a2j_port* a2j_port_create (alsa_midi_driver_t* driver, int dir, snd_seq_addr_t addr, const snd_seq_port_info_t * info);
+void a2j_port_setdead (a2j_port_hash_t hash, snd_seq_addr_t addr);
+void a2j_port_free (struct a2j_port * port);
+
+#endif /* #ifndef PORT_H__757ADD0F_5E53_41F7_8B7F_8119C5E8A9F1__INCLUDED */
diff --git a/drivers/alsa_midi/port_hash.c b/drivers/alsa_midi/port_hash.c
new file mode 100644
index 0000000..cc9c5c6
--- /dev/null
+++ b/drivers/alsa_midi/port_hash.c
@@ -0,0 +1,63 @@
+/* -*- Mode: C ; c-basic-offset: 2 -*- */
+/*
+ * ALSA SEQ < - > JACK MIDI bridge
+ *
+ * Copyright (c) 2006,2007 Dmitry S. Baikov <c0ff@konstruktiv.org>
+ * Copyright (c) 2007,2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdbool.h>
+#include <semaphore.h>
+#include <alsa/asoundlib.h>
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+
+#include "list.h"
+#include "a2j.h"
+#include "port_hash.h"
+
+static inline
+int
+a2j_port_hash(
+ snd_seq_addr_t addr)
+{
+ return (addr.client + addr.port) % PORT_HASH_SIZE;
+}
+
+struct a2j_port *
+a2j_port_get(
+ a2j_port_hash_t hash,
+ snd_seq_addr_t addr)
+{
+ struct a2j_port **pport = &hash[a2j_port_hash(addr)];
+ while (*pport) {
+ struct a2j_port *port = *pport;
+ if (port->remote.client == addr.client && port->remote.port == addr.port)
+ return port;
+ pport = &port->next;
+ }
+ return NULL;
+}
+
+void
+a2j_port_insert(
+ a2j_port_hash_t hash,
+ struct a2j_port * port)
+{
+ struct a2j_port **pport = &hash[a2j_port_hash(port->remote)];
+ port->next = *pport;
+ *pport = port;
+}
diff --git a/drivers/alsa_midi/port_hash.h b/drivers/alsa_midi/port_hash.h
new file mode 100644
index 0000000..ec21f11
--- /dev/null
+++ b/drivers/alsa_midi/port_hash.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C ; c-basic-offset: 2 -*- */
+/*
+ * ALSA SEQ < - > JACK MIDI bridge
+ *
+ * Copyright (c) 2006,2007 Dmitry S. Baikov <c0ff@konstruktiv.org>
+ * Copyright (c) 2007,2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PORT_HASH_H__A44CBCD6_E075_49CB_8F73_DF9772511D55__INCLUDED
+#define PORT_HASH_H__A44CBCD6_E075_49CB_8F73_DF9772511D55__INCLUDED
+
+void
+a2j_port_insert(
+ a2j_port_hash_t hash,
+ struct a2j_port * port);
+
+struct a2j_port *
+a2j_port_get(
+ a2j_port_hash_t hash,
+ snd_seq_addr_t addr);
+
+#endif /* #ifndef PORT_HASH_H__A44CBCD6_E075_49CB_8F73_DF9772511D55__INCLUDED */
diff --git a/drivers/alsa_midi/port_thread.c b/drivers/alsa_midi/port_thread.c
new file mode 100644
index 0000000..39b68d7
--- /dev/null
+++ b/drivers/alsa_midi/port_thread.c
@@ -0,0 +1,235 @@
+/* -*- Mode: C ; c-basic-offset: 2 -*- */
+/*
+ * ALSA SEQ < - > JACK MIDI bridge
+ *
+ * Copyright (c) 2006,2007 Dmitry S. Baikov <c0ff@konstruktiv.org>
+ * Copyright (c) 2007,2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdbool.h>
+#include <semaphore.h>
+#include <alsa/asoundlib.h>
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+
+#include "list.h"
+#include "a2j.h"
+#include "port.h"
+#include "port_hash.h"
+#include "port_thread.h"
+
+struct a2j_port *
+a2j_find_port_by_addr(
+ struct a2j_stream * stream_ptr,
+ snd_seq_addr_t addr)
+{
+ struct list_head * node_ptr;
+ struct a2j_port * port_ptr;
+
+ list_for_each(node_ptr, &stream_ptr->list)
+ {
+ port_ptr = list_entry(node_ptr, struct a2j_port, siblings);
+ if (port_ptr->remote.client == addr.client && port_ptr->remote.port == addr.port)
+ {
+ return port_ptr;
+ }
+ }
+
+ return NULL;
+}
+
+struct a2j_port *
+a2j_find_port_by_jack_port_name(
+ struct a2j_stream * stream_ptr,
+ const char * jack_port)
+{
+ struct list_head * node_ptr;
+ struct a2j_port * port_ptr;
+
+ list_for_each(node_ptr, &stream_ptr->list)
+ {
+ port_ptr = list_entry(node_ptr, struct a2j_port, siblings);
+ if (strcmp(port_ptr->name, jack_port) == 0)
+ {
+ return port_ptr;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * ==================== Port add/del handling thread ==============================
+ */
+
+static
+void
+a2j_update_port_type (alsa_midi_driver_t * driver, int dir, snd_seq_addr_t addr, int caps, const snd_seq_port_info_t * info)
+{
+ struct a2j_stream * stream_ptr;
+ int alsa_mask;
+ struct a2j_port * port_ptr;
+
+ a2j_debug("update_port_type(%d:%d)", addr.client, addr.port);
+
+ stream_ptr = &driver->stream[dir];
+ port_ptr = a2j_find_port_by_addr(stream_ptr, addr);
+
+ if (dir == A2J_PORT_CAPTURE) {
+ alsa_mask = SND_SEQ_PORT_CAP_SUBS_READ;
+ } else {
+ alsa_mask = SND_SEQ_PORT_CAP_SUBS_WRITE;
+ }
+
+ if (port_ptr != NULL && (caps & alsa_mask) != alsa_mask) {
+ a2j_debug("setdead: %s", port_ptr->name);
+ port_ptr->is_dead = true;
+ }
+
+ if (port_ptr == NULL && (caps & alsa_mask) == alsa_mask) {
+ if(jack_ringbuffer_write_space(stream_ptr->new_ports) >= sizeof(port_ptr)) {
+ port_ptr = a2j_port_create (driver, dir, addr, info);
+ if (port_ptr != NULL) {
+ jack_ringbuffer_write(stream_ptr->new_ports, (char *)&port_ptr, sizeof(port_ptr));
+ }
+ } else {
+ a2j_error( "dropping new port event... increase MAX_PORTS" );
+ }
+ }
+}
+
+void
+a2j_update_port (alsa_midi_driver_t * driver, snd_seq_addr_t addr, const snd_seq_port_info_t * info)
+{
+ unsigned int port_caps = snd_seq_port_info_get_capability(info);
+ unsigned int port_type = snd_seq_port_info_get_type(info);
+
+ a2j_debug("port %u:%u", addr.client, addr.port);
+ a2j_debug("port type: 0x%08X", port_type);
+ a2j_debug("port caps: 0x%08X", port_caps);
+
+ if (port_type & SND_SEQ_PORT_TYPE_SPECIFIC) {
+ a2j_debug("SPECIFIC");
+ }
+
+ if (port_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) {
+ a2j_debug("MIDI_GENERIC");
+ }
+
+ if (port_type & SND_SEQ_PORT_TYPE_MIDI_GM) {
+ a2j_debug("MIDI_GM");
+ }
+
+ if (port_type & SND_SEQ_PORT_TYPE_MIDI_GS) {
+ a2j_debug("MIDI_GS");
+ }
+
+ if (port_type & SND_SEQ_PORT_TYPE_MIDI_XG) {
+ a2j_debug("MIDI_XG");
+ }
+
+ if (port_type & SND_SEQ_PORT_TYPE_MIDI_MT32) {
+ a2j_debug("MIDI_MT32");
+ }
+
+ if (port_type & SND_SEQ_PORT_TYPE_MIDI_GM2) {
+ a2j_debug("MIDI_GM2");
+ }
+
+ if (port_type & SND_SEQ_PORT_TYPE_SYNTH) {
+ a2j_debug("SYNTH");
+ }
+
+ if (port_type & SND_SEQ_PORT_TYPE_DIRECT_SAMPLE) {
+ a2j_debug("DIRECT_SAMPLE");
+ }
+
+ if (port_type & SND_SEQ_PORT_TYPE_SAMPLE) {
+ a2j_debug("SAMPLE");
+ }
+
+ if (port_type & SND_SEQ_PORT_TYPE_HARDWARE) {
+ a2j_debug("HARDWARE");
+ }
+
+ if (port_type & SND_SEQ_PORT_TYPE_SOFTWARE) {
+ a2j_debug("SOFTWARE");
+ }
+
+ if (port_type & SND_SEQ_PORT_TYPE_SYNTHESIZER) {
+ a2j_debug("SYNTHESIZER");
+ }
+
+ if (port_type & SND_SEQ_PORT_TYPE_PORT) {
+ a2j_debug("PORT");
+ }
+
+ if (port_type & SND_SEQ_PORT_TYPE_APPLICATION) {
+ a2j_debug("APPLICATION");
+ }
+
+ if (port_type == 0) {
+ a2j_debug("Ignoring port of type 0");
+ return;
+ }
+
+ if (port_caps & SND_SEQ_PORT_CAP_NO_EXPORT) {
+ a2j_debug("Ignoring no-export port");
+ return;
+ }
+
+ a2j_update_port_type (driver, A2J_PORT_CAPTURE, addr, port_caps, info);
+ a2j_update_port_type (driver, A2J_PORT_PLAYBACK, addr, port_caps, info);
+}
+
+void
+a2j_free_ports (jack_ringbuffer_t * ports)
+{
+ struct a2j_port *port;
+ int sz;
+
+ while ((sz = jack_ringbuffer_read (ports, (char*)&port, sizeof(port)))) {
+ assert (sz == sizeof(port));
+ a2j_debug("port deleted: %s", port->name);
+ list_del (&port->siblings);
+ a2j_port_free(port);
+ }
+}
+
+void
+a2j_update_ports (alsa_midi_driver_t * driver)
+{
+ snd_seq_addr_t addr;
+ int size;
+
+ while ((size = jack_ringbuffer_read(driver->port_add, (char *)&addr, sizeof(addr))) != 0) {
+
+ snd_seq_port_info_t * info;
+ int err;
+
+ snd_seq_port_info_alloca(&info);
+
+ assert (size == sizeof(addr));
+ assert (addr.client != driver->client_id);
+
+ if ((err = snd_seq_get_any_port_info(driver->seq, addr.client, addr.port, info)) >= 0) {
+ a2j_update_port(driver, addr, info);
+ } else {
+ a2j_port_setdead(driver->stream[A2J_PORT_CAPTURE].port_hash, addr);
+ a2j_port_setdead(driver->stream[A2J_PORT_PLAYBACK].port_hash, addr);
+ }
+ }
+}
diff --git a/drivers/alsa_midi/port_thread.h b/drivers/alsa_midi/port_thread.h
new file mode 100644
index 0000000..28d68a9
--- /dev/null
+++ b/drivers/alsa_midi/port_thread.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C ; c-basic-offset: 2 -*- */
+/*
+ * ALSA SEQ < - > JACK MIDI bridge
+ *
+ * Copyright (c) 2006,2007 Dmitry S. Baikov <c0ff@konstruktiv.org>
+ * Copyright (c) 2007,2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PORT_THREAD_H__1C6B5065_5556_4AC6_AA9F_44C32A9648C6__INCLUDED
+#define PORT_THREAD_H__1C6B5065_5556_4AC6_AA9F_44C32A9648C6__INCLUDED
+
+void a2j_update_port (alsa_midi_driver_t* driver, snd_seq_addr_t addr, const snd_seq_port_info_t* info);
+void a2j_update_ports (alsa_midi_driver_t* driver);
+void a2j_free_ports (jack_ringbuffer_t * ports);
+struct a2j_port * a2j_find_port_by_addr (struct a2j_stream * stream_ptr, snd_seq_addr_t addr);
+struct a2j_port * a2j_find_port_by_jack_port_name (struct a2j_stream * stream_ptr, const char * jack_port);
+
+#endif /* #ifndef PORT_THREAD_H__1C6B5065_5556_4AC6_AA9F_44C32A9648C6__INCLUDED */
diff --git a/drivers/alsa-midi/Makefile.am b/drivers/am/Makefile.am
index 5c28128..5c28128 100644
--- a/drivers/alsa-midi/Makefile.am
+++ b/drivers/am/Makefile.am
diff --git a/drivers/alsa-midi/alsa_midi.h b/drivers/am/alsa_midi.h
index b49228a..b49228a 100644
--- a/drivers/alsa-midi/alsa_midi.h
+++ b/drivers/am/alsa_midi.h
diff --git a/drivers/alsa-midi/alsa_midi_driver.c b/drivers/am/alsa_midi_driver.c
index b27f03c..b27f03c 100644
--- a/drivers/alsa-midi/alsa_midi_driver.c
+++ b/drivers/am/alsa_midi_driver.c
diff --git a/drivers/alsa-midi/alsa_rawmidi.c b/drivers/am/alsa_rawmidi.c
index 9db97fb..9db97fb 100644
--- a/drivers/alsa-midi/alsa_rawmidi.c
+++ b/drivers/am/alsa_rawmidi.c
diff --git a/drivers/alsa-midi/alsa_seqmidi.c b/drivers/am/alsa_seqmidi.c
index 3351b36..3351b36 100644
--- a/drivers/alsa-midi/alsa_seqmidi.c
+++ b/drivers/am/alsa_seqmidi.c
diff --git a/drivers/alsa-midi/midi_pack.h b/drivers/am/midi_pack.h
index 6fb704b..6fb704b 100644
--- a/drivers/alsa-midi/midi_pack.h
+++ b/drivers/am/midi_pack.h
diff --git a/drivers/alsa-midi/midi_unpack.h b/drivers/am/midi_unpack.h
index c917f4d..c917f4d 100644
--- a/drivers/alsa-midi/midi_unpack.h
+++ b/drivers/am/midi_unpack.h