Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2e5dc73f authored by Takashi Iwai's avatar Takashi Iwai
Browse files

Merge branch 'topic/core-fixes' into for-linus

parents 2154cc0e 7f0973e9
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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 */

+94 −38
Original line number Diff line number Diff line
@@ -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,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) {
@@ -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;
@@ -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);

/**
@@ -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;
	}
+3 −0
Original line number Diff line number Diff line
@@ -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 */
+130 −103
Original line number Diff line number Diff line
@@ -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,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,
@@ -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,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,
@@ -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;
}


+12 −5
Original line number Diff line number Diff line
@@ -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;
	}