diff options
Diffstat (limited to 'sound/core/rawmidi.c')
| -rw-r--r-- | sound/core/rawmidi.c | 249 | 
1 files changed, 133 insertions, 116 deletions
| diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 69616d00481c..69517e18ef07 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -29,6 +29,7 @@  #include <linux/mutex.h>  #include <linux/module.h>  #include <linux/delay.h> +#include <linux/mm.h>  #include <sound/rawmidi.h>  #include <sound/info.h>  #include <sound/control.h> @@ -88,6 +89,7 @@ static inline unsigned short snd_rawmidi_file_flags(struct file *file)  static inline int snd_rawmidi_ready(struct snd_rawmidi_substream *substream)  {  	struct snd_rawmidi_runtime *runtime = substream->runtime; +  	return runtime->avail >= runtime->avail_min;  } @@ -95,6 +97,7 @@ static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substre  					   size_t count)  {  	struct snd_rawmidi_runtime *runtime = substream->runtime; +  	return runtime->avail >= runtime->avail_min &&  	       (!substream->append || runtime->avail >= count);  } @@ -103,6 +106,7 @@ static void snd_rawmidi_input_event_work(struct work_struct *work)  {  	struct snd_rawmidi_runtime *runtime =  		container_of(work, struct snd_rawmidi_runtime, event_work); +  	if (runtime->event)  		runtime->event(runtime->substream);  } @@ -111,7 +115,8 @@ static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)  {  	struct snd_rawmidi_runtime *runtime; -	if ((runtime = kzalloc(sizeof(*runtime), GFP_KERNEL)) == NULL) +	runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); +	if (!runtime)  		return -ENOMEM;  	runtime->substream = substream;  	spin_lock_init(&runtime->lock); @@ -124,7 +129,8 @@ static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)  		runtime->avail = 0;  	else  		runtime->avail = runtime->buffer_size; -	if ((runtime->buffer = kmalloc(runtime->buffer_size, GFP_KERNEL)) == NULL) { +	runtime->buffer = kvmalloc(runtime->buffer_size, GFP_KERNEL); +	if (!runtime->buffer) {  		kfree(runtime);  		return -ENOMEM;  	} @@ -137,13 +143,13 @@ static int snd_rawmidi_runtime_free(struct snd_rawmidi_substream *substream)  {  	struct snd_rawmidi_runtime *runtime = substream->runtime; -	kfree(runtime->buffer); +	kvfree(runtime->buffer);  	kfree(runtime);  	substream->runtime = NULL;  	return 0;  } -static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *substream,int up) +static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)  {  	if (!substream->opened)  		return; @@ -159,17 +165,28 @@ static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, i  		cancel_work_sync(&substream->runtime->event_work);  } -int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream) +static void __reset_runtime_ptrs(struct snd_rawmidi_runtime *runtime, +				 bool is_input) +{ +	runtime->drain = 0; +	runtime->appl_ptr = runtime->hw_ptr = 0; +	runtime->avail = is_input ? 0 : runtime->buffer_size; +} + +static void reset_runtime_ptrs(struct snd_rawmidi_runtime *runtime, +			       bool is_input)  {  	unsigned long flags; -	struct snd_rawmidi_runtime *runtime = substream->runtime; -	snd_rawmidi_output_trigger(substream, 0); -	runtime->drain = 0;  	spin_lock_irqsave(&runtime->lock, flags); -	runtime->appl_ptr = runtime->hw_ptr = 0; -	runtime->avail = runtime->buffer_size; +	__reset_runtime_ptrs(runtime, is_input);  	spin_unlock_irqrestore(&runtime->lock, flags); +} + +int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream) +{ +	snd_rawmidi_output_trigger(substream, 0); +	reset_runtime_ptrs(substream->runtime, false);  	return 0;  }  EXPORT_SYMBOL(snd_rawmidi_drop_output); @@ -208,15 +225,8 @@ EXPORT_SYMBOL(snd_rawmidi_drain_output);  int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream)  { -	unsigned long flags; -	struct snd_rawmidi_runtime *runtime = substream->runtime; -  	snd_rawmidi_input_trigger(substream, 0); -	runtime->drain = 0; -	spin_lock_irqsave(&runtime->lock, flags); -	runtime->appl_ptr = runtime->hw_ptr = 0; -	runtime->avail = 0; -	spin_unlock_irqrestore(&runtime->lock, flags); +	reset_runtime_ptrs(substream->runtime, true);  	return 0;  }  EXPORT_SYMBOL(snd_rawmidi_drain_input); @@ -330,25 +340,23 @@ static int rawmidi_open_priv(struct snd_rawmidi *rmidi, int subdevice, int mode,  /* called from sound/core/seq/seq_midi.c */  int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice, -			    int mode, struct snd_rawmidi_file * rfile) +			    int mode, struct snd_rawmidi_file *rfile)  {  	struct snd_rawmidi *rmidi; -	int err; +	int err = 0;  	if (snd_BUG_ON(!rfile))  		return -EINVAL;  	mutex_lock(®ister_mutex);  	rmidi = snd_rawmidi_search(card, device); -	if (rmidi == NULL) { -		mutex_unlock(®ister_mutex); -		return -ENODEV; -	} -	if (!try_module_get(rmidi->card->module)) { -		mutex_unlock(®ister_mutex); -		return -ENXIO; -	} +	if (!rmidi) +		err = -ENODEV; +	else if (!try_module_get(rmidi->card->module)) +		err = -ENXIO;  	mutex_unlock(®ister_mutex); +	if (err < 0) +		return err;  	mutex_lock(&rmidi->open_mutex);  	err = rawmidi_open_priv(rmidi, subdevice, mode, rfile); @@ -370,7 +378,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)  	struct snd_rawmidi_file *rawmidi_file = NULL;  	wait_queue_entry_t wait; -	if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))  +	if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))  		return -EINVAL;		/* invalid combination */  	err = nonseekable_open(inode, file); @@ -520,7 +528,7 @@ int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile)  	if (snd_BUG_ON(!rfile))  		return -ENXIO; -	 +  	rmidi = rfile->rmidi;  	rawmidi_release_priv(rfile);  	module_put(rmidi->card->module); @@ -548,7 +556,7 @@ static int snd_rawmidi_info(struct snd_rawmidi_substream *substream,  			    struct snd_rawmidi_info *info)  {  	struct snd_rawmidi *rmidi; -	 +  	if (substream == NULL)  		return -ENODEV;  	rmidi = substream->rmidi; @@ -568,11 +576,13 @@ static int snd_rawmidi_info(struct snd_rawmidi_substream *substream,  }  static int snd_rawmidi_info_user(struct snd_rawmidi_substream *substream, -				 struct snd_rawmidi_info __user * _info) +				 struct snd_rawmidi_info __user *_info)  {  	struct snd_rawmidi_info info;  	int err; -	if ((err = snd_rawmidi_info(substream, &info)) < 0) + +	err = snd_rawmidi_info(substream, &info); +	if (err < 0)  		return err;  	if (copy_to_user(_info, &info, sizeof(struct snd_rawmidi_info)))  		return -EFAULT; @@ -619,77 +629,68 @@ static int snd_rawmidi_info_select_user(struct snd_card *card,  {  	int err;  	struct snd_rawmidi_info info; +  	if (get_user(info.device, &_info->device))  		return -EFAULT;  	if (get_user(info.stream, &_info->stream))  		return -EFAULT;  	if (get_user(info.subdevice, &_info->subdevice))  		return -EFAULT; -	if ((err = snd_rawmidi_info_select(card, &info)) < 0) +	err = snd_rawmidi_info_select(card, &info); +	if (err < 0)  		return err;  	if (copy_to_user(_info, &info, sizeof(struct snd_rawmidi_info)))  		return -EFAULT;  	return 0;  } -int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, -			      struct snd_rawmidi_params * params) +static int resize_runtime_buffer(struct snd_rawmidi_runtime *runtime, +				 struct snd_rawmidi_params *params, +				 bool is_input)  { -	char *newbuf; -	struct snd_rawmidi_runtime *runtime = substream->runtime; -	 -	if (substream->append && substream->use_count > 1) -		return -EBUSY; -	snd_rawmidi_drain_output(substream); -	if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) { +	char *newbuf, *oldbuf; + +	if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L)  		return -EINVAL; -	} -	if (params->avail_min < 1 || params->avail_min > params->buffer_size) { +	if (params->avail_min < 1 || params->avail_min > params->buffer_size)  		return -EINVAL; -	}  	if (params->buffer_size != runtime->buffer_size) { -		newbuf = krealloc(runtime->buffer, params->buffer_size, -				  GFP_KERNEL); +		newbuf = kvmalloc(params->buffer_size, GFP_KERNEL);  		if (!newbuf)  			return -ENOMEM; +		spin_lock_irq(&runtime->lock); +		oldbuf = runtime->buffer;  		runtime->buffer = newbuf;  		runtime->buffer_size = params->buffer_size; -		runtime->avail = runtime->buffer_size; +		__reset_runtime_ptrs(runtime, is_input); +		spin_unlock_irq(&runtime->lock); +		kvfree(oldbuf);  	}  	runtime->avail_min = params->avail_min; -	substream->active_sensing = !params->no_active_sensing;  	return 0;  } + +int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, +			      struct snd_rawmidi_params *params) +{ +	if (substream->append && substream->use_count > 1) +		return -EBUSY; +	snd_rawmidi_drain_output(substream); +	substream->active_sensing = !params->no_active_sensing; +	return resize_runtime_buffer(substream->runtime, params, false); +}  EXPORT_SYMBOL(snd_rawmidi_output_params);  int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, -			     struct snd_rawmidi_params * params) +			     struct snd_rawmidi_params *params)  { -	char *newbuf; -	struct snd_rawmidi_runtime *runtime = substream->runtime; -  	snd_rawmidi_drain_input(substream); -	if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) { -		return -EINVAL; -	} -	if (params->avail_min < 1 || params->avail_min > params->buffer_size) { -		return -EINVAL; -	} -	if (params->buffer_size != runtime->buffer_size) { -		newbuf = krealloc(runtime->buffer, params->buffer_size, -				  GFP_KERNEL); -		if (!newbuf) -			return -ENOMEM; -		runtime->buffer = newbuf; -		runtime->buffer_size = params->buffer_size; -	} -	runtime->avail_min = params->avail_min; -	return 0; +	return resize_runtime_buffer(substream->runtime, params, true);  }  EXPORT_SYMBOL(snd_rawmidi_input_params);  static int snd_rawmidi_output_status(struct snd_rawmidi_substream *substream, -				     struct snd_rawmidi_status * status) +				     struct snd_rawmidi_status *status)  {  	struct snd_rawmidi_runtime *runtime = substream->runtime; @@ -702,7 +703,7 @@ static int snd_rawmidi_output_status(struct snd_rawmidi_substream *substream,  }  static int snd_rawmidi_input_status(struct snd_rawmidi_substream *substream, -				    struct snd_rawmidi_status * status) +				    struct snd_rawmidi_status *status)  {  	struct snd_rawmidi_runtime *runtime = substream->runtime; @@ -731,6 +732,7 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long  	{  		int stream;  		struct snd_rawmidi_info __user *info = argp; +  		if (get_user(stream, &info->stream))  			return -EFAULT;  		switch (stream) { @@ -745,6 +747,7 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long  	case SNDRV_RAWMIDI_IOCTL_PARAMS:  	{  		struct snd_rawmidi_params params; +  		if (copy_from_user(¶ms, argp, sizeof(struct snd_rawmidi_params)))  			return -EFAULT;  		switch (params.stream) { @@ -764,6 +767,7 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long  	{  		int err = 0;  		struct snd_rawmidi_status status; +  		if (copy_from_user(&status, argp, sizeof(struct snd_rawmidi_status)))  			return -EFAULT;  		switch (status.stream) { @@ -789,6 +793,7 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long  	case SNDRV_RAWMIDI_IOCTL_DROP:  	{  		int val; +  		if (get_user(val, (int __user *) argp))  			return -EFAULT;  		switch (val) { @@ -803,6 +808,7 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long  	case SNDRV_RAWMIDI_IOCTL_DRAIN:  	{  		int val; +  		if (get_user(val, (int __user *) argp))  			return -EFAULT;  		switch (val) { @@ -836,7 +842,7 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,  	case SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE:  	{  		int device; -		 +  		if (get_user(device, (int __user *)argp))  			return -EFAULT;  		if (device >= SNDRV_RAWMIDI_DEVICES) /* next device is -1 */ @@ -858,7 +864,7 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,  	case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE:  	{  		int val; -		 +  		if (get_user(val, (int __user *)argp))  			return -EFAULT;  		control->preferred_subdevice[SND_CTL_SUBDEV_RAWMIDI] = val; @@ -1012,6 +1018,7 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun  		spin_lock_irq(&runtime->lock);  		while (!snd_rawmidi_ready(substream)) {  			wait_queue_entry_t wait; +  			if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {  				spin_unlock_irq(&runtime->lock);  				return result > 0 ? result : -EAGAIN; @@ -1064,7 +1071,7 @@ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream)  	spin_lock_irqsave(&runtime->lock, flags);  	result = runtime->avail >= runtime->buffer_size;  	spin_unlock_irqrestore(&runtime->lock, flags); -	return result;		 +	return result;  }  EXPORT_SYMBOL(snd_rawmidi_transmit_empty); @@ -1202,7 +1209,7 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_ack);   * @substream: the rawmidi substream   * @buffer: the buffer pointer   * @count: the data size to transfer - *  + *   * Copies data from the buffer to the device and advances the pointer.   *   * Return: The copied size if successful, or a negative error code on failure. @@ -1316,6 +1323,7 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,  		spin_lock_irq(&runtime->lock);  		while (!snd_rawmidi_ready_append(substream, count)) {  			wait_queue_entry_t wait; +  			if (file->f_flags & O_NONBLOCK) {  				spin_unlock_irq(&runtime->lock);  				return result > 0 ? result : -EAGAIN; @@ -1349,6 +1357,7 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,  		while (runtime->avail != runtime->buffer_size) {  			wait_queue_entry_t wait;  			unsigned int last_avail = runtime->avail; +  			init_waitqueue_entry(&wait, current);  			add_wait_queue(&runtime->sleep, &wait);  			set_current_state(TASK_INTERRUPTIBLE); @@ -1366,7 +1375,7 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,  	return result;  } -static __poll_t snd_rawmidi_poll(struct file *file, poll_table * wait) +static __poll_t snd_rawmidi_poll(struct file *file, poll_table *wait)  {  	struct snd_rawmidi_file *rfile;  	struct snd_rawmidi_runtime *runtime; @@ -1403,7 +1412,6 @@ static __poll_t snd_rawmidi_poll(struct file *file, poll_table * wait)  #endif  /* -   */  static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, @@ -1471,8 +1479,7 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,   *  Register functions   */ -static const struct file_operations snd_rawmidi_f_ops = -{ +static const struct file_operations snd_rawmidi_f_ops = {  	.owner =	THIS_MODULE,  	.read =		snd_rawmidi_read,  	.write =	snd_rawmidi_write, @@ -1527,7 +1534,7 @@ static void release_rawmidi_device(struct device *dev)   */  int snd_rawmidi_new(struct snd_card *card, char *id, int device,  		    int output_count, int input_count, -		    struct snd_rawmidi ** rrawmidi) +		    struct snd_rawmidi **rrawmidi)  {  	struct snd_rawmidi *rmidi;  	int err; @@ -1558,27 +1565,29 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,  	rmidi->dev.release = release_rawmidi_device;  	dev_set_name(&rmidi->dev, "midiC%iD%i", card->number, device); -	if ((err = snd_rawmidi_alloc_substreams(rmidi, -						&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT], -						SNDRV_RAWMIDI_STREAM_INPUT, -						input_count)) < 0) { -		snd_rawmidi_free(rmidi); -		return err; -	} -	if ((err = snd_rawmidi_alloc_substreams(rmidi, -						&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT], -						SNDRV_RAWMIDI_STREAM_OUTPUT, -						output_count)) < 0) { -		snd_rawmidi_free(rmidi); -		return err; -	} -	if ((err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops)) < 0) { -		snd_rawmidi_free(rmidi); -		return err; -	} +	err = snd_rawmidi_alloc_substreams(rmidi, +					   &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT], +					   SNDRV_RAWMIDI_STREAM_INPUT, +					   input_count); +	if (err < 0) +		goto error; +	err = snd_rawmidi_alloc_substreams(rmidi, +					   &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT], +					   SNDRV_RAWMIDI_STREAM_OUTPUT, +					   output_count); +	if (err < 0) +		goto error; +	err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops); +	if (err < 0) +		goto error; +  	if (rrawmidi)  		*rrawmidi = rmidi;  	return 0; + + error: +	snd_rawmidi_free(rmidi); +	return err;  }  EXPORT_SYMBOL(snd_rawmidi_new); @@ -1616,6 +1625,7 @@ static int snd_rawmidi_free(struct snd_rawmidi *rmidi)  static int snd_rawmidi_dev_free(struct snd_device *device)  {  	struct snd_rawmidi *rmidi = device->device_data; +  	return snd_rawmidi_free(rmidi);  } @@ -1623,6 +1633,7 @@ static int snd_rawmidi_dev_free(struct snd_device *device)  static void snd_rawmidi_dev_seq_free(struct snd_seq_device *device)  {  	struct snd_rawmidi *rmidi = device->private_data; +  	rmidi->seq_dev = NULL;  }  #endif @@ -1636,30 +1647,27 @@ static int snd_rawmidi_dev_register(struct snd_device *device)  	if (rmidi->device >= SNDRV_RAWMIDI_DEVICES)  		return -ENOMEM; +	err = 0;  	mutex_lock(®ister_mutex); -	if (snd_rawmidi_search(rmidi->card, rmidi->device)) { -		mutex_unlock(®ister_mutex); -		return -EBUSY; -	} -	list_add_tail(&rmidi->list, &snd_rawmidi_devices); +	if (snd_rawmidi_search(rmidi->card, rmidi->device)) +		err = -EBUSY; +	else +		list_add_tail(&rmidi->list, &snd_rawmidi_devices);  	mutex_unlock(®ister_mutex); +	if (err < 0) +		return err; +  	err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI,  				  rmidi->card, rmidi->device,  				  &snd_rawmidi_f_ops, rmidi, &rmidi->dev);  	if (err < 0) {  		rmidi_err(rmidi, "unable to register\n"); -		mutex_lock(®ister_mutex); -		list_del(&rmidi->list); -		mutex_unlock(®ister_mutex); -		return err; +		goto error;  	} -	if (rmidi->ops && rmidi->ops->dev_register && -	    (err = rmidi->ops->dev_register(rmidi)) < 0) { -		snd_unregister_device(&rmidi->dev); -		mutex_lock(®ister_mutex); -		list_del(&rmidi->list); -		mutex_unlock(®ister_mutex); -		return err; +	if (rmidi->ops && rmidi->ops->dev_register) { +		err = rmidi->ops->dev_register(rmidi); +		if (err < 0) +			goto error_unregister;  	}  #ifdef CONFIG_SND_OSSEMUL  	rmidi->ossreg = 0; @@ -1711,6 +1719,14 @@ static int snd_rawmidi_dev_register(struct snd_device *device)  	}  #endif  	return 0; + + error_unregister: +	snd_unregister_device(&rmidi->dev); + error: +	mutex_lock(®ister_mutex); +	list_del(&rmidi->list); +	mutex_unlock(®ister_mutex); +	return err;  }  static int snd_rawmidi_dev_disconnect(struct snd_device *device) @@ -1724,6 +1740,7 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)  	list_del_init(&rmidi->list);  	for (dir = 0; dir < 2; dir++) {  		struct snd_rawmidi_substream *s; +  		list_for_each_entry(s, &rmidi->streams[dir].substreams, list) {  			if (s->runtime)  				wake_up(&s->runtime->sleep); @@ -1761,7 +1778,7 @@ void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream,  			 const struct snd_rawmidi_ops *ops)  {  	struct snd_rawmidi_substream *substream; -	 +  	list_for_each_entry(substream, &rmidi->streams[stream].substreams, list)  		substream->ops = ops;  } |