mirror of https://gitee.com/openkylin/linux.git
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_ack(struct snd_rawmidi_substream *substream, int count);
|
||||||
int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
|
int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
|
||||||
unsigned char *buffer, int count);
|
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 */
|
/* main midi functions */
|
||||||
|
|
||||||
|
|
|
@ -942,31 +942,36 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
long result = 0, count1;
|
long result = 0, count1;
|
||||||
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
||||||
|
unsigned long appl_ptr;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&runtime->lock, flags);
|
||||||
while (count > 0 && runtime->avail) {
|
while (count > 0 && runtime->avail) {
|
||||||
count1 = runtime->buffer_size - runtime->appl_ptr;
|
count1 = runtime->buffer_size - runtime->appl_ptr;
|
||||||
if (count1 > count)
|
if (count1 > count)
|
||||||
count1 = count;
|
count1 = count;
|
||||||
spin_lock_irqsave(&runtime->lock, flags);
|
|
||||||
if (count1 > (int)runtime->avail)
|
if (count1 > (int)runtime->avail)
|
||||||
count1 = 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)
|
if (kernelbuf)
|
||||||
memcpy(kernelbuf + result, runtime->buffer + runtime->appl_ptr, count1);
|
memcpy(kernelbuf + result, runtime->buffer + appl_ptr, count1);
|
||||||
if (userbuf) {
|
if (userbuf) {
|
||||||
spin_unlock_irqrestore(&runtime->lock, flags);
|
spin_unlock_irqrestore(&runtime->lock, flags);
|
||||||
if (copy_to_user(userbuf + result,
|
if (copy_to_user(userbuf + result,
|
||||||
runtime->buffer + runtime->appl_ptr, count1)) {
|
runtime->buffer + appl_ptr, count1)) {
|
||||||
return result > 0 ? result : -EFAULT;
|
return result > 0 ? result : -EFAULT;
|
||||||
}
|
}
|
||||||
spin_lock_irqsave(&runtime->lock, flags);
|
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;
|
result += count1;
|
||||||
count -= count1;
|
count -= count1;
|
||||||
}
|
}
|
||||||
|
spin_unlock_irqrestore(&runtime->lock, flags);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1055,23 +1060,16 @@ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream)
|
||||||
EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
|
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
|
* @substream: the rawmidi substream
|
||||||
* @buffer: the buffer pointer
|
* @buffer: the buffer pointer
|
||||||
* @count: data size to transfer
|
* @count: data size to transfer
|
||||||
*
|
*
|
||||||
* Copies data from the internal output buffer to the given buffer.
|
* This is a variant of snd_rawmidi_transmit_peek() without spinlock.
|
||||||
*
|
|
||||||
* 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,
|
int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
|
||||||
unsigned char *buffer, int count)
|
unsigned char *buffer, int count)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
int result, count1;
|
int result, count1;
|
||||||
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
||||||
|
|
||||||
|
@ -1081,7 +1079,6 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
result = 0;
|
result = 0;
|
||||||
spin_lock_irqsave(&runtime->lock, flags);
|
|
||||||
if (runtime->avail >= runtime->buffer_size) {
|
if (runtime->avail >= runtime->buffer_size) {
|
||||||
/* warning: lowlevel layer MUST trigger down the hardware */
|
/* warning: lowlevel layer MUST trigger down the hardware */
|
||||||
goto __skip;
|
goto __skip;
|
||||||
|
@ -1106,11 +1103,67 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
__skip:
|
__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);
|
spin_unlock_irqrestore(&runtime->lock, flags);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
|
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
|
* snd_rawmidi_transmit_ack - acknowledge the transmission
|
||||||
* @substream: the rawmidi substream
|
* @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)
|
int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
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);
|
spin_lock_irqsave(&runtime->lock, flags);
|
||||||
snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
|
result = __snd_rawmidi_transmit_ack(substream, count);
|
||||||
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);
|
|
||||||
}
|
|
||||||
spin_unlock_irqrestore(&runtime->lock, flags);
|
spin_unlock_irqrestore(&runtime->lock, flags);
|
||||||
return count;
|
return result;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
|
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,
|
int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
|
||||||
unsigned char *buffer, int count)
|
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)
|
if (!substream->opened)
|
||||||
return -EBADFD;
|
result = -EBADFD;
|
||||||
count = snd_rawmidi_transmit_peek(substream, buffer, count);
|
else {
|
||||||
if (count < 0)
|
count = __snd_rawmidi_transmit_peek(substream, buffer, count);
|
||||||
return count;
|
if (count <= 0)
|
||||||
return snd_rawmidi_transmit_ack(substream, count);
|
result = count;
|
||||||
|
else
|
||||||
|
result = __snd_rawmidi_transmit_ack(substream, count);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&runtime->lock, flags);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(snd_rawmidi_transmit);
|
EXPORT_SYMBOL(snd_rawmidi_transmit);
|
||||||
|
|
||||||
|
@ -1177,6 +1228,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
long count1, result;
|
long count1, result;
|
||||||
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
||||||
|
unsigned long appl_ptr;
|
||||||
|
|
||||||
if (!kernelbuf && !userbuf)
|
if (!kernelbuf && !userbuf)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -1197,12 +1249,19 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
|
||||||
count1 = count;
|
count1 = count;
|
||||||
if (count1 > (long)runtime->avail)
|
if (count1 > (long)runtime->avail)
|
||||||
count1 = 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)
|
if (kernelbuf)
|
||||||
memcpy(runtime->buffer + runtime->appl_ptr,
|
memcpy(runtime->buffer + appl_ptr,
|
||||||
kernelbuf + result, count1);
|
kernelbuf + result, count1);
|
||||||
else if (userbuf) {
|
else if (userbuf) {
|
||||||
spin_unlock_irqrestore(&runtime->lock, flags);
|
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)) {
|
userbuf + result, count1)) {
|
||||||
spin_lock_irqsave(&runtime->lock, flags);
|
spin_lock_irqsave(&runtime->lock, flags);
|
||||||
result = result > 0 ? result : -EFAULT;
|
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);
|
spin_lock_irqsave(&runtime->lock, flags);
|
||||||
}
|
}
|
||||||
runtime->appl_ptr += count1;
|
|
||||||
runtime->appl_ptr %= runtime->buffer_size;
|
|
||||||
runtime->avail -= count1;
|
|
||||||
result += count1;
|
result += count1;
|
||||||
count -= count1;
|
count -= count1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -678,6 +678,9 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
|
||||||
else
|
else
|
||||||
down_read(&grp->list_mutex);
|
down_read(&grp->list_mutex);
|
||||||
list_for_each_entry(subs, &grp->list_head, src_list) {
|
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;
|
event->dest = subs->info.dest;
|
||||||
if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
|
if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
|
||||||
/* convert time according to flag with subscription */
|
/* 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,
|
static int subscribe_port(struct snd_seq_client *client,
|
||||||
struct snd_seq_client_port *port,
|
struct snd_seq_client_port *port,
|
||||||
struct snd_seq_port_subs_info *grp,
|
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;
|
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
|
* remove all subscribers on the list
|
||||||
* this is called from port_delete, for each src and dest 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,
|
static void clear_subscriber_list(struct snd_seq_client *client,
|
||||||
struct snd_seq_client_port *port,
|
struct snd_seq_client_port *port,
|
||||||
struct snd_seq_port_subs_info *grp,
|
struct snd_seq_port_subs_info *grp,
|
||||||
int grptype)
|
int is_src)
|
||||||
{
|
{
|
||||||
struct list_head *p, *n;
|
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 *c;
|
||||||
struct snd_seq_client_port *aport;
|
struct snd_seq_client_port *aport;
|
||||||
|
|
||||||
if (grptype == SRC_LIST) {
|
subs = get_subscriber(p, is_src);
|
||||||
subs = list_entry(p, struct snd_seq_subscribers, src_list);
|
if (is_src)
|
||||||
aport = get_client_port(&subs->info.dest, &c);
|
aport = get_client_port(&subs->info.dest, &c);
|
||||||
} else {
|
else
|
||||||
subs = list_entry(p, struct snd_seq_subscribers, dest_list);
|
|
||||||
aport = get_client_port(&subs->info.sender, &c);
|
aport = get_client_port(&subs->info.sender, &c);
|
||||||
}
|
delete_and_unsubscribe_port(client, port, subs, is_src, false);
|
||||||
list_del(p);
|
|
||||||
unsubscribe_port(client, port, grp, &subs->info, 0);
|
|
||||||
if (!aport) {
|
if (!aport) {
|
||||||
/* looks like the connected port is being deleted.
|
/* looks like the connected port is being deleted.
|
||||||
* we decrease the counter, and when both ports are deleted
|
* we decrease the counter, and when both ports are deleted
|
||||||
|
@ -235,22 +243,15 @@ static void clear_subscriber_list(struct snd_seq_client *client,
|
||||||
*/
|
*/
|
||||||
if (atomic_dec_and_test(&subs->ref_count))
|
if (atomic_dec_and_test(&subs->ref_count))
|
||||||
kfree(subs);
|
kfree(subs);
|
||||||
} else {
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* ok we got the connected port */
|
/* ok we got the connected port */
|
||||||
struct snd_seq_port_subs_info *agrp;
|
delete_and_unsubscribe_port(c, aport, subs, !is_src, true);
|
||||||
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);
|
kfree(subs);
|
||||||
snd_seq_port_unlock(aport);
|
snd_seq_port_unlock(aport);
|
||||||
snd_seq_client_unlock(c);
|
snd_seq_client_unlock(c);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* delete port data */
|
/* delete port data */
|
||||||
|
@ -262,8 +263,8 @@ static int port_delete(struct snd_seq_client *client,
|
||||||
snd_use_lock_sync(&port->use_lock);
|
snd_use_lock_sync(&port->use_lock);
|
||||||
|
|
||||||
/* clear subscribers info */
|
/* clear subscribers info */
|
||||||
clear_subscriber_list(client, port, &port->c_src, SRC_LIST);
|
clear_subscriber_list(client, port, &port->c_src, true);
|
||||||
clear_subscriber_list(client, port, &port->c_dest, DEST_LIST);
|
clear_subscriber_list(client, port, &port->c_dest, false);
|
||||||
|
|
||||||
if (port->private_free)
|
if (port->private_free)
|
||||||
port->private_free(port->private_data);
|
port->private_free(port->private_data);
|
||||||
|
@ -479,6 +480,75 @@ static int match_subs_info(struct snd_seq_port_subscribe *r,
|
||||||
return 0;
|
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 */
|
/* connect two ports */
|
||||||
int snd_seq_port_connect(struct snd_seq_client *connector,
|
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_client_port *dest_port,
|
||||||
struct snd_seq_port_subscribe *info)
|
struct snd_seq_port_subscribe *info)
|
||||||
{
|
{
|
||||||
struct snd_seq_port_subs_info *src = &src_port->c_src;
|
struct snd_seq_subscribers *subs;
|
||||||
struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
|
bool exclusive;
|
||||||
struct snd_seq_subscribers *subs, *s;
|
int err;
|
||||||
int err, src_called = 0;
|
|
||||||
unsigned long flags;
|
|
||||||
int exclusive;
|
|
||||||
|
|
||||||
subs = kzalloc(sizeof(*subs), GFP_KERNEL);
|
subs = kzalloc(sizeof(*subs), GFP_KERNEL);
|
||||||
if (! subs)
|
if (!subs)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
subs->info = *info;
|
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);
|
exclusive = !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE);
|
||||||
down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
|
|
||||||
|
|
||||||
exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0;
|
err = check_and_subscribe_port(src_client, src_port, subs, true,
|
||||||
err = -EBUSY;
|
exclusive,
|
||||||
if (exclusive) {
|
connector->number != src_client->number);
|
||||||
if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head))
|
if (err < 0)
|
||||||
goto __error;
|
goto error;
|
||||||
} else {
|
err = check_and_subscribe_port(dest_client, dest_port, subs, false,
|
||||||
if (src->exclusive || dest->exclusive)
|
exclusive,
|
||||||
goto __error;
|
connector->number != dest_client->number);
|
||||||
/* check whether already exists */
|
if (err < 0)
|
||||||
list_for_each_entry(s, &src->list_head, src_list) {
|
goto error_dest;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
return 0;
|
||||||
|
|
||||||
__error:
|
error_dest:
|
||||||
if (src_called)
|
delete_and_unsubscribe_port(src_client, src_port, subs, true,
|
||||||
unsubscribe_port(src_client, src_port, src, info,
|
|
||||||
connector->number != src_client->number);
|
connector->number != src_client->number);
|
||||||
|
error:
|
||||||
kfree(subs);
|
kfree(subs);
|
||||||
up_write(&dest->list_mutex);
|
|
||||||
up_write(&src->list_mutex);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* remove the connection */
|
/* remove the connection */
|
||||||
int snd_seq_port_disconnect(struct snd_seq_client *connector,
|
int snd_seq_port_disconnect(struct snd_seq_client *connector,
|
||||||
struct snd_seq_client *src_client,
|
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_subscribe *info)
|
||||||
{
|
{
|
||||||
struct snd_seq_port_subs_info *src = &src_port->c_src;
|
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;
|
struct snd_seq_subscribers *subs;
|
||||||
int err = -ENOENT;
|
int err = -ENOENT;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
down_write(&src->list_mutex);
|
down_write(&src->list_mutex);
|
||||||
down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
|
|
||||||
|
|
||||||
/* look for the connection */
|
/* look for the connection */
|
||||||
list_for_each_entry(subs, &src->list_head, src_list) {
|
list_for_each_entry(subs, &src->list_head, src_list) {
|
||||||
if (match_subs_info(info, &subs->info)) {
|
if (match_subs_info(info, &subs->info)) {
|
||||||
write_lock_irqsave(&src->list_lock, flags);
|
atomic_dec(&subs->ref_count); /* mark as not ready */
|
||||||
// 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);
|
|
||||||
err = 0;
|
err = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
up_write(&dest->list_mutex);
|
|
||||||
up_write(&src->list_mutex);
|
up_write(&src->list_mutex);
|
||||||
|
if (err < 0)
|
||||||
return err;
|
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;
|
struct snd_virmidi *vmidi = substream->runtime->private_data;
|
||||||
int count, res;
|
int count, res;
|
||||||
unsigned char buf[32], *pbuf;
|
unsigned char buf[32], *pbuf;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
if (up) {
|
if (up) {
|
||||||
vmidi->trigger = 1;
|
vmidi->trigger = 1;
|
||||||
if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
|
if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
|
||||||
!(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
|
!(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
|
||||||
snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail);
|
while (snd_rawmidi_transmit(substream, buf,
|
||||||
return; /* ignored */
|
sizeof(buf)) > 0) {
|
||||||
|
/* ignored */
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
|
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
|
||||||
if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
|
if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
|
||||||
return;
|
return;
|
||||||
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
|
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
|
||||||
}
|
}
|
||||||
|
spin_lock_irqsave(&substream->runtime->lock, flags);
|
||||||
while (1) {
|
while (1) {
|
||||||
count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
|
count = __snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
|
||||||
if (count <= 0)
|
if (count <= 0)
|
||||||
break;
|
break;
|
||||||
pbuf = buf;
|
pbuf = buf;
|
||||||
|
@ -179,16 +184,18 @@ static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream,
|
||||||
snd_midi_event_reset_encode(vmidi->parser);
|
snd_midi_event_reset_encode(vmidi->parser);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
snd_rawmidi_transmit_ack(substream, res);
|
__snd_rawmidi_transmit_ack(substream, res);
|
||||||
pbuf += res;
|
pbuf += res;
|
||||||
count -= res;
|
count -= res;
|
||||||
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
|
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
|
||||||
if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
|
if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
|
||||||
return;
|
goto out;
|
||||||
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
|
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
|
spin_unlock_irqrestore(&substream->runtime->lock, flags);
|
||||||
} else {
|
} else {
|
||||||
vmidi->trigger = 0;
|
vmidi->trigger = 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue