diff options
Diffstat (limited to 'sound/core/rawmidi.c')
-rw-r--r-- | sound/core/rawmidi.c | 249 |
1 files changed, 129 insertions, 120 deletions
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index b53026a72e73..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,85 +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, *oldbuf; - 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) { + + 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 = kmalloc(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; - runtime->appl_ptr = runtime->hw_ptr = 0; + __reset_runtime_ptrs(runtime, is_input); spin_unlock_irq(&runtime->lock); - kfree(oldbuf); + 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, *oldbuf; - 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 = kmalloc(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->appl_ptr = runtime->hw_ptr = 0; - spin_unlock_irq(&runtime->lock); - kfree(oldbuf); - } - 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; @@ -710,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; @@ -739,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) { @@ -753,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) { @@ -772,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) { @@ -797,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) { @@ -811,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) { @@ -844,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 */ @@ -866,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; @@ -1020,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; @@ -1072,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); @@ -1210,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. @@ -1324,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; @@ -1357,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); @@ -1374,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; @@ -1411,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, @@ -1479,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, @@ -1535,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; @@ -1566,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); @@ -1624,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); } @@ -1631,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 @@ -1644,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; @@ -1719,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) @@ -1732,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); @@ -1769,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; } |