From 4492cea02fb46305632d19796460b91fd319ec96 Mon Sep 17 00:00:00 2001 From: Hanspeter Portner Date: Sun, 15 Feb 2015 12:48:02 +0100 Subject: [PATCH] [alsa_midi] fix hotplug device (de)enumeration Issues: - With a running JACK with enabled alsa_midi driver (-X alsa_midi), plugging in a new MIDI device has no effect, e.g. no corresponding JACK ports are spawned - With a running JACK with enabled alsa_midi driver (-X alsa_midi), deplugging a MIDI device has no effect, e.g. the corresponding JACK ports stay around Result: - JACK only creates JACK ports of ALSA MIDI clients/ports found at startup - JACK has to be restarted for any ALSA MIDI device (de)enumeration to take place Problem: - There are some functions defined which actually should accomplish this in the alsa_midi driver code (e.g. 'a2j_update_ports' and 'a2j_free_ports'), but they are not called from any other function ;-) Solution: - Discriminate properly between ALSA PORT_START and PORT_CHANGE events - 'a2j_new_ports' function has been added which recycles some code from 'alsa_input_thread' - Actually call the already existing hot(de)plugging infrastructure - 'a2j_update_ports' and 'a2j_new_ports' get called from the 'alsa_input_thread' - 'a2j_free_ports' gets called from 'alsa_output_thread' - 'alsa_out_thread' is woken up by 'a2j_jack_process_internal' - Cleanup code that is not used: - 'port_add' ringbuffer has no function, as 'new_ports' ringbuffer seems to be implemented to accomplish the same Signed-off-by: Hanspeter Portner --- drivers/alsa_midi/a2j.h | 1 - drivers/alsa_midi/alsa_midi.c | 38 +++++++++-------------- drivers/alsa_midi/port_thread.c | 55 ++++++++++++++++++++------------- drivers/alsa_midi/port_thread.h | 5 +-- 4 files changed, 51 insertions(+), 48 deletions(-) diff --git a/drivers/alsa_midi/a2j.h b/drivers/alsa_midi/a2j.h index c4428b3..6696325 100644 --- a/drivers/alsa_midi/a2j.h +++ b/drivers/alsa_midi/a2j.h @@ -88,7 +88,6 @@ typedef struct alsa_midi_driver 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; diff --git a/drivers/alsa_midi/alsa_midi.c b/drivers/alsa_midi/alsa_midi.c index 9e5737c..741b496 100644 --- a/drivers/alsa_midi/alsa_midi.c +++ b/drivers/alsa_midi/alsa_midi.c @@ -44,7 +44,7 @@ _a2j_debug (const char* fmt, ...) va_list ap; va_start (ap, fmt); vfprintf (stderr, fmt, ap); - fputc ('\n', stdout); + fputc ('\n', stderr); } #endif @@ -150,13 +150,12 @@ a2j_port_event (alsa_midi_driver_t* driver, snd_seq_event_t * ev) 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); - } + if (ev->type == SND_SEQ_EVENT_PORT_START) { + a2j_debug("port_event: add %d:%d", addr.client, addr.port); + a2j_new_ports (driver, addr); + } else if (ev->type == SND_SEQ_EVENT_PORT_CHANGE) { + a2j_debug("port_event: change %d:%d", addr.client, addr.port); + a2j_update_ports (driver, addr); } 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); @@ -321,7 +320,6 @@ alsa_input_thread (void* arg) 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; @@ -338,19 +336,14 @@ alsa_input_thread (void* arg) 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); - } + + a2j_new_ports (driver, addr); } initial = false; @@ -470,6 +463,10 @@ alsa_output_thread(void * arg) int limit; while (driver->running) { + /* pre-first, handle port deletion requests */ + + a2j_free_ports(driver); + /* first, make a list of all events in the outbound_events FIFO */ INIT_LIST_HEAD(&evlist); @@ -610,6 +607,7 @@ a2j_jack_process_internal (alsa_midi_driver_t* driver, int dir, jack_nframes_t n 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)); + nevents += 1; /* wake up output thread, see: a2j_free_ports */ continue; } @@ -695,13 +693,6 @@ 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; @@ -813,7 +804,6 @@ alsa_midi_driver_delete (alsa_midi_driver_t* driver) sem_destroy (&driver->output_semaphore); jack_ringbuffer_free (driver->outbound_events); - jack_ringbuffer_free (driver->port_add); jack_ringbuffer_free (driver->port_del); } diff --git a/drivers/alsa_midi/port_thread.c b/drivers/alsa_midi/port_thread.c index 39b68d7..c6741fd 100644 --- a/drivers/alsa_midi/port_thread.c +++ b/drivers/alsa_midi/port_thread.c @@ -196,12 +196,12 @@ a2j_update_port (alsa_midi_driver_t * driver, snd_seq_addr_t addr, const snd_seq } void -a2j_free_ports (jack_ringbuffer_t * ports) +a2j_free_ports (alsa_midi_driver_t * driver) { struct a2j_port *port; int sz; - while ((sz = jack_ringbuffer_read (ports, (char*)&port, sizeof(port)))) { + while ((sz = jack_ringbuffer_read (driver->port_del, (char*)&port, sizeof(port)))) { assert (sz == sizeof(port)); a2j_debug("port deleted: %s", port->name); list_del (&port->siblings); @@ -210,26 +210,39 @@ a2j_free_ports (jack_ringbuffer_t * ports) } void -a2j_update_ports (alsa_midi_driver_t * driver) +a2j_update_ports (alsa_midi_driver_t * driver, snd_seq_addr_t addr) { - snd_seq_addr_t addr; - int size; + snd_seq_port_info_t * info; + int err; - 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); - } + assert (addr.client != driver->client_id); + + snd_seq_port_info_alloca(&info); + + if ((err = snd_seq_get_any_port_info(driver->seq, addr.client, addr.port, info)) >= 0) { + a2j_debug("updating: %d:%d", addr.client, addr.port); + a2j_update_port(driver, addr, info); + } else { + a2j_debug("setting dead: %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); + } +} + +void +a2j_new_ports (alsa_midi_driver_t * driver, snd_seq_addr_t addr) +{ + snd_seq_port_info_t * port_info; + + assert (addr.client != driver->client_id); + + snd_seq_port_info_alloca(&port_info); + + a2j_debug("adding new port: %d:%d", addr.client, addr.port); + 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); } } diff --git a/drivers/alsa_midi/port_thread.h b/drivers/alsa_midi/port_thread.h index 28d68a9..51b5fc2 100644 --- a/drivers/alsa_midi/port_thread.h +++ b/drivers/alsa_midi/port_thread.h @@ -23,8 +23,9 @@ #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); +void a2j_update_ports (alsa_midi_driver_t* driver, snd_seq_addr_t addr); +void a2j_new_ports (alsa_midi_driver_t* driver, snd_seq_addr_t addr); +void a2j_free_ports (alsa_midi_driver_t* driver); 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);