Loading include/sound/rawmidi.h +4 −0 Original line number Diff line number Diff line Loading @@ -167,6 +167,10 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count); int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count); int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count); int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count); /* main midi functions */ Loading sound/core/rawmidi.c +94 −38 Original line number Diff line number Diff line Loading @@ -942,31 +942,36 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, unsigned long flags; long result = 0, count1; struct snd_rawmidi_runtime *runtime = substream->runtime; unsigned long appl_ptr; spin_lock_irqsave(&runtime->lock, flags); while (count > 0 && runtime->avail) { count1 = runtime->buffer_size - runtime->appl_ptr; if (count1 > count) count1 = count; spin_lock_irqsave(&runtime->lock, flags); if (count1 > (int)runtime->avail) count1 = runtime->avail; /* update runtime->appl_ptr before unlocking for userbuf */ appl_ptr = runtime->appl_ptr; runtime->appl_ptr += count1; runtime->appl_ptr %= runtime->buffer_size; runtime->avail -= count1; if (kernelbuf) memcpy(kernelbuf + result, runtime->buffer + runtime->appl_ptr, count1); memcpy(kernelbuf + result, runtime->buffer + appl_ptr, count1); if (userbuf) { spin_unlock_irqrestore(&runtime->lock, flags); if (copy_to_user(userbuf + result, runtime->buffer + runtime->appl_ptr, count1)) { runtime->buffer + appl_ptr, count1)) { return result > 0 ? result : -EFAULT; } spin_lock_irqsave(&runtime->lock, flags); } runtime->appl_ptr += count1; runtime->appl_ptr %= runtime->buffer_size; runtime->avail -= count1; spin_unlock_irqrestore(&runtime->lock, flags); result += count1; count -= count1; } spin_unlock_irqrestore(&runtime->lock, flags); return result; } Loading Loading @@ -1055,23 +1060,16 @@ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream) EXPORT_SYMBOL(snd_rawmidi_transmit_empty); /** * snd_rawmidi_transmit_peek - copy data from the internal buffer * __snd_rawmidi_transmit_peek - copy data from the internal buffer * @substream: the rawmidi substream * @buffer: the buffer pointer * @count: data size to transfer * * Copies data from the internal output buffer to the given buffer. * * Call this in the interrupt handler when the midi output is ready, * and call snd_rawmidi_transmit_ack() after the transmission is * finished. * * Return: The size of copied data, or a negative error code on failure. * This is a variant of snd_rawmidi_transmit_peek() without spinlock. */ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count) { unsigned long flags; int result, count1; struct snd_rawmidi_runtime *runtime = substream->runtime; Loading @@ -1081,7 +1079,6 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, return -EINVAL; } result = 0; spin_lock_irqsave(&runtime->lock, flags); if (runtime->avail >= runtime->buffer_size) { /* warning: lowlevel layer MUST trigger down the hardware */ goto __skip; Loading @@ -1106,25 +1103,47 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, } } __skip: return result; } EXPORT_SYMBOL(__snd_rawmidi_transmit_peek); /** * snd_rawmidi_transmit_peek - copy data from the internal buffer * @substream: the rawmidi substream * @buffer: the buffer pointer * @count: data size to transfer * * Copies data from the internal output buffer to the given buffer. * * Call this in the interrupt handler when the midi output is ready, * and call snd_rawmidi_transmit_ack() after the transmission is * finished. * * Return: The size of copied data, or a negative error code on failure. */ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count) { struct snd_rawmidi_runtime *runtime = substream->runtime; int result; unsigned long flags; spin_lock_irqsave(&runtime->lock, flags); result = __snd_rawmidi_transmit_peek(substream, buffer, count); spin_unlock_irqrestore(&runtime->lock, flags); return result; } EXPORT_SYMBOL(snd_rawmidi_transmit_peek); /** * snd_rawmidi_transmit_ack - acknowledge the transmission * __snd_rawmidi_transmit_ack - acknowledge the transmission * @substream: the rawmidi substream * @count: the transferred count * * Advances the hardware pointer for the internal output buffer with * the given size and updates the condition. * Call after the transmission is finished. * * Return: The advanced size if successful, or a negative error code on failure. * This is a variant of __snd_rawmidi_transmit_ack() without spinlock. */ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) { unsigned long flags; struct snd_rawmidi_runtime *runtime = substream->runtime; if (runtime->buffer == NULL) { Loading @@ -1132,7 +1151,6 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) "snd_rawmidi_transmit_ack: output is not active!!!\n"); return -EINVAL; } spin_lock_irqsave(&runtime->lock, flags); snd_BUG_ON(runtime->avail + count > runtime->buffer_size); runtime->hw_ptr += count; runtime->hw_ptr %= runtime->buffer_size; Loading @@ -1142,9 +1160,32 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) if (runtime->drain || snd_rawmidi_ready(substream)) wake_up(&runtime->sleep); } spin_unlock_irqrestore(&runtime->lock, flags); return count; } EXPORT_SYMBOL(__snd_rawmidi_transmit_ack); /** * snd_rawmidi_transmit_ack - acknowledge the transmission * @substream: the rawmidi substream * @count: the transferred count * * Advances the hardware pointer for the internal output buffer with * the given size and updates the condition. * Call after the transmission is finished. * * Return: The advanced size if successful, or a negative error code on failure. */ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) { struct snd_rawmidi_runtime *runtime = substream->runtime; int result; unsigned long flags; spin_lock_irqsave(&runtime->lock, flags); result = __snd_rawmidi_transmit_ack(substream, count); spin_unlock_irqrestore(&runtime->lock, flags); return result; } EXPORT_SYMBOL(snd_rawmidi_transmit_ack); /** Loading @@ -1160,12 +1201,22 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_ack); int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count) { struct snd_rawmidi_runtime *runtime = substream->runtime; int result; unsigned long flags; spin_lock_irqsave(&runtime->lock, flags); if (!substream->opened) return -EBADFD; count = snd_rawmidi_transmit_peek(substream, buffer, count); if (count < 0) return count; return snd_rawmidi_transmit_ack(substream, count); result = -EBADFD; else { count = __snd_rawmidi_transmit_peek(substream, buffer, count); if (count <= 0) result = count; else result = __snd_rawmidi_transmit_ack(substream, count); } spin_unlock_irqrestore(&runtime->lock, flags); return result; } EXPORT_SYMBOL(snd_rawmidi_transmit); Loading @@ -1177,6 +1228,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, unsigned long flags; long count1, result; struct snd_rawmidi_runtime *runtime = substream->runtime; unsigned long appl_ptr; if (!kernelbuf && !userbuf) return -EINVAL; Loading @@ -1197,12 +1249,19 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, count1 = count; if (count1 > (long)runtime->avail) count1 = runtime->avail; /* update runtime->appl_ptr before unlocking for userbuf */ appl_ptr = runtime->appl_ptr; runtime->appl_ptr += count1; runtime->appl_ptr %= runtime->buffer_size; runtime->avail -= count1; if (kernelbuf) memcpy(runtime->buffer + runtime->appl_ptr, memcpy(runtime->buffer + appl_ptr, kernelbuf + result, count1); else if (userbuf) { spin_unlock_irqrestore(&runtime->lock, flags); if (copy_from_user(runtime->buffer + runtime->appl_ptr, if (copy_from_user(runtime->buffer + appl_ptr, userbuf + result, count1)) { spin_lock_irqsave(&runtime->lock, flags); result = result > 0 ? result : -EFAULT; Loading @@ -1210,9 +1269,6 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, } spin_lock_irqsave(&runtime->lock, flags); } runtime->appl_ptr += count1; runtime->appl_ptr %= runtime->buffer_size; runtime->avail -= count1; result += count1; count -= count1; } Loading sound/core/seq/seq_clientmgr.c +3 −0 Original line number Diff line number Diff line Loading @@ -678,6 +678,9 @@ static int deliver_to_subscribers(struct snd_seq_client *client, else down_read(&grp->list_mutex); list_for_each_entry(subs, &grp->list_head, src_list) { /* both ports ready? */ if (atomic_read(&subs->ref_count) != 2) continue; event->dest = subs->info.dest; if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) /* convert time according to flag with subscription */ Loading sound/core/seq/seq_ports.c +130 −103 Original line number Diff line number Diff line Loading @@ -173,10 +173,6 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, } /* */ enum group_type { SRC_LIST, DEST_LIST }; static int subscribe_port(struct snd_seq_client *client, struct snd_seq_client_port *port, struct snd_seq_port_subs_info *grp, Loading @@ -203,6 +199,20 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr, return NULL; } static void delete_and_unsubscribe_port(struct snd_seq_client *client, struct snd_seq_client_port *port, struct snd_seq_subscribers *subs, bool is_src, bool ack); static inline struct snd_seq_subscribers * get_subscriber(struct list_head *p, bool is_src) { if (is_src) return list_entry(p, struct snd_seq_subscribers, src_list); else return list_entry(p, struct snd_seq_subscribers, dest_list); } /* * remove all subscribers on the list * this is called from port_delete, for each src and dest list. Loading @@ -210,7 +220,7 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr, static void clear_subscriber_list(struct snd_seq_client *client, struct snd_seq_client_port *port, struct snd_seq_port_subs_info *grp, int grptype) int is_src) { struct list_head *p, *n; Loading @@ -219,15 +229,13 @@ static void clear_subscriber_list(struct snd_seq_client *client, struct snd_seq_client *c; struct snd_seq_client_port *aport; if (grptype == SRC_LIST) { subs = list_entry(p, struct snd_seq_subscribers, src_list); subs = get_subscriber(p, is_src); if (is_src) aport = get_client_port(&subs->info.dest, &c); } else { subs = list_entry(p, struct snd_seq_subscribers, dest_list); else aport = get_client_port(&subs->info.sender, &c); } list_del(p); unsubscribe_port(client, port, grp, &subs->info, 0); delete_and_unsubscribe_port(client, port, subs, is_src, false); if (!aport) { /* looks like the connected port is being deleted. * we decrease the counter, and when both ports are deleted Loading @@ -235,23 +243,16 @@ static void clear_subscriber_list(struct snd_seq_client *client, */ if (atomic_dec_and_test(&subs->ref_count)) kfree(subs); } else { continue; } /* ok we got the connected port */ struct snd_seq_port_subs_info *agrp; agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src; down_write(&agrp->list_mutex); if (grptype == SRC_LIST) list_del(&subs->dest_list); else list_del(&subs->src_list); up_write(&agrp->list_mutex); unsubscribe_port(c, aport, agrp, &subs->info, 1); delete_and_unsubscribe_port(c, aport, subs, !is_src, true); kfree(subs); snd_seq_port_unlock(aport); snd_seq_client_unlock(c); } } } /* delete port data */ static int port_delete(struct snd_seq_client *client, Loading @@ -262,8 +263,8 @@ static int port_delete(struct snd_seq_client *client, snd_use_lock_sync(&port->use_lock); /* clear subscribers info */ clear_subscriber_list(client, port, &port->c_src, SRC_LIST); clear_subscriber_list(client, port, &port->c_dest, DEST_LIST); clear_subscriber_list(client, port, &port->c_src, true); clear_subscriber_list(client, port, &port->c_dest, false); if (port->private_free) port->private_free(port->private_data); Loading Loading @@ -479,85 +480,120 @@ static int match_subs_info(struct snd_seq_port_subscribe *r, return 0; } /* connect two ports */ int snd_seq_port_connect(struct snd_seq_client *connector, struct snd_seq_client *src_client, struct snd_seq_client_port *src_port, struct snd_seq_client *dest_client, struct snd_seq_client_port *dest_port, struct snd_seq_port_subscribe *info) static int check_and_subscribe_port(struct snd_seq_client *client, struct snd_seq_client_port *port, struct snd_seq_subscribers *subs, bool is_src, bool exclusive, bool ack) { struct snd_seq_port_subs_info *src = &src_port->c_src; struct snd_seq_port_subs_info *dest = &dest_port->c_dest; struct snd_seq_subscribers *subs, *s; int err, src_called = 0; unsigned long flags; int exclusive; subs = kzalloc(sizeof(*subs), GFP_KERNEL); if (! subs) return -ENOMEM; subs->info = *info; atomic_set(&subs->ref_count, 2); down_write(&src->list_mutex); down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING); struct snd_seq_port_subs_info *grp; struct list_head *p; struct snd_seq_subscribers *s; int err; exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0; grp = is_src ? &port->c_src : &port->c_dest; err = -EBUSY; down_write(&grp->list_mutex); if (exclusive) { if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head)) if (!list_empty(&grp->list_head)) goto __error; } else { if (src->exclusive || dest->exclusive) if (grp->exclusive) goto __error; /* check whether already exists */ list_for_each_entry(s, &src->list_head, src_list) { if (match_subs_info(info, &s->info)) list_for_each(p, &grp->list_head) { s = get_subscriber(p, is_src); if (match_subs_info(&subs->info, &s->info)) goto __error; } list_for_each_entry(s, &dest->list_head, dest_list) { if (match_subs_info(info, &s->info)) } err = subscribe_port(client, port, grp, &subs->info, ack); if (err < 0) { grp->exclusive = 0; goto __error; } /* add to list */ write_lock_irq(&grp->list_lock); if (is_src) list_add_tail(&subs->src_list, &grp->list_head); else list_add_tail(&subs->dest_list, &grp->list_head); grp->exclusive = exclusive; atomic_inc(&subs->ref_count); write_unlock_irq(&grp->list_lock); err = 0; __error: up_write(&grp->list_mutex); return err; } if ((err = subscribe_port(src_client, src_port, src, info, connector->number != src_client->number)) < 0) goto __error; src_called = 1; static void delete_and_unsubscribe_port(struct snd_seq_client *client, struct snd_seq_client_port *port, struct snd_seq_subscribers *subs, bool is_src, bool ack) { struct snd_seq_port_subs_info *grp; if ((err = subscribe_port(dest_client, dest_port, dest, info, connector->number != dest_client->number)) < 0) goto __error; grp = is_src ? &port->c_src : &port->c_dest; down_write(&grp->list_mutex); write_lock_irq(&grp->list_lock); if (is_src) list_del(&subs->src_list); else list_del(&subs->dest_list); grp->exclusive = 0; write_unlock_irq(&grp->list_lock); up_write(&grp->list_mutex); /* add to list */ write_lock_irqsave(&src->list_lock, flags); // write_lock(&dest->list_lock); // no other lock yet list_add_tail(&subs->src_list, &src->list_head); list_add_tail(&subs->dest_list, &dest->list_head); // write_unlock(&dest->list_lock); // no other lock yet write_unlock_irqrestore(&src->list_lock, flags); unsubscribe_port(client, port, grp, &subs->info, ack); } src->exclusive = dest->exclusive = exclusive; /* connect two ports */ int snd_seq_port_connect(struct snd_seq_client *connector, struct snd_seq_client *src_client, struct snd_seq_client_port *src_port, struct snd_seq_client *dest_client, struct snd_seq_client_port *dest_port, struct snd_seq_port_subscribe *info) { struct snd_seq_subscribers *subs; bool exclusive; int err; subs = kzalloc(sizeof(*subs), GFP_KERNEL); if (!subs) return -ENOMEM; subs->info = *info; atomic_set(&subs->ref_count, 0); INIT_LIST_HEAD(&subs->src_list); INIT_LIST_HEAD(&subs->dest_list); exclusive = !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE); err = check_and_subscribe_port(src_client, src_port, subs, true, exclusive, connector->number != src_client->number); if (err < 0) goto error; err = check_and_subscribe_port(dest_client, dest_port, subs, false, exclusive, connector->number != dest_client->number); if (err < 0) goto error_dest; up_write(&dest->list_mutex); up_write(&src->list_mutex); return 0; __error: if (src_called) unsubscribe_port(src_client, src_port, src, info, error_dest: delete_and_unsubscribe_port(src_client, src_port, subs, true, connector->number != src_client->number); error: kfree(subs); up_write(&dest->list_mutex); up_write(&src->list_mutex); return err; } /* remove the connection */ int snd_seq_port_disconnect(struct snd_seq_client *connector, struct snd_seq_client *src_client, Loading @@ -567,37 +603,28 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector, struct snd_seq_port_subscribe *info) { struct snd_seq_port_subs_info *src = &src_port->c_src; struct snd_seq_port_subs_info *dest = &dest_port->c_dest; struct snd_seq_subscribers *subs; int err = -ENOENT; unsigned long flags; down_write(&src->list_mutex); down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING); /* look for the connection */ list_for_each_entry(subs, &src->list_head, src_list) { if (match_subs_info(info, &subs->info)) { write_lock_irqsave(&src->list_lock, flags); // write_lock(&dest->list_lock); // no lock yet list_del(&subs->src_list); list_del(&subs->dest_list); // write_unlock(&dest->list_lock); write_unlock_irqrestore(&src->list_lock, flags); src->exclusive = dest->exclusive = 0; unsubscribe_port(src_client, src_port, src, info, connector->number != src_client->number); unsubscribe_port(dest_client, dest_port, dest, info, connector->number != dest_client->number); kfree(subs); atomic_dec(&subs->ref_count); /* mark as not ready */ err = 0; break; } } up_write(&dest->list_mutex); up_write(&src->list_mutex); if (err < 0) return err; delete_and_unsubscribe_port(src_client, src_port, subs, true, connector->number != src_client->number); delete_and_unsubscribe_port(dest_client, dest_port, subs, false, connector->number != dest_client->number); kfree(subs); return 0; } Loading sound/core/seq/seq_virmidi.c +12 −5 Original line number Diff line number Diff line Loading @@ -155,21 +155,26 @@ static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, struct snd_virmidi *vmidi = substream->runtime->private_data; int count, res; unsigned char buf[32], *pbuf; unsigned long flags; if (up) { vmidi->trigger = 1; if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH && !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) { snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail); return; /* ignored */ while (snd_rawmidi_transmit(substream, buf, sizeof(buf)) > 0) { /* ignored */ } return; } if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) { if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0) return; vmidi->event.type = SNDRV_SEQ_EVENT_NONE; } spin_lock_irqsave(&substream->runtime->lock, flags); while (1) { count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf)); count = __snd_rawmidi_transmit_peek(substream, buf, sizeof(buf)); if (count <= 0) break; pbuf = buf; Loading @@ -179,16 +184,18 @@ static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, snd_midi_event_reset_encode(vmidi->parser); continue; } snd_rawmidi_transmit_ack(substream, res); __snd_rawmidi_transmit_ack(substream, res); pbuf += res; count -= res; if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) { if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0) return; goto out; vmidi->event.type = SNDRV_SEQ_EVENT_NONE; } } } out: spin_unlock_irqrestore(&substream->runtime->lock, flags); } else { vmidi->trigger = 0; } Loading Loading
include/sound/rawmidi.h +4 −0 Original line number Diff line number Diff line Loading @@ -167,6 +167,10 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count); int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count); int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count); int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count); /* main midi functions */ Loading
sound/core/rawmidi.c +94 −38 Original line number Diff line number Diff line Loading @@ -942,31 +942,36 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, unsigned long flags; long result = 0, count1; struct snd_rawmidi_runtime *runtime = substream->runtime; unsigned long appl_ptr; spin_lock_irqsave(&runtime->lock, flags); while (count > 0 && runtime->avail) { count1 = runtime->buffer_size - runtime->appl_ptr; if (count1 > count) count1 = count; spin_lock_irqsave(&runtime->lock, flags); if (count1 > (int)runtime->avail) count1 = runtime->avail; /* update runtime->appl_ptr before unlocking for userbuf */ appl_ptr = runtime->appl_ptr; runtime->appl_ptr += count1; runtime->appl_ptr %= runtime->buffer_size; runtime->avail -= count1; if (kernelbuf) memcpy(kernelbuf + result, runtime->buffer + runtime->appl_ptr, count1); memcpy(kernelbuf + result, runtime->buffer + appl_ptr, count1); if (userbuf) { spin_unlock_irqrestore(&runtime->lock, flags); if (copy_to_user(userbuf + result, runtime->buffer + runtime->appl_ptr, count1)) { runtime->buffer + appl_ptr, count1)) { return result > 0 ? result : -EFAULT; } spin_lock_irqsave(&runtime->lock, flags); } runtime->appl_ptr += count1; runtime->appl_ptr %= runtime->buffer_size; runtime->avail -= count1; spin_unlock_irqrestore(&runtime->lock, flags); result += count1; count -= count1; } spin_unlock_irqrestore(&runtime->lock, flags); return result; } Loading Loading @@ -1055,23 +1060,16 @@ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream) EXPORT_SYMBOL(snd_rawmidi_transmit_empty); /** * snd_rawmidi_transmit_peek - copy data from the internal buffer * __snd_rawmidi_transmit_peek - copy data from the internal buffer * @substream: the rawmidi substream * @buffer: the buffer pointer * @count: data size to transfer * * Copies data from the internal output buffer to the given buffer. * * Call this in the interrupt handler when the midi output is ready, * and call snd_rawmidi_transmit_ack() after the transmission is * finished. * * Return: The size of copied data, or a negative error code on failure. * This is a variant of snd_rawmidi_transmit_peek() without spinlock. */ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count) { unsigned long flags; int result, count1; struct snd_rawmidi_runtime *runtime = substream->runtime; Loading @@ -1081,7 +1079,6 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, return -EINVAL; } result = 0; spin_lock_irqsave(&runtime->lock, flags); if (runtime->avail >= runtime->buffer_size) { /* warning: lowlevel layer MUST trigger down the hardware */ goto __skip; Loading @@ -1106,25 +1103,47 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, } } __skip: return result; } EXPORT_SYMBOL(__snd_rawmidi_transmit_peek); /** * snd_rawmidi_transmit_peek - copy data from the internal buffer * @substream: the rawmidi substream * @buffer: the buffer pointer * @count: data size to transfer * * Copies data from the internal output buffer to the given buffer. * * Call this in the interrupt handler when the midi output is ready, * and call snd_rawmidi_transmit_ack() after the transmission is * finished. * * Return: The size of copied data, or a negative error code on failure. */ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count) { struct snd_rawmidi_runtime *runtime = substream->runtime; int result; unsigned long flags; spin_lock_irqsave(&runtime->lock, flags); result = __snd_rawmidi_transmit_peek(substream, buffer, count); spin_unlock_irqrestore(&runtime->lock, flags); return result; } EXPORT_SYMBOL(snd_rawmidi_transmit_peek); /** * snd_rawmidi_transmit_ack - acknowledge the transmission * __snd_rawmidi_transmit_ack - acknowledge the transmission * @substream: the rawmidi substream * @count: the transferred count * * Advances the hardware pointer for the internal output buffer with * the given size and updates the condition. * Call after the transmission is finished. * * Return: The advanced size if successful, or a negative error code on failure. * This is a variant of __snd_rawmidi_transmit_ack() without spinlock. */ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) { unsigned long flags; struct snd_rawmidi_runtime *runtime = substream->runtime; if (runtime->buffer == NULL) { Loading @@ -1132,7 +1151,6 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) "snd_rawmidi_transmit_ack: output is not active!!!\n"); return -EINVAL; } spin_lock_irqsave(&runtime->lock, flags); snd_BUG_ON(runtime->avail + count > runtime->buffer_size); runtime->hw_ptr += count; runtime->hw_ptr %= runtime->buffer_size; Loading @@ -1142,9 +1160,32 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) if (runtime->drain || snd_rawmidi_ready(substream)) wake_up(&runtime->sleep); } spin_unlock_irqrestore(&runtime->lock, flags); return count; } EXPORT_SYMBOL(__snd_rawmidi_transmit_ack); /** * snd_rawmidi_transmit_ack - acknowledge the transmission * @substream: the rawmidi substream * @count: the transferred count * * Advances the hardware pointer for the internal output buffer with * the given size and updates the condition. * Call after the transmission is finished. * * Return: The advanced size if successful, or a negative error code on failure. */ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) { struct snd_rawmidi_runtime *runtime = substream->runtime; int result; unsigned long flags; spin_lock_irqsave(&runtime->lock, flags); result = __snd_rawmidi_transmit_ack(substream, count); spin_unlock_irqrestore(&runtime->lock, flags); return result; } EXPORT_SYMBOL(snd_rawmidi_transmit_ack); /** Loading @@ -1160,12 +1201,22 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_ack); int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count) { struct snd_rawmidi_runtime *runtime = substream->runtime; int result; unsigned long flags; spin_lock_irqsave(&runtime->lock, flags); if (!substream->opened) return -EBADFD; count = snd_rawmidi_transmit_peek(substream, buffer, count); if (count < 0) return count; return snd_rawmidi_transmit_ack(substream, count); result = -EBADFD; else { count = __snd_rawmidi_transmit_peek(substream, buffer, count); if (count <= 0) result = count; else result = __snd_rawmidi_transmit_ack(substream, count); } spin_unlock_irqrestore(&runtime->lock, flags); return result; } EXPORT_SYMBOL(snd_rawmidi_transmit); Loading @@ -1177,6 +1228,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, unsigned long flags; long count1, result; struct snd_rawmidi_runtime *runtime = substream->runtime; unsigned long appl_ptr; if (!kernelbuf && !userbuf) return -EINVAL; Loading @@ -1197,12 +1249,19 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, count1 = count; if (count1 > (long)runtime->avail) count1 = runtime->avail; /* update runtime->appl_ptr before unlocking for userbuf */ appl_ptr = runtime->appl_ptr; runtime->appl_ptr += count1; runtime->appl_ptr %= runtime->buffer_size; runtime->avail -= count1; if (kernelbuf) memcpy(runtime->buffer + runtime->appl_ptr, memcpy(runtime->buffer + appl_ptr, kernelbuf + result, count1); else if (userbuf) { spin_unlock_irqrestore(&runtime->lock, flags); if (copy_from_user(runtime->buffer + runtime->appl_ptr, if (copy_from_user(runtime->buffer + appl_ptr, userbuf + result, count1)) { spin_lock_irqsave(&runtime->lock, flags); result = result > 0 ? result : -EFAULT; Loading @@ -1210,9 +1269,6 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, } spin_lock_irqsave(&runtime->lock, flags); } runtime->appl_ptr += count1; runtime->appl_ptr %= runtime->buffer_size; runtime->avail -= count1; result += count1; count -= count1; } Loading
sound/core/seq/seq_clientmgr.c +3 −0 Original line number Diff line number Diff line Loading @@ -678,6 +678,9 @@ static int deliver_to_subscribers(struct snd_seq_client *client, else down_read(&grp->list_mutex); list_for_each_entry(subs, &grp->list_head, src_list) { /* both ports ready? */ if (atomic_read(&subs->ref_count) != 2) continue; event->dest = subs->info.dest; if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) /* convert time according to flag with subscription */ Loading
sound/core/seq/seq_ports.c +130 −103 Original line number Diff line number Diff line Loading @@ -173,10 +173,6 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, } /* */ enum group_type { SRC_LIST, DEST_LIST }; static int subscribe_port(struct snd_seq_client *client, struct snd_seq_client_port *port, struct snd_seq_port_subs_info *grp, Loading @@ -203,6 +199,20 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr, return NULL; } static void delete_and_unsubscribe_port(struct snd_seq_client *client, struct snd_seq_client_port *port, struct snd_seq_subscribers *subs, bool is_src, bool ack); static inline struct snd_seq_subscribers * get_subscriber(struct list_head *p, bool is_src) { if (is_src) return list_entry(p, struct snd_seq_subscribers, src_list); else return list_entry(p, struct snd_seq_subscribers, dest_list); } /* * remove all subscribers on the list * this is called from port_delete, for each src and dest list. Loading @@ -210,7 +220,7 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr, static void clear_subscriber_list(struct snd_seq_client *client, struct snd_seq_client_port *port, struct snd_seq_port_subs_info *grp, int grptype) int is_src) { struct list_head *p, *n; Loading @@ -219,15 +229,13 @@ static void clear_subscriber_list(struct snd_seq_client *client, struct snd_seq_client *c; struct snd_seq_client_port *aport; if (grptype == SRC_LIST) { subs = list_entry(p, struct snd_seq_subscribers, src_list); subs = get_subscriber(p, is_src); if (is_src) aport = get_client_port(&subs->info.dest, &c); } else { subs = list_entry(p, struct snd_seq_subscribers, dest_list); else aport = get_client_port(&subs->info.sender, &c); } list_del(p); unsubscribe_port(client, port, grp, &subs->info, 0); delete_and_unsubscribe_port(client, port, subs, is_src, false); if (!aport) { /* looks like the connected port is being deleted. * we decrease the counter, and when both ports are deleted Loading @@ -235,23 +243,16 @@ static void clear_subscriber_list(struct snd_seq_client *client, */ if (atomic_dec_and_test(&subs->ref_count)) kfree(subs); } else { continue; } /* ok we got the connected port */ struct snd_seq_port_subs_info *agrp; agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src; down_write(&agrp->list_mutex); if (grptype == SRC_LIST) list_del(&subs->dest_list); else list_del(&subs->src_list); up_write(&agrp->list_mutex); unsubscribe_port(c, aport, agrp, &subs->info, 1); delete_and_unsubscribe_port(c, aport, subs, !is_src, true); kfree(subs); snd_seq_port_unlock(aport); snd_seq_client_unlock(c); } } } /* delete port data */ static int port_delete(struct snd_seq_client *client, Loading @@ -262,8 +263,8 @@ static int port_delete(struct snd_seq_client *client, snd_use_lock_sync(&port->use_lock); /* clear subscribers info */ clear_subscriber_list(client, port, &port->c_src, SRC_LIST); clear_subscriber_list(client, port, &port->c_dest, DEST_LIST); clear_subscriber_list(client, port, &port->c_src, true); clear_subscriber_list(client, port, &port->c_dest, false); if (port->private_free) port->private_free(port->private_data); Loading Loading @@ -479,85 +480,120 @@ static int match_subs_info(struct snd_seq_port_subscribe *r, return 0; } /* connect two ports */ int snd_seq_port_connect(struct snd_seq_client *connector, struct snd_seq_client *src_client, struct snd_seq_client_port *src_port, struct snd_seq_client *dest_client, struct snd_seq_client_port *dest_port, struct snd_seq_port_subscribe *info) static int check_and_subscribe_port(struct snd_seq_client *client, struct snd_seq_client_port *port, struct snd_seq_subscribers *subs, bool is_src, bool exclusive, bool ack) { struct snd_seq_port_subs_info *src = &src_port->c_src; struct snd_seq_port_subs_info *dest = &dest_port->c_dest; struct snd_seq_subscribers *subs, *s; int err, src_called = 0; unsigned long flags; int exclusive; subs = kzalloc(sizeof(*subs), GFP_KERNEL); if (! subs) return -ENOMEM; subs->info = *info; atomic_set(&subs->ref_count, 2); down_write(&src->list_mutex); down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING); struct snd_seq_port_subs_info *grp; struct list_head *p; struct snd_seq_subscribers *s; int err; exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0; grp = is_src ? &port->c_src : &port->c_dest; err = -EBUSY; down_write(&grp->list_mutex); if (exclusive) { if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head)) if (!list_empty(&grp->list_head)) goto __error; } else { if (src->exclusive || dest->exclusive) if (grp->exclusive) goto __error; /* check whether already exists */ list_for_each_entry(s, &src->list_head, src_list) { if (match_subs_info(info, &s->info)) list_for_each(p, &grp->list_head) { s = get_subscriber(p, is_src); if (match_subs_info(&subs->info, &s->info)) goto __error; } list_for_each_entry(s, &dest->list_head, dest_list) { if (match_subs_info(info, &s->info)) } err = subscribe_port(client, port, grp, &subs->info, ack); if (err < 0) { grp->exclusive = 0; goto __error; } /* add to list */ write_lock_irq(&grp->list_lock); if (is_src) list_add_tail(&subs->src_list, &grp->list_head); else list_add_tail(&subs->dest_list, &grp->list_head); grp->exclusive = exclusive; atomic_inc(&subs->ref_count); write_unlock_irq(&grp->list_lock); err = 0; __error: up_write(&grp->list_mutex); return err; } if ((err = subscribe_port(src_client, src_port, src, info, connector->number != src_client->number)) < 0) goto __error; src_called = 1; static void delete_and_unsubscribe_port(struct snd_seq_client *client, struct snd_seq_client_port *port, struct snd_seq_subscribers *subs, bool is_src, bool ack) { struct snd_seq_port_subs_info *grp; if ((err = subscribe_port(dest_client, dest_port, dest, info, connector->number != dest_client->number)) < 0) goto __error; grp = is_src ? &port->c_src : &port->c_dest; down_write(&grp->list_mutex); write_lock_irq(&grp->list_lock); if (is_src) list_del(&subs->src_list); else list_del(&subs->dest_list); grp->exclusive = 0; write_unlock_irq(&grp->list_lock); up_write(&grp->list_mutex); /* add to list */ write_lock_irqsave(&src->list_lock, flags); // write_lock(&dest->list_lock); // no other lock yet list_add_tail(&subs->src_list, &src->list_head); list_add_tail(&subs->dest_list, &dest->list_head); // write_unlock(&dest->list_lock); // no other lock yet write_unlock_irqrestore(&src->list_lock, flags); unsubscribe_port(client, port, grp, &subs->info, ack); } src->exclusive = dest->exclusive = exclusive; /* connect two ports */ int snd_seq_port_connect(struct snd_seq_client *connector, struct snd_seq_client *src_client, struct snd_seq_client_port *src_port, struct snd_seq_client *dest_client, struct snd_seq_client_port *dest_port, struct snd_seq_port_subscribe *info) { struct snd_seq_subscribers *subs; bool exclusive; int err; subs = kzalloc(sizeof(*subs), GFP_KERNEL); if (!subs) return -ENOMEM; subs->info = *info; atomic_set(&subs->ref_count, 0); INIT_LIST_HEAD(&subs->src_list); INIT_LIST_HEAD(&subs->dest_list); exclusive = !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE); err = check_and_subscribe_port(src_client, src_port, subs, true, exclusive, connector->number != src_client->number); if (err < 0) goto error; err = check_and_subscribe_port(dest_client, dest_port, subs, false, exclusive, connector->number != dest_client->number); if (err < 0) goto error_dest; up_write(&dest->list_mutex); up_write(&src->list_mutex); return 0; __error: if (src_called) unsubscribe_port(src_client, src_port, src, info, error_dest: delete_and_unsubscribe_port(src_client, src_port, subs, true, connector->number != src_client->number); error: kfree(subs); up_write(&dest->list_mutex); up_write(&src->list_mutex); return err; } /* remove the connection */ int snd_seq_port_disconnect(struct snd_seq_client *connector, struct snd_seq_client *src_client, Loading @@ -567,37 +603,28 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector, struct snd_seq_port_subscribe *info) { struct snd_seq_port_subs_info *src = &src_port->c_src; struct snd_seq_port_subs_info *dest = &dest_port->c_dest; struct snd_seq_subscribers *subs; int err = -ENOENT; unsigned long flags; down_write(&src->list_mutex); down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING); /* look for the connection */ list_for_each_entry(subs, &src->list_head, src_list) { if (match_subs_info(info, &subs->info)) { write_lock_irqsave(&src->list_lock, flags); // write_lock(&dest->list_lock); // no lock yet list_del(&subs->src_list); list_del(&subs->dest_list); // write_unlock(&dest->list_lock); write_unlock_irqrestore(&src->list_lock, flags); src->exclusive = dest->exclusive = 0; unsubscribe_port(src_client, src_port, src, info, connector->number != src_client->number); unsubscribe_port(dest_client, dest_port, dest, info, connector->number != dest_client->number); kfree(subs); atomic_dec(&subs->ref_count); /* mark as not ready */ err = 0; break; } } up_write(&dest->list_mutex); up_write(&src->list_mutex); if (err < 0) return err; delete_and_unsubscribe_port(src_client, src_port, subs, true, connector->number != src_client->number); delete_and_unsubscribe_port(dest_client, dest_port, subs, false, connector->number != dest_client->number); kfree(subs); return 0; } Loading
sound/core/seq/seq_virmidi.c +12 −5 Original line number Diff line number Diff line Loading @@ -155,21 +155,26 @@ static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, struct snd_virmidi *vmidi = substream->runtime->private_data; int count, res; unsigned char buf[32], *pbuf; unsigned long flags; if (up) { vmidi->trigger = 1; if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH && !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) { snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail); return; /* ignored */ while (snd_rawmidi_transmit(substream, buf, sizeof(buf)) > 0) { /* ignored */ } return; } if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) { if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0) return; vmidi->event.type = SNDRV_SEQ_EVENT_NONE; } spin_lock_irqsave(&substream->runtime->lock, flags); while (1) { count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf)); count = __snd_rawmidi_transmit_peek(substream, buf, sizeof(buf)); if (count <= 0) break; pbuf = buf; Loading @@ -179,16 +184,18 @@ static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, snd_midi_event_reset_encode(vmidi->parser); continue; } snd_rawmidi_transmit_ack(substream, res); __snd_rawmidi_transmit_ack(substream, res); pbuf += res; count -= res; if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) { if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0) return; goto out; vmidi->event.type = SNDRV_SEQ_EVENT_NONE; } } } out: spin_unlock_irqrestore(&substream->runtime->lock, flags); } else { vmidi->trigger = 0; } Loading