Merge branch 'topic/core-fixes' into for-linus
This commit is contained in:
commit
2e5dc73fe1
@ -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 */
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
@ -1106,11 +1103,67 @@ 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
|
||||
* @substream: the rawmidi substream
|
||||
* @count: the transferred count
|
||||
*
|
||||
* This is a variant of __snd_rawmidi_transmit_ack() without spinlock.
|
||||
*/
|
||||
int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
|
||||
{
|
||||
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
||||
|
||||
if (runtime->buffer == NULL) {
|
||||
rmidi_dbg(substream->rmidi,
|
||||
"snd_rawmidi_transmit_ack: output is not active!!!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
|
||||
runtime->hw_ptr += count;
|
||||
runtime->hw_ptr %= runtime->buffer_size;
|
||||
runtime->avail += count;
|
||||
substream->bytes += count;
|
||||
if (count > 0) {
|
||||
if (runtime->drain || snd_rawmidi_ready(substream))
|
||||
wake_up(&runtime->sleep);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL(__snd_rawmidi_transmit_ack);
|
||||
|
||||
/**
|
||||
* snd_rawmidi_transmit_ack - acknowledge the transmission
|
||||
* @substream: the rawmidi substream
|
||||
@ -1124,26 +1177,14 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
|
||||
*/
|
||||
int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
||||
int result;
|
||||
unsigned long flags;
|
||||
|
||||
if (runtime->buffer == NULL) {
|
||||
rmidi_dbg(substream->rmidi,
|
||||
"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;
|
||||
runtime->avail += count;
|
||||
substream->bytes += count;
|
||||
if (count > 0) {
|
||||
if (runtime->drain || snd_rawmidi_ready(substream))
|
||||
wake_up(&runtime->sleep);
|
||||
}
|
||||
result = __snd_rawmidi_transmit_ack(substream, count);
|
||||
spin_unlock_irqrestore(&runtime->lock, flags);
|
||||
return count;
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
@ -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.
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
@ -235,21 +243,14 @@ static void clear_subscriber_list(struct snd_seq_client *client,
|
||||
*/
|
||||
if (atomic_dec_and_test(&subs->ref_count))
|
||||
kfree(subs);
|
||||
} else {
|
||||
/* 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);
|
||||
kfree(subs);
|
||||
snd_seq_port_unlock(aport);
|
||||
snd_seq_client_unlock(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ok we got the connected port */
|
||||
delete_and_unsubscribe_port(c, aport, subs, !is_src, true);
|
||||
kfree(subs);
|
||||
snd_seq_port_unlock(aport);
|
||||
snd_seq_client_unlock(c);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
@ -479,6 +480,75 @@ static int match_subs_info(struct snd_seq_port_subscribe *r,
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 *grp;
|
||||
struct list_head *p;
|
||||
struct snd_seq_subscribers *s;
|
||||
int err;
|
||||
|
||||
grp = is_src ? &port->c_src : &port->c_dest;
|
||||
err = -EBUSY;
|
||||
down_write(&grp->list_mutex);
|
||||
if (exclusive) {
|
||||
if (!list_empty(&grp->list_head))
|
||||
goto __error;
|
||||
} else {
|
||||
if (grp->exclusive)
|
||||
goto __error;
|
||||
/* check whether already exists */
|
||||
list_for_each(p, &grp->list_head) {
|
||||
s = get_subscriber(p, is_src);
|
||||
if (match_subs_info(&subs->info, &s->info))
|
||||
goto __error;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
unsubscribe_port(client, port, grp, &subs->info, ack);
|
||||
}
|
||||
|
||||
/* connect two ports */
|
||||
int snd_seq_port_connect(struct snd_seq_client *connector,
|
||||
@ -488,76 +558,42 @@ int snd_seq_port_connect(struct snd_seq_client *connector,
|
||||
struct snd_seq_client_port *dest_port,
|
||||
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, *s;
|
||||
int err, src_called = 0;
|
||||
unsigned long flags;
|
||||
int exclusive;
|
||||
struct snd_seq_subscribers *subs;
|
||||
bool exclusive;
|
||||
int err;
|
||||
|
||||
subs = kzalloc(sizeof(*subs), GFP_KERNEL);
|
||||
if (! subs)
|
||||
if (!subs)
|
||||
return -ENOMEM;
|
||||
|
||||
subs->info = *info;
|
||||
atomic_set(&subs->ref_count, 2);
|
||||
atomic_set(&subs->ref_count, 0);
|
||||
INIT_LIST_HEAD(&subs->src_list);
|
||||
INIT_LIST_HEAD(&subs->dest_list);
|
||||
|
||||
down_write(&src->list_mutex);
|
||||
down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
|
||||
exclusive = !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE);
|
||||
|
||||
exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0;
|
||||
err = -EBUSY;
|
||||
if (exclusive) {
|
||||
if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head))
|
||||
goto __error;
|
||||
} else {
|
||||
if (src->exclusive || dest->exclusive)
|
||||
goto __error;
|
||||
/* check whether already exists */
|
||||
list_for_each_entry(s, &src->list_head, src_list) {
|
||||
if (match_subs_info(info, &s->info))
|
||||
goto __error;
|
||||
}
|
||||
list_for_each_entry(s, &dest->list_head, dest_list) {
|
||||
if (match_subs_info(info, &s->info))
|
||||
goto __error;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
if ((err = subscribe_port(src_client, src_port, src, info,
|
||||
connector->number != src_client->number)) < 0)
|
||||
goto __error;
|
||||
src_called = 1;
|
||||
|
||||
if ((err = subscribe_port(dest_client, dest_port, dest, info,
|
||||
connector->number != dest_client->number)) < 0)
|
||||
goto __error;
|
||||
|
||||
/* 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);
|
||||
|
||||
src->exclusive = dest->exclusive = exclusive;
|
||||
|
||||
up_write(&dest->list_mutex);
|
||||
up_write(&src->list_mutex);
|
||||
return 0;
|
||||
|
||||
__error:
|
||||
if (src_called)
|
||||
unsubscribe_port(src_client, src_port, src, info,
|
||||
connector->number != src_client->number);
|
||||
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,
|
||||
@ -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);
|
||||
return err;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
@ -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…
Reference in New Issue
Block a user