diff options
Diffstat (limited to 'sound/core')
-rw-r--r-- | sound/core/compress_offload.c | 67 | ||||
-rw-r--r-- | sound/core/control.c | 34 | ||||
-rw-r--r-- | sound/core/pcm.c | 14 | ||||
-rw-r--r-- | sound/core/seq/oss/seq_oss_synth.c | 10 | ||||
-rw-r--r-- | sound/core/seq/seq_timer.c | 23 | ||||
-rw-r--r-- | sound/core/seq/seq_timer.h | 2 | ||||
-rw-r--r-- | sound/core/timer.c | 2 |
7 files changed, 125 insertions, 27 deletions
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 9b3334be9df2..2c498488af6c 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -67,6 +67,8 @@ struct snd_compr_file { struct snd_compr_stream stream; }; +static void error_delayed_work(struct work_struct *work); + /* * a note on stream states used: * we use following states in the compressed core @@ -123,6 +125,9 @@ static int snd_compr_open(struct inode *inode, struct file *f) snd_card_unref(compr->card); return -ENOMEM; } + + INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work); + data->stream.ops = compr->ops; data->stream.direction = dirn; data->stream.private_data = compr->private_data; @@ -153,6 +158,8 @@ static int snd_compr_free(struct inode *inode, struct file *f) struct snd_compr_file *data = f->private_data; struct snd_compr_runtime *runtime = data->stream.runtime; + cancel_delayed_work_sync(&data->stream.error_work); + switch (runtime->state) { case SNDRV_PCM_STATE_RUNNING: case SNDRV_PCM_STATE_DRAINING: @@ -237,6 +244,15 @@ snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg) avail = snd_compr_calc_avail(stream, &ioctl_avail); ioctl_avail.avail = avail; + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_OPEN: + return -EBADFD; + case SNDRV_PCM_STATE_XRUN: + return -EPIPE; + default: + break; + } + if (copy_to_user((__u64 __user *)arg, &ioctl_avail, sizeof(ioctl_avail))) return -EFAULT; @@ -346,11 +362,13 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf, switch (stream->runtime->state) { case SNDRV_PCM_STATE_OPEN: case SNDRV_PCM_STATE_PREPARED: - case SNDRV_PCM_STATE_XRUN: case SNDRV_PCM_STATE_SUSPENDED: case SNDRV_PCM_STATE_DISCONNECTED: retval = -EBADFD; goto out; + case SNDRV_PCM_STATE_XRUN: + retval = -EPIPE; + goto out; } avail = snd_compr_get_avail(stream); @@ -399,10 +417,16 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait) stream = &data->stream; mutex_lock(&stream->device->lock); - if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { + + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_XRUN: retval = snd_compr_get_poll(stream) | POLLERR; goto out; + default: + break; } + poll_wait(f, &stream->runtime->sleep, wait); avail = snd_compr_get_avail(stream); @@ -697,6 +721,45 @@ static int snd_compr_stop(struct snd_compr_stream *stream) return retval; } +static void error_delayed_work(struct work_struct *work) +{ + struct snd_compr_stream *stream; + + stream = container_of(work, struct snd_compr_stream, error_work.work); + + mutex_lock(&stream->device->lock); + + stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); + wake_up(&stream->runtime->sleep); + + mutex_unlock(&stream->device->lock); +} + +/* + * snd_compr_stop_error: Report a fatal error on a stream + * @stream: pointer to stream + * @state: state to transition the stream to + * + * Stop the stream and set its state. + * + * Should be called with compressed device lock held. + */ +int snd_compr_stop_error(struct snd_compr_stream *stream, + snd_pcm_state_t state) +{ + if (stream->runtime->state == state) + return 0; + + stream->runtime->state = state; + + pr_debug("Changing state to: %d\n", state); + + queue_delayed_work(system_power_efficient_wq, &stream->error_work, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_compr_stop_error); + static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) { int ret; diff --git a/sound/core/control.c b/sound/core/control.c index a85d45595d02..fb096cb20a80 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -160,6 +160,8 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, if (snd_BUG_ON(!card || !id)) return; + if (card->shutdown) + return; read_lock(&card->ctl_files_rwlock); #if IS_ENABLED(CONFIG_SND_MIXER_OSS) card->mixer_oss_change_count++; @@ -805,6 +807,36 @@ static int snd_ctl_elem_list(struct snd_card *card, return 0; } +static bool validate_element_member_dimension(struct snd_ctl_elem_info *info) +{ + unsigned int members; + unsigned int i; + + if (info->dimen.d[0] == 0) + return true; + + members = 1; + for (i = 0; i < ARRAY_SIZE(info->dimen.d); ++i) { + if (info->dimen.d[i] == 0) + break; + members *= info->dimen.d[i]; + + /* + * info->count should be validated in advance, to guarantee + * calculation soundness. + */ + if (members > info->count) + return false; + } + + for (++i; i < ARRAY_SIZE(info->dimen.d); ++i) { + if (info->dimen.d[i] > 0) + return false; + } + + return members == info->count; +} + static int snd_ctl_elem_info(struct snd_ctl_file *ctl, struct snd_ctl_elem_info *info) { @@ -1272,6 +1304,8 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, if (info->count < 1 || info->count > max_value_counts[info->type]) return -EINVAL; + if (!validate_element_member_dimension(info)) + return -EINVAL; private_size = value_sizes[info->type] * info->count; /* diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 308c9ecf73db..8e980aa678d0 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -849,6 +849,14 @@ int snd_pcm_new_internal(struct snd_card *card, const char *id, int device, } EXPORT_SYMBOL(snd_pcm_new_internal); +static void free_chmap(struct snd_pcm_str *pstr) +{ + if (pstr->chmap_kctl) { + snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl); + pstr->chmap_kctl = NULL; + } +} + static void snd_pcm_free_stream(struct snd_pcm_str * pstr) { struct snd_pcm_substream *substream, *substream_next; @@ -871,6 +879,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr) kfree(setup); } #endif + free_chmap(pstr); if (pstr->substream_count) put_device(&pstr->dev); } @@ -1135,10 +1144,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) for (cidx = 0; cidx < 2; cidx++) { if (!pcm->internal) snd_unregister_device(&pcm->streams[cidx].dev); - if (pcm->streams[cidx].chmap_kctl) { - snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl); - pcm->streams[cidx].chmap_kctl = NULL; - } + free_chmap(&pcm->streams[cidx]); } mutex_unlock(&pcm->open_mutex); mutex_unlock(®ister_mutex); diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index b16dbef04174..cd0e0ebbfdb1 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -70,11 +70,11 @@ struct seq_oss_synth { static int max_synth_devs; static struct seq_oss_synth *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS]; static struct seq_oss_synth midi_synth_dev = { - -1, /* seq_device */ - SYNTH_TYPE_MIDI, /* synth_type */ - 0, /* synth_subtype */ - 16, /* nr_voices */ - "MIDI", /* name */ + .seq_device = -1, + .synth_type = SYNTH_TYPE_MIDI, + .synth_subtype = 0, + .nr_voices = 16, + .name = "MIDI", }; static DEFINE_SPINLOCK(register_lock); diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index 293104926098..dcc102813aef 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -165,7 +165,7 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri, snd_seq_timer_update_tick(&tmr->tick, resolution); /* register actual time of this timer update */ - do_gettimeofday(&tmr->last_update); + ktime_get_ts64(&tmr->last_update); spin_unlock_irqrestore(&tmr->lock, flags); @@ -392,7 +392,7 @@ static int seq_timer_start(struct snd_seq_timer *tmr) return -EINVAL; snd_timer_start(tmr->timeri, tmr->ticks); tmr->running = 1; - do_gettimeofday(&tmr->last_update); + ktime_get_ts64(&tmr->last_update); return 0; } @@ -420,7 +420,7 @@ static int seq_timer_continue(struct snd_seq_timer *tmr) } snd_timer_start(tmr->timeri, tmr->ticks); tmr->running = 1; - do_gettimeofday(&tmr->last_update); + ktime_get_ts64(&tmr->last_update); return 0; } @@ -444,17 +444,12 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr) spin_lock_irqsave(&tmr->lock, flags); cur_time = tmr->cur_time; if (tmr->running) { - struct timeval tm; - int usec; - do_gettimeofday(&tm); - usec = (int)(tm.tv_usec - tmr->last_update.tv_usec); - if (usec < 0) { - cur_time.tv_nsec += (1000000 + usec) * 1000; - cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1; - } else { - cur_time.tv_nsec += usec * 1000; - cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec; - } + struct timespec64 tm; + + ktime_get_ts64(&tm); + tm = timespec64_sub(tm, tmr->last_update); + cur_time.tv_nsec = tm.tv_nsec; + cur_time.tv_sec = tm.tv_sec; snd_seq_sanity_real_time(&cur_time); } spin_unlock_irqrestore(&tmr->lock, flags); diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h index 88dfb71805ae..9506b661fe5b 100644 --- a/sound/core/seq/seq_timer.h +++ b/sound/core/seq/seq_timer.h @@ -52,7 +52,7 @@ struct snd_seq_timer { unsigned int skew; unsigned int skew_base; - struct timeval last_update; /* time of last clock update, used for interpolation */ + struct timespec64 last_update; /* time of last clock update, used for interpolation */ spinlock_t lock; }; diff --git a/sound/core/timer.c b/sound/core/timer.c index e722022d325d..9a6157ea6881 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1955,6 +1955,7 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, qhead = tu->qhead++; tu->qhead %= tu->queue_size; + tu->qused--; spin_unlock_irq(&tu->qlock); if (tu->tread) { @@ -1968,7 +1969,6 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, } spin_lock_irq(&tu->qlock); - tu->qused--; if (err < 0) goto _error; result += unit; |