diff options
Diffstat (limited to '')
-rw-r--r-- | sound/core/rawmidi.c | 417 |
1 files changed, 311 insertions, 106 deletions
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 20dd08e1f675..d8edb6055072 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -35,7 +35,7 @@ module_param_array(amidi_map, int, NULL, 0444); MODULE_PARM_DESC(amidi_map, "Raw MIDI device number assigned to 2nd OSS device."); #endif /* CONFIG_SND_OSSEMUL */ -static int snd_rawmidi_free(struct snd_rawmidi *rawmidi); +static int snd_rawmidi_free(struct snd_rawmidi *rmidi); static int snd_rawmidi_dev_free(struct snd_device *device); static int snd_rawmidi_dev_register(struct snd_device *device); static int snd_rawmidi_dev_disconnect(struct snd_device *device); @@ -95,13 +95,22 @@ static inline unsigned short snd_rawmidi_file_flags(struct file *file) } } -static inline int snd_rawmidi_ready(struct snd_rawmidi_substream *substream) +static inline bool __snd_rawmidi_ready(struct snd_rawmidi_runtime *runtime) { - struct snd_rawmidi_runtime *runtime = substream->runtime; - return runtime->avail >= runtime->avail_min; } +static bool snd_rawmidi_ready(struct snd_rawmidi_substream *substream) +{ + unsigned long flags; + bool ready; + + spin_lock_irqsave(&substream->lock, flags); + ready = __snd_rawmidi_ready(substream->runtime); + spin_unlock_irqrestore(&substream->lock, flags); + return ready; +} + static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substream, size_t count) { @@ -120,6 +129,34 @@ static void snd_rawmidi_input_event_work(struct work_struct *work) runtime->event(runtime->substream); } +/* buffer refcount management: call with substream->lock held */ +static inline void snd_rawmidi_buffer_ref(struct snd_rawmidi_runtime *runtime) +{ + runtime->buffer_ref++; +} + +static inline void snd_rawmidi_buffer_unref(struct snd_rawmidi_runtime *runtime) +{ + runtime->buffer_ref--; +} + +static void snd_rawmidi_buffer_ref_sync(struct snd_rawmidi_substream *substream) +{ + int loop = HZ; + + spin_lock_irq(&substream->lock); + while (substream->runtime->buffer_ref) { + spin_unlock_irq(&substream->lock); + if (!--loop) { + rmidi_err(substream->rmidi, "Buffer ref sync timeout\n"); + return; + } + schedule_timeout_uninterruptible(1); + spin_lock_irq(&substream->lock); + } + spin_unlock_irq(&substream->lock); +} + static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream) { struct snd_rawmidi_runtime *runtime; @@ -128,7 +165,6 @@ static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream) if (!runtime) return -ENOMEM; runtime->substream = substream; - spin_lock_init(&runtime->lock); init_waitqueue_head(&runtime->sleep); INIT_WORK(&runtime->event_work, snd_rawmidi_input_event_work); runtime->event = NULL; @@ -182,35 +218,48 @@ static void __reset_runtime_ptrs(struct snd_rawmidi_runtime *runtime, runtime->avail = is_input ? 0 : runtime->buffer_size; } -static void reset_runtime_ptrs(struct snd_rawmidi_runtime *runtime, +static void reset_runtime_ptrs(struct snd_rawmidi_substream *substream, bool is_input) { unsigned long flags; - spin_lock_irqsave(&runtime->lock, flags); - __reset_runtime_ptrs(runtime, is_input); - spin_unlock_irqrestore(&runtime->lock, flags); + spin_lock_irqsave(&substream->lock, flags); + if (substream->opened && substream->runtime) + __reset_runtime_ptrs(substream->runtime, is_input); + spin_unlock_irqrestore(&substream->lock, flags); } int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream) { snd_rawmidi_output_trigger(substream, 0); - reset_runtime_ptrs(substream->runtime, false); + reset_runtime_ptrs(substream, false); return 0; } EXPORT_SYMBOL(snd_rawmidi_drop_output); int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream) { - int err; + int err = 0; long timeout; - struct snd_rawmidi_runtime *runtime = substream->runtime; + struct snd_rawmidi_runtime *runtime; + + spin_lock_irq(&substream->lock); + runtime = substream->runtime; + if (!substream->opened || !runtime || !runtime->buffer) { + err = -EINVAL; + } else { + snd_rawmidi_buffer_ref(runtime); + runtime->drain = 1; + } + spin_unlock_irq(&substream->lock); + if (err < 0) + return err; - err = 0; - runtime->drain = 1; timeout = wait_event_interruptible_timeout(runtime->sleep, (runtime->avail >= runtime->buffer_size), 10*HZ); + + spin_lock_irq(&substream->lock); if (signal_pending(current)) err = -ERESTARTSYS; if (runtime->avail < runtime->buffer_size && !timeout) { @@ -220,6 +269,8 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream) err = -EIO; } runtime->drain = 0; + spin_unlock_irq(&substream->lock); + if (err != -ERESTARTSYS) { /* we need wait a while to make sure that Tx FIFOs are empty */ if (substream->ops->drain) @@ -228,6 +279,11 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream) msleep(50); snd_rawmidi_drop_output(substream); } + + spin_lock_irq(&substream->lock); + snd_rawmidi_buffer_unref(runtime); + spin_unlock_irq(&substream->lock); + return err; } EXPORT_SYMBOL(snd_rawmidi_drain_output); @@ -235,7 +291,7 @@ EXPORT_SYMBOL(snd_rawmidi_drain_output); int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream) { snd_rawmidi_input_trigger(substream, 0); - reset_runtime_ptrs(substream->runtime, true); + reset_runtime_ptrs(substream, true); return 0; } EXPORT_SYMBOL(snd_rawmidi_drain_input); @@ -290,12 +346,14 @@ static int open_substream(struct snd_rawmidi *rmidi, snd_rawmidi_runtime_free(substream); return err; } + spin_lock_irq(&substream->lock); substream->opened = 1; substream->active_sensing = 0; if (mode & SNDRV_RAWMIDI_LFLG_APPEND) substream->append = 1; substream->pid = get_pid(task_pid(current)); rmidi->streams[substream->stream].substream_opened++; + spin_unlock_irq(&substream->lock); } substream->use_count++; return 0; @@ -426,6 +484,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) err = -ENOMEM; goto __error; } + rawmidi_file->user_pversion = 0; init_waitqueue_entry(&wait, current); add_wait_queue(&rmidi->open_wait, &wait); while (1) { @@ -499,13 +558,16 @@ static void close_substream(struct snd_rawmidi *rmidi, if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS) snd_rawmidi_output_trigger(substream, 0); } + snd_rawmidi_buffer_ref_sync(substream); } + spin_lock_irq(&substream->lock); + substream->opened = 0; + substream->append = 0; + spin_unlock_irq(&substream->lock); substream->ops->close(substream); if (substream->runtime->private_free) substream->runtime->private_free(substream); snd_rawmidi_runtime_free(substream); - substream->opened = 0; - substream->append = 0; put_pid(substream->pid); substream->pid = NULL; rmidi->streams[substream->stream].substream_opened--; @@ -654,26 +716,35 @@ static int snd_rawmidi_info_select_user(struct snd_card *card, return 0; } -static int resize_runtime_buffer(struct snd_rawmidi_runtime *runtime, +static int resize_runtime_buffer(struct snd_rawmidi_substream *substream, struct snd_rawmidi_params *params, bool is_input) { + struct snd_rawmidi_runtime *runtime = substream->runtime; char *newbuf, *oldbuf; + unsigned int framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK; if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) return -EINVAL; + if (framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP && (params->buffer_size & 0x1f) != 0) + return -EINVAL; if (params->avail_min < 1 || params->avail_min > params->buffer_size) return -EINVAL; if (params->buffer_size != runtime->buffer_size) { newbuf = kvzalloc(params->buffer_size, GFP_KERNEL); if (!newbuf) return -ENOMEM; - spin_lock_irq(&runtime->lock); + spin_lock_irq(&substream->lock); + if (runtime->buffer_ref) { + spin_unlock_irq(&substream->lock); + kvfree(newbuf); + return -EBUSY; + } oldbuf = runtime->buffer; runtime->buffer = newbuf; runtime->buffer_size = params->buffer_size; __reset_runtime_ptrs(runtime, is_input); - spin_unlock_irq(&runtime->lock); + spin_unlock_irq(&substream->lock); kvfree(oldbuf); } runtime->avail_min = params->avail_min; @@ -683,19 +754,46 @@ static int resize_runtime_buffer(struct snd_rawmidi_runtime *runtime, int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, struct snd_rawmidi_params *params) { - if (substream->append && substream->use_count > 1) - return -EBUSY; + int err; + snd_rawmidi_drain_output(substream); - substream->active_sensing = !params->no_active_sensing; - return resize_runtime_buffer(substream->runtime, params, false); + mutex_lock(&substream->rmidi->open_mutex); + if (substream->append && substream->use_count > 1) + err = -EBUSY; + else + err = resize_runtime_buffer(substream, params, false); + + if (!err) + substream->active_sensing = !params->no_active_sensing; + mutex_unlock(&substream->rmidi->open_mutex); + return err; } EXPORT_SYMBOL(snd_rawmidi_output_params); int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, struct snd_rawmidi_params *params) { + unsigned int framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK; + unsigned int clock_type = params->mode & SNDRV_RAWMIDI_MODE_CLOCK_MASK; + int err; + snd_rawmidi_drain_input(substream); - return resize_runtime_buffer(substream->runtime, params, true); + mutex_lock(&substream->rmidi->open_mutex); + if (framing == SNDRV_RAWMIDI_MODE_FRAMING_NONE && clock_type != SNDRV_RAWMIDI_MODE_CLOCK_NONE) + err = -EINVAL; + else if (clock_type > SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW) + err = -EINVAL; + else if (framing > SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) + err = -EINVAL; + else + err = resize_runtime_buffer(substream, params, true); + + if (!err) { + substream->framing = framing; + substream->clock_type = clock_type; + } + mutex_unlock(&substream->rmidi->open_mutex); + return 0; } EXPORT_SYMBOL(snd_rawmidi_input_params); @@ -706,9 +804,9 @@ static int snd_rawmidi_output_status(struct snd_rawmidi_substream *substream, memset(status, 0, sizeof(*status)); status->stream = SNDRV_RAWMIDI_STREAM_OUTPUT; - spin_lock_irq(&runtime->lock); + spin_lock_irq(&substream->lock); status->avail = runtime->avail; - spin_unlock_irq(&runtime->lock); + spin_unlock_irq(&substream->lock); return 0; } @@ -719,11 +817,11 @@ static int snd_rawmidi_input_status(struct snd_rawmidi_substream *substream, memset(status, 0, sizeof(*status)); status->stream = SNDRV_RAWMIDI_STREAM_INPUT; - spin_lock_irq(&runtime->lock); + spin_lock_irq(&substream->lock); status->avail = runtime->avail; status->xruns = runtime->xruns; runtime->xruns = 0; - spin_unlock_irq(&runtime->lock); + spin_unlock_irq(&substream->lock); return 0; } @@ -828,12 +926,21 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long return -EINVAL; } } + case SNDRV_RAWMIDI_IOCTL_USER_PVERSION: + if (get_user(rfile->user_pversion, (unsigned int __user *)arg)) + return -EFAULT; + return 0; + case SNDRV_RAWMIDI_IOCTL_PARAMS: { struct snd_rawmidi_params params; if (copy_from_user(¶ms, argp, sizeof(struct snd_rawmidi_params))) return -EFAULT; + if (rfile->user_pversion < SNDRV_PROTOCOL_VERSION(2, 0, 2)) { + params.mode = 0; + memset(params.reserved, 0, sizeof(params.reserved)); + } switch (params.stream) { case SNDRV_RAWMIDI_STREAM_OUTPUT: if (rfile->output == NULL) @@ -937,6 +1044,62 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card, return -ENOIOCTLCMD; } +static int receive_with_tstamp_framing(struct snd_rawmidi_substream *substream, + const unsigned char *buffer, int src_count, const struct timespec64 *tstamp) +{ + struct snd_rawmidi_runtime *runtime = substream->runtime; + struct snd_rawmidi_framing_tstamp *dest_ptr; + struct snd_rawmidi_framing_tstamp frame = { .tv_sec = tstamp->tv_sec, .tv_nsec = tstamp->tv_nsec }; + int dest_frames = 0; + int orig_count = src_count; + int frame_size = sizeof(struct snd_rawmidi_framing_tstamp); + + BUILD_BUG_ON(frame_size != 0x20); + if (snd_BUG_ON((runtime->hw_ptr & 0x1f) != 0)) + return -EINVAL; + + while (src_count > 0) { + if ((int)(runtime->buffer_size - runtime->avail) < frame_size) { + runtime->xruns += src_count; + break; + } + if (src_count >= SNDRV_RAWMIDI_FRAMING_DATA_LENGTH) + frame.length = SNDRV_RAWMIDI_FRAMING_DATA_LENGTH; + else { + frame.length = src_count; + memset(frame.data, 0, SNDRV_RAWMIDI_FRAMING_DATA_LENGTH); + } + memcpy(frame.data, buffer, frame.length); + buffer += frame.length; + src_count -= frame.length; + dest_ptr = (struct snd_rawmidi_framing_tstamp *) (runtime->buffer + runtime->hw_ptr); + *dest_ptr = frame; + runtime->avail += frame_size; + runtime->hw_ptr += frame_size; + runtime->hw_ptr %= runtime->buffer_size; + dest_frames++; + } + return orig_count - src_count; +} + +static struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream) +{ + struct timespec64 ts64 = {0, 0}; + + switch (substream->clock_type) { + case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW: + ktime_get_raw_ts64(&ts64); + break; + case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC: + ktime_get_ts64(&ts64); + break; + case SNDRV_RAWMIDI_MODE_CLOCK_REALTIME: + ktime_get_real_ts64(&ts64); + break; + } + return ts64; +} + /** * snd_rawmidi_receive - receive the input data from the device * @substream: the rawmidi substream @@ -951,18 +1114,26 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, const unsigned char *buffer, int count) { unsigned long flags; + struct timespec64 ts64 = get_framing_tstamp(substream); int result = 0, count1; - struct snd_rawmidi_runtime *runtime = substream->runtime; + struct snd_rawmidi_runtime *runtime; - if (!substream->opened) - return -EBADFD; - if (runtime->buffer == NULL) { + spin_lock_irqsave(&substream->lock, flags); + if (!substream->opened) { + result = -EBADFD; + goto unlock; + } + runtime = substream->runtime; + if (!runtime || !runtime->buffer) { rmidi_dbg(substream->rmidi, "snd_rawmidi_receive: input is not active!!!\n"); - return -EINVAL; + result = -EINVAL; + goto unlock; } - spin_lock_irqsave(&runtime->lock, flags); - if (count == 1) { /* special case, faster code */ + + if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) { + result = receive_with_tstamp_framing(substream, buffer, count, &ts64); + } else if (count == 1) { /* special case, faster code */ substream->bytes++; if (runtime->avail < runtime->buffer_size) { runtime->buffer[runtime->hw_ptr++] = buffer[0]; @@ -1003,10 +1174,11 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, if (result > 0) { if (runtime->event) schedule_work(&runtime->event_work); - else if (snd_rawmidi_ready(substream)) + else if (__snd_rawmidi_ready(runtime)) wake_up(&runtime->sleep); } - spin_unlock_irqrestore(&runtime->lock, flags); + unlock: + spin_unlock_irqrestore(&substream->lock, flags); return result; } EXPORT_SYMBOL(snd_rawmidi_receive); @@ -1019,8 +1191,10 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, long result = 0, count1; struct snd_rawmidi_runtime *runtime = substream->runtime; unsigned long appl_ptr; + int err = 0; - spin_lock_irqsave(&runtime->lock, flags); + spin_lock_irqsave(&substream->lock, flags); + snd_rawmidi_buffer_ref(runtime); while (count > 0 && runtime->avail) { count1 = runtime->buffer_size - runtime->appl_ptr; if (count1 > count) @@ -1037,18 +1211,21 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, if (kernelbuf) memcpy(kernelbuf + result, runtime->buffer + appl_ptr, count1); if (userbuf) { - spin_unlock_irqrestore(&runtime->lock, flags); + spin_unlock_irqrestore(&substream->lock, flags); if (copy_to_user(userbuf + result, - runtime->buffer + appl_ptr, count1)) { - return result > 0 ? result : -EFAULT; - } - spin_lock_irqsave(&runtime->lock, flags); + runtime->buffer + appl_ptr, count1)) + err = -EFAULT; + spin_lock_irqsave(&substream->lock, flags); + if (err) + goto out; } result += count1; count -= count1; } - spin_unlock_irqrestore(&runtime->lock, flags); - return result; + out: + snd_rawmidi_buffer_unref(runtime); + spin_unlock_irqrestore(&substream->lock, flags); + return result > 0 ? result : err; } long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream, @@ -1076,29 +1253,31 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun snd_rawmidi_input_trigger(substream, 1); result = 0; while (count > 0) { - spin_lock_irq(&runtime->lock); - while (!snd_rawmidi_ready(substream)) { + spin_lock_irq(&substream->lock); + while (!__snd_rawmidi_ready(runtime)) { wait_queue_entry_t wait; if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { - spin_unlock_irq(&runtime->lock); + spin_unlock_irq(&substream->lock); return result > 0 ? result : -EAGAIN; } init_waitqueue_entry(&wait, current); add_wait_queue(&runtime->sleep, &wait); set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irq(&runtime->lock); + spin_unlock_irq(&substream->lock); schedule(); remove_wait_queue(&runtime->sleep, &wait); if (rfile->rmidi->card->shutdown) return -ENODEV; if (signal_pending(current)) return result > 0 ? result : -ERESTARTSYS; - if (!runtime->avail) + spin_lock_irq(&substream->lock); + if (!runtime->avail) { + spin_unlock_irq(&substream->lock); return result > 0 ? result : -EIO; - spin_lock_irq(&runtime->lock); + } } - spin_unlock_irq(&runtime->lock); + spin_unlock_irq(&substream->lock); count1 = snd_rawmidi_kernel_read1(substream, (unsigned char __user *)buf, NULL/*kernelbuf*/, @@ -1120,23 +1299,25 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun */ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream) { - struct snd_rawmidi_runtime *runtime = substream->runtime; + struct snd_rawmidi_runtime *runtime; int result; unsigned long flags; - if (runtime->buffer == NULL) { + spin_lock_irqsave(&substream->lock, flags); + runtime = substream->runtime; + if (!substream->opened || !runtime || !runtime->buffer) { rmidi_dbg(substream->rmidi, "snd_rawmidi_transmit_empty: output is not active!!!\n"); - return 1; + result = 1; + } else { + result = runtime->avail >= runtime->buffer_size; } - spin_lock_irqsave(&runtime->lock, flags); - result = runtime->avail >= runtime->buffer_size; - spin_unlock_irqrestore(&runtime->lock, flags); + spin_unlock_irqrestore(&substream->lock, flags); return result; } EXPORT_SYMBOL(snd_rawmidi_transmit_empty); -/** +/* * __snd_rawmidi_transmit_peek - copy data from the internal buffer * @substream: the rawmidi substream * @buffer: the buffer pointer @@ -1144,8 +1325,8 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_empty); * * This is a variant of snd_rawmidi_transmit_peek() without spinlock. */ -int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, - unsigned char *buffer, int count) +static int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, + unsigned char *buffer, int count) { int result, count1; struct snd_rawmidi_runtime *runtime = substream->runtime; @@ -1182,7 +1363,6 @@ 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 @@ -1201,25 +1381,28 @@ EXPORT_SYMBOL(__snd_rawmidi_transmit_peek); 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_lock_irqsave(&substream->lock, flags); + if (!substream->opened || !substream->runtime) + result = -EBADFD; + else + result = __snd_rawmidi_transmit_peek(substream, buffer, count); + spin_unlock_irqrestore(&substream->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) +static int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, + int count) { struct snd_rawmidi_runtime *runtime = substream->runtime; @@ -1234,12 +1417,11 @@ int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int coun runtime->avail += count; substream->bytes += count; if (count > 0) { - if (runtime->drain || snd_rawmidi_ready(substream)) + if (runtime->drain || __snd_rawmidi_ready(runtime)) wake_up(&runtime->sleep); } return count; } -EXPORT_SYMBOL(__snd_rawmidi_transmit_ack); /** * snd_rawmidi_transmit_ack - acknowledge the transmission @@ -1254,13 +1436,15 @@ EXPORT_SYMBOL(__snd_rawmidi_transmit_ack); */ 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); + spin_lock_irqsave(&substream->lock, flags); + if (!substream->opened || !substream->runtime) + result = -EBADFD; + else + result = __snd_rawmidi_transmit_ack(substream, count); + spin_unlock_irqrestore(&substream->lock, flags); return result; } EXPORT_SYMBOL(snd_rawmidi_transmit_ack); @@ -1278,11 +1462,10 @@ 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); + spin_lock_irqsave(&substream->lock, flags); if (!substream->opened) result = -EBADFD; else { @@ -1292,7 +1475,7 @@ int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream, else result = __snd_rawmidi_transmit_ack(substream, count); } - spin_unlock_irqrestore(&runtime->lock, flags); + spin_unlock_irqrestore(&substream->lock, flags); return result; } EXPORT_SYMBOL(snd_rawmidi_transmit); @@ -1305,16 +1488,18 @@ EXPORT_SYMBOL(snd_rawmidi_transmit); */ int snd_rawmidi_proceed(struct snd_rawmidi_substream *substream) { - struct snd_rawmidi_runtime *runtime = substream->runtime; + struct snd_rawmidi_runtime *runtime; unsigned long flags; int count = 0; - spin_lock_irqsave(&runtime->lock, flags); - if (runtime->avail < runtime->buffer_size) { + spin_lock_irqsave(&substream->lock, flags); + runtime = substream->runtime; + if (substream->opened && runtime && + runtime->avail < runtime->buffer_size) { count = runtime->buffer_size - runtime->avail; __snd_rawmidi_transmit_ack(substream, count); } - spin_unlock_irqrestore(&runtime->lock, flags); + spin_unlock_irqrestore(&substream->lock, flags); return count; } EXPORT_SYMBOL(snd_rawmidi_proceed); @@ -1335,13 +1520,14 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, return -EINVAL; result = 0; - spin_lock_irqsave(&runtime->lock, flags); + spin_lock_irqsave(&substream->lock, flags); if (substream->append) { if ((long)runtime->avail < count) { - spin_unlock_irqrestore(&runtime->lock, flags); + spin_unlock_irqrestore(&substream->lock, flags); return -EAGAIN; } } + snd_rawmidi_buffer_ref(runtime); while (count > 0 && runtime->avail > 0) { count1 = runtime->buffer_size - runtime->appl_ptr; if (count1 > count) @@ -1359,21 +1545,22 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, memcpy(runtime->buffer + appl_ptr, kernelbuf + result, count1); else if (userbuf) { - spin_unlock_irqrestore(&runtime->lock, flags); + spin_unlock_irqrestore(&substream->lock, flags); if (copy_from_user(runtime->buffer + appl_ptr, userbuf + result, count1)) { - spin_lock_irqsave(&runtime->lock, flags); + spin_lock_irqsave(&substream->lock, flags); result = result > 0 ? result : -EFAULT; goto __end; } - spin_lock_irqsave(&runtime->lock, flags); + spin_lock_irqsave(&substream->lock, flags); } result += count1; count -= count1; } __end: count1 = runtime->avail < runtime->buffer_size; - spin_unlock_irqrestore(&runtime->lock, flags); + snd_rawmidi_buffer_unref(runtime); + spin_unlock_irqrestore(&substream->lock, flags); if (count1) snd_rawmidi_output_trigger(substream, 1); return result; @@ -1403,29 +1590,31 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf, return -EIO; result = 0; while (count > 0) { - spin_lock_irq(&runtime->lock); + spin_lock_irq(&substream->lock); while (!snd_rawmidi_ready_append(substream, count)) { wait_queue_entry_t wait; if (file->f_flags & O_NONBLOCK) { - spin_unlock_irq(&runtime->lock); + spin_unlock_irq(&substream->lock); return result > 0 ? result : -EAGAIN; } init_waitqueue_entry(&wait, current); add_wait_queue(&runtime->sleep, &wait); set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irq(&runtime->lock); + spin_unlock_irq(&substream->lock); timeout = schedule_timeout(30 * HZ); remove_wait_queue(&runtime->sleep, &wait); if (rfile->rmidi->card->shutdown) return -ENODEV; if (signal_pending(current)) return result > 0 ? result : -ERESTARTSYS; - if (!runtime->avail && !timeout) + spin_lock_irq(&substream->lock); + if (!runtime->avail && !timeout) { + spin_unlock_irq(&substream->lock); return result > 0 ? result : -EIO; - spin_lock_irq(&runtime->lock); + } } - spin_unlock_irq(&runtime->lock); + spin_unlock_irq(&substream->lock); count1 = snd_rawmidi_kernel_write1(substream, buf, NULL, count); if (count1 < 0) return result > 0 ? result : count1; @@ -1436,7 +1625,7 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf, count -= count1; } if (file->f_flags & O_DSYNC) { - spin_lock_irq(&runtime->lock); + spin_lock_irq(&substream->lock); while (runtime->avail != runtime->buffer_size) { wait_queue_entry_t wait; unsigned int last_avail = runtime->avail; @@ -1444,16 +1633,16 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf, init_waitqueue_entry(&wait, current); add_wait_queue(&runtime->sleep, &wait); set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irq(&runtime->lock); + spin_unlock_irq(&substream->lock); timeout = schedule_timeout(30 * HZ); remove_wait_queue(&runtime->sleep, &wait); if (signal_pending(current)) return result > 0 ? result : -ERESTARTSYS; if (runtime->avail == last_avail && !timeout) return result > 0 ? result : -EIO; - spin_lock_irq(&runtime->lock); + spin_lock_irq(&substream->lock); } - spin_unlock_irq(&runtime->lock); + spin_unlock_irq(&substream->lock); } return result; } @@ -1503,6 +1692,9 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, struct snd_rawmidi *rmidi; struct snd_rawmidi_substream *substream; struct snd_rawmidi_runtime *runtime; + unsigned long buffer_size, avail, xruns; + unsigned int clock_type; + static const char *clock_names[4] = { "none", "realtime", "monotonic", "monotonic raw" }; rmidi = entry->private_data; snd_iprintf(buffer, "%s\n\n", rmidi->name); @@ -1521,13 +1713,16 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, " Owner PID : %d\n", pid_vnr(substream->pid)); runtime = substream->runtime; + spin_lock_irq(&substream->lock); + buffer_size = runtime->buffer_size; + avail = runtime->avail; + spin_unlock_irq(&substream->lock); snd_iprintf(buffer, " Mode : %s\n" " Buffer size : %lu\n" " Avail : %lu\n", runtime->oss ? "OSS compatible" : "native", - (unsigned long) runtime->buffer_size, - (unsigned long) runtime->avail); + buffer_size, avail); } } } @@ -1545,13 +1740,24 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, " Owner PID : %d\n", pid_vnr(substream->pid)); runtime = substream->runtime; + spin_lock_irq(&substream->lock); + buffer_size = runtime->buffer_size; + avail = runtime->avail; + xruns = runtime->xruns; + spin_unlock_irq(&substream->lock); snd_iprintf(buffer, " Buffer size : %lu\n" " Avail : %lu\n" " Overruns : %lu\n", - (unsigned long) runtime->buffer_size, - (unsigned long) runtime->avail, - (unsigned long) runtime->xruns); + buffer_size, avail, xruns); + if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) { + clock_type = substream->clock_type >> SNDRV_RAWMIDI_MODE_CLOCK_SHIFT; + if (!snd_BUG_ON(clock_type >= ARRAY_SIZE(clock_names))) + snd_iprintf(buffer, + " Framing : tstamp\n" + " Clock type : %s\n", + clock_names[clock_type]); + } } } } @@ -1590,6 +1796,7 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi, substream->number = idx; substream->rmidi = rmidi; substream->pstr = stream; + spin_lock_init(&substream->lock); list_add_tail(&substream->list, &stream->substreams); stream->substream_count++; } @@ -1642,7 +1849,7 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device, INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams); if (id != NULL) - strlcpy(rmidi->id, id, sizeof(rmidi->id)); + strscpy(rmidi->id, id, sizeof(rmidi->id)); snd_device_initialize(&rmidi->dev, card); rmidi->dev.release = release_rawmidi_device; @@ -1692,10 +1899,8 @@ static int snd_rawmidi_free(struct snd_rawmidi *rmidi) snd_info_free_entry(rmidi->proc_entry); rmidi->proc_entry = NULL; - mutex_lock(®ister_mutex); if (rmidi->ops && rmidi->ops->dev_unregister) rmidi->ops->dev_unregister(rmidi); - mutex_unlock(®ister_mutex); snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]); snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]); |