1168 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * compress_core.c - compress offload core4 *5 * Copyright (C) 2011 Intel Corporation6 * Authors: Vinod Koul <vinod.koul@linux.intel.com>7 * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>8 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~9 *10 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~11 */12#define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__13#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)14 15#include <linux/file.h>16#include <linux/fs.h>17#include <linux/list.h>18#include <linux/math64.h>19#include <linux/mm.h>20#include <linux/mutex.h>21#include <linux/poll.h>22#include <linux/slab.h>23#include <linux/sched.h>24#include <linux/types.h>25#include <linux/uio.h>26#include <linux/uaccess.h>27#include <linux/module.h>28#include <linux/compat.h>29#include <sound/core.h>30#include <sound/initval.h>31#include <sound/info.h>32#include <sound/compress_params.h>33#include <sound/compress_offload.h>34#include <sound/compress_driver.h>35 36/* struct snd_compr_codec_caps overflows the ioctl bit size for some37 * architectures, so we need to disable the relevant ioctls.38 */39#if _IOC_SIZEBITS < 1440#define COMPR_CODEC_CAPS_OVERFLOW41#endif42 43/* TODO:44 * - add substream support for multiple devices in case of45 * SND_DYNAMIC_MINORS is not used46 * - Multiple node representation47 * driver should be able to register multiple nodes48 */49 50struct snd_compr_file {51 unsigned long caps;52 struct snd_compr_stream stream;53};54 55static void error_delayed_work(struct work_struct *work);56 57/*58 * a note on stream states used:59 * we use following states in the compressed core60 * SNDRV_PCM_STATE_OPEN: When stream has been opened.61 * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by62 * calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this63 * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.64 * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for65 * playback only). User after setting up stream writes the data buffer66 * before starting the stream.67 * SNDRV_PCM_STATE_RUNNING: When stream has been started and is68 * decoding/encoding and rendering/capturing data.69 * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done70 * by calling SNDRV_COMPRESS_DRAIN.71 * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling72 * SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling73 * SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively.74 */75static int snd_compr_open(struct inode *inode, struct file *f)76{77 struct snd_compr *compr;78 struct snd_compr_file *data;79 struct snd_compr_runtime *runtime;80 enum snd_compr_direction dirn;81 int maj = imajor(inode);82 int ret;83 84 if ((f->f_flags & O_ACCMODE) == O_WRONLY)85 dirn = SND_COMPRESS_PLAYBACK;86 else if ((f->f_flags & O_ACCMODE) == O_RDONLY)87 dirn = SND_COMPRESS_CAPTURE;88 else89 return -EINVAL;90 91 if (maj == snd_major)92 compr = snd_lookup_minor_data(iminor(inode),93 SNDRV_DEVICE_TYPE_COMPRESS);94 else95 return -EBADFD;96 97 if (compr == NULL) {98 pr_err("no device data!!!\n");99 return -ENODEV;100 }101 102 if (dirn != compr->direction) {103 pr_err("this device doesn't support this direction\n");104 snd_card_unref(compr->card);105 return -EINVAL;106 }107 108 data = kzalloc(sizeof(*data), GFP_KERNEL);109 if (!data) {110 snd_card_unref(compr->card);111 return -ENOMEM;112 }113 114 INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work);115 116 data->stream.ops = compr->ops;117 data->stream.direction = dirn;118 data->stream.private_data = compr->private_data;119 data->stream.device = compr;120 runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);121 if (!runtime) {122 kfree(data);123 snd_card_unref(compr->card);124 return -ENOMEM;125 }126 runtime->state = SNDRV_PCM_STATE_OPEN;127 init_waitqueue_head(&runtime->sleep);128 data->stream.runtime = runtime;129 f->private_data = (void *)data;130 scoped_guard(mutex, &compr->lock)131 ret = compr->ops->open(&data->stream);132 if (ret) {133 kfree(runtime);134 kfree(data);135 }136 snd_card_unref(compr->card);137 return ret;138}139 140static int snd_compr_free(struct inode *inode, struct file *f)141{142 struct snd_compr_file *data = f->private_data;143 struct snd_compr_runtime *runtime = data->stream.runtime;144 145 cancel_delayed_work_sync(&data->stream.error_work);146 147 switch (runtime->state) {148 case SNDRV_PCM_STATE_RUNNING:149 case SNDRV_PCM_STATE_DRAINING:150 case SNDRV_PCM_STATE_PAUSED:151 data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP);152 break;153 default:154 break;155 }156 157 data->stream.ops->free(&data->stream);158 if (!data->stream.runtime->dma_buffer_p)159 kfree(data->stream.runtime->buffer);160 kfree(data->stream.runtime);161 kfree(data);162 return 0;163}164 165static int snd_compr_update_tstamp(struct snd_compr_stream *stream,166 struct snd_compr_tstamp *tstamp)167{168 if (!stream->ops->pointer)169 return -ENOTSUPP;170 stream->ops->pointer(stream, tstamp);171 pr_debug("dsp consumed till %d total %d bytes\n",172 tstamp->byte_offset, tstamp->copied_total);173 if (stream->direction == SND_COMPRESS_PLAYBACK)174 stream->runtime->total_bytes_transferred = tstamp->copied_total;175 else176 stream->runtime->total_bytes_available = tstamp->copied_total;177 return 0;178}179 180static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,181 struct snd_compr_avail *avail)182{183 memset(avail, 0, sizeof(*avail));184 snd_compr_update_tstamp(stream, &avail->tstamp);185 /* Still need to return avail even if tstamp can't be filled in */186 187 if (stream->runtime->total_bytes_available == 0 &&188 stream->runtime->state == SNDRV_PCM_STATE_SETUP &&189 stream->direction == SND_COMPRESS_PLAYBACK) {190 pr_debug("detected init and someone forgot to do a write\n");191 return stream->runtime->buffer_size;192 }193 pr_debug("app wrote %lld, DSP consumed %lld\n",194 stream->runtime->total_bytes_available,195 stream->runtime->total_bytes_transferred);196 if (stream->runtime->total_bytes_available ==197 stream->runtime->total_bytes_transferred) {198 if (stream->direction == SND_COMPRESS_PLAYBACK) {199 pr_debug("both pointers are same, returning full avail\n");200 return stream->runtime->buffer_size;201 } else {202 pr_debug("both pointers are same, returning no avail\n");203 return 0;204 }205 }206 207 avail->avail = stream->runtime->total_bytes_available -208 stream->runtime->total_bytes_transferred;209 if (stream->direction == SND_COMPRESS_PLAYBACK)210 avail->avail = stream->runtime->buffer_size - avail->avail;211 212 pr_debug("ret avail as %lld\n", avail->avail);213 return avail->avail;214}215 216static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)217{218 struct snd_compr_avail avail;219 220 return snd_compr_calc_avail(stream, &avail);221}222 223static int224snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)225{226 struct snd_compr_avail ioctl_avail;227 size_t avail;228 229 avail = snd_compr_calc_avail(stream, &ioctl_avail);230 ioctl_avail.avail = avail;231 232 switch (stream->runtime->state) {233 case SNDRV_PCM_STATE_OPEN:234 return -EBADFD;235 case SNDRV_PCM_STATE_XRUN:236 return -EPIPE;237 default:238 break;239 }240 241 if (copy_to_user((__u64 __user *)arg,242 &ioctl_avail, sizeof(ioctl_avail)))243 return -EFAULT;244 return 0;245}246 247static int snd_compr_write_data(struct snd_compr_stream *stream,248 const char __user *buf, size_t count)249{250 void *dstn;251 size_t copy;252 struct snd_compr_runtime *runtime = stream->runtime;253 /* 64-bit Modulus */254 u64 app_pointer = div64_u64(runtime->total_bytes_available,255 runtime->buffer_size);256 app_pointer = runtime->total_bytes_available -257 (app_pointer * runtime->buffer_size);258 259 dstn = runtime->buffer + app_pointer;260 pr_debug("copying %ld at %lld\n",261 (unsigned long)count, app_pointer);262 if (count < runtime->buffer_size - app_pointer) {263 if (copy_from_user(dstn, buf, count))264 return -EFAULT;265 } else {266 copy = runtime->buffer_size - app_pointer;267 if (copy_from_user(dstn, buf, copy))268 return -EFAULT;269 if (copy_from_user(runtime->buffer, buf + copy, count - copy))270 return -EFAULT;271 }272 /* if DSP cares, let it know data has been written */273 if (stream->ops->ack)274 stream->ops->ack(stream, count);275 return count;276}277 278static ssize_t snd_compr_write(struct file *f, const char __user *buf,279 size_t count, loff_t *offset)280{281 struct snd_compr_file *data = f->private_data;282 struct snd_compr_stream *stream;283 size_t avail;284 int retval;285 286 if (snd_BUG_ON(!data))287 return -EFAULT;288 289 stream = &data->stream;290 guard(mutex)(&stream->device->lock);291 /* write is allowed when stream is running or has been setup */292 switch (stream->runtime->state) {293 case SNDRV_PCM_STATE_SETUP:294 case SNDRV_PCM_STATE_PREPARED:295 case SNDRV_PCM_STATE_RUNNING:296 break;297 default:298 return -EBADFD;299 }300 301 avail = snd_compr_get_avail(stream);302 pr_debug("avail returned %ld\n", (unsigned long)avail);303 /* calculate how much we can write to buffer */304 if (avail > count)305 avail = count;306 307 if (stream->ops->copy) {308 char __user* cbuf = (char __user*)buf;309 retval = stream->ops->copy(stream, cbuf, avail);310 } else {311 retval = snd_compr_write_data(stream, buf, avail);312 }313 if (retval > 0)314 stream->runtime->total_bytes_available += retval;315 316 /* while initiating the stream, write should be called before START317 * call, so in setup move state */318 if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {319 stream->runtime->state = SNDRV_PCM_STATE_PREPARED;320 pr_debug("stream prepared, Houston we are good to go\n");321 }322 323 return retval;324}325 326 327static ssize_t snd_compr_read(struct file *f, char __user *buf,328 size_t count, loff_t *offset)329{330 struct snd_compr_file *data = f->private_data;331 struct snd_compr_stream *stream;332 size_t avail;333 int retval;334 335 if (snd_BUG_ON(!data))336 return -EFAULT;337 338 stream = &data->stream;339 guard(mutex)(&stream->device->lock);340 341 /* read is allowed when stream is running, paused, draining and setup342 * (yes setup is state which we transition to after stop, so if user343 * wants to read data after stop we allow that)344 */345 switch (stream->runtime->state) {346 case SNDRV_PCM_STATE_OPEN:347 case SNDRV_PCM_STATE_PREPARED:348 case SNDRV_PCM_STATE_SUSPENDED:349 case SNDRV_PCM_STATE_DISCONNECTED:350 return -EBADFD;351 case SNDRV_PCM_STATE_XRUN:352 return -EPIPE;353 }354 355 avail = snd_compr_get_avail(stream);356 pr_debug("avail returned %ld\n", (unsigned long)avail);357 /* calculate how much we can read from buffer */358 if (avail > count)359 avail = count;360 361 if (stream->ops->copy)362 retval = stream->ops->copy(stream, buf, avail);363 else364 return -ENXIO;365 if (retval > 0)366 stream->runtime->total_bytes_transferred += retval;367 368 return retval;369}370 371static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)372{373 return -ENXIO;374}375 376static __poll_t snd_compr_get_poll(struct snd_compr_stream *stream)377{378 if (stream->direction == SND_COMPRESS_PLAYBACK)379 return EPOLLOUT | EPOLLWRNORM;380 else381 return EPOLLIN | EPOLLRDNORM;382}383 384static __poll_t snd_compr_poll(struct file *f, poll_table *wait)385{386 struct snd_compr_file *data = f->private_data;387 struct snd_compr_stream *stream;388 size_t avail;389 __poll_t retval = 0;390 391 if (snd_BUG_ON(!data))392 return EPOLLERR;393 394 stream = &data->stream;395 396 guard(mutex)(&stream->device->lock);397 398 switch (stream->runtime->state) {399 case SNDRV_PCM_STATE_OPEN:400 case SNDRV_PCM_STATE_XRUN:401 return snd_compr_get_poll(stream) | EPOLLERR;402 default:403 break;404 }405 406 poll_wait(f, &stream->runtime->sleep, wait);407 408 avail = snd_compr_get_avail(stream);409 pr_debug("avail is %ld\n", (unsigned long)avail);410 /* check if we have at least one fragment to fill */411 switch (stream->runtime->state) {412 case SNDRV_PCM_STATE_DRAINING:413 /* stream has been woken up after drain is complete414 * draining done so set stream state to stopped415 */416 retval = snd_compr_get_poll(stream);417 stream->runtime->state = SNDRV_PCM_STATE_SETUP;418 break;419 case SNDRV_PCM_STATE_RUNNING:420 case SNDRV_PCM_STATE_PREPARED:421 case SNDRV_PCM_STATE_PAUSED:422 if (avail >= stream->runtime->fragment_size)423 retval = snd_compr_get_poll(stream);424 break;425 default:426 return snd_compr_get_poll(stream) | EPOLLERR;427 }428 429 return retval;430}431 432static int433snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)434{435 int retval;436 struct snd_compr_caps caps;437 438 if (!stream->ops->get_caps)439 return -ENXIO;440 441 memset(&caps, 0, sizeof(caps));442 retval = stream->ops->get_caps(stream, &caps);443 if (retval)444 goto out;445 if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))446 retval = -EFAULT;447out:448 return retval;449}450 451#ifndef COMPR_CODEC_CAPS_OVERFLOW452static int453snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)454{455 int retval;456 struct snd_compr_codec_caps *caps __free(kfree) = NULL;457 458 if (!stream->ops->get_codec_caps)459 return -ENXIO;460 461 caps = kzalloc(sizeof(*caps), GFP_KERNEL);462 if (!caps)463 return -ENOMEM;464 465 retval = stream->ops->get_codec_caps(stream, caps);466 if (retval)467 return retval;468 if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))469 return -EFAULT;470 return retval;471}472#endif /* !COMPR_CODEC_CAPS_OVERFLOW */473 474int snd_compr_malloc_pages(struct snd_compr_stream *stream, size_t size)475{476 struct snd_dma_buffer *dmab;477 int ret;478 479 if (snd_BUG_ON(!(stream) || !(stream)->runtime))480 return -EINVAL;481 dmab = kzalloc(sizeof(*dmab), GFP_KERNEL);482 if (!dmab)483 return -ENOMEM;484 dmab->dev = stream->dma_buffer.dev;485 ret = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev, size, dmab);486 if (ret < 0) {487 kfree(dmab);488 return ret;489 }490 491 snd_compr_set_runtime_buffer(stream, dmab);492 stream->runtime->dma_bytes = size;493 return 1;494}495EXPORT_SYMBOL(snd_compr_malloc_pages);496 497int snd_compr_free_pages(struct snd_compr_stream *stream)498{499 struct snd_compr_runtime *runtime;500 501 if (snd_BUG_ON(!(stream) || !(stream)->runtime))502 return -EINVAL;503 runtime = stream->runtime;504 if (runtime->dma_area == NULL)505 return 0;506 if (runtime->dma_buffer_p != &stream->dma_buffer) {507 /* It's a newly allocated buffer. Release it now. */508 snd_dma_free_pages(runtime->dma_buffer_p);509 kfree(runtime->dma_buffer_p);510 }511 512 snd_compr_set_runtime_buffer(stream, NULL);513 return 0;514}515EXPORT_SYMBOL(snd_compr_free_pages);516 517/* revisit this with snd_pcm_preallocate_xxx */518static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,519 struct snd_compr_params *params)520{521 unsigned int buffer_size;522 void *buffer = NULL;523 524 buffer_size = params->buffer.fragment_size * params->buffer.fragments;525 if (stream->ops->copy) {526 buffer = NULL;527 /* if copy is defined the driver will be required to copy528 * the data from core529 */530 } else {531 if (stream->runtime->dma_buffer_p) {532 533 if (buffer_size > stream->runtime->dma_buffer_p->bytes)534 dev_err(stream->device->dev,535 "Not enough DMA buffer");536 else537 buffer = stream->runtime->dma_buffer_p->area;538 539 } else {540 buffer = kmalloc(buffer_size, GFP_KERNEL);541 }542 543 if (!buffer)544 return -ENOMEM;545 }546 stream->runtime->fragment_size = params->buffer.fragment_size;547 stream->runtime->fragments = params->buffer.fragments;548 stream->runtime->buffer = buffer;549 stream->runtime->buffer_size = buffer_size;550 return 0;551}552 553static int snd_compress_check_input(struct snd_compr_params *params)554{555 /* first let's check the buffer parameter's */556 if (params->buffer.fragment_size == 0 ||557 params->buffer.fragments > U32_MAX / params->buffer.fragment_size ||558 params->buffer.fragments == 0)559 return -EINVAL;560 561 /* now codec parameters */562 if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX)563 return -EINVAL;564 565 if (params->codec.ch_in == 0 || params->codec.ch_out == 0)566 return -EINVAL;567 568 return 0;569}570 571static int572snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)573{574 struct snd_compr_params *params __free(kfree) = NULL;575 int retval;576 577 if (stream->runtime->state == SNDRV_PCM_STATE_OPEN || stream->next_track) {578 /*579 * we should allow parameter change only when stream has been580 * opened not in other cases581 */582 params = memdup_user((void __user *)arg, sizeof(*params));583 if (IS_ERR(params))584 return PTR_ERR(params);585 586 retval = snd_compress_check_input(params);587 if (retval)588 return retval;589 590 retval = snd_compr_allocate_buffer(stream, params);591 if (retval)592 return -ENOMEM;593 594 retval = stream->ops->set_params(stream, params);595 if (retval)596 return retval;597 598 if (stream->next_track)599 return retval;600 601 stream->metadata_set = false;602 stream->next_track = false;603 604 stream->runtime->state = SNDRV_PCM_STATE_SETUP;605 } else {606 return -EPERM;607 }608 return retval;609}610 611static int612snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)613{614 struct snd_codec *params __free(kfree) = NULL;615 int retval;616 617 if (!stream->ops->get_params)618 return -EBADFD;619 620 params = kzalloc(sizeof(*params), GFP_KERNEL);621 if (!params)622 return -ENOMEM;623 retval = stream->ops->get_params(stream, params);624 if (retval)625 return retval;626 if (copy_to_user((char __user *)arg, params, sizeof(*params)))627 return -EFAULT;628 return retval;629}630 631static int632snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg)633{634 struct snd_compr_metadata metadata;635 int retval;636 637 if (!stream->ops->get_metadata)638 return -ENXIO;639 640 if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))641 return -EFAULT;642 643 retval = stream->ops->get_metadata(stream, &metadata);644 if (retval != 0)645 return retval;646 647 if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata)))648 return -EFAULT;649 650 return 0;651}652 653static int654snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg)655{656 struct snd_compr_metadata metadata;657 int retval;658 659 if (!stream->ops->set_metadata)660 return -ENXIO;661 /*662 * we should allow parameter change only when stream has been663 * opened not in other cases664 */665 if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))666 return -EFAULT;667 668 retval = stream->ops->set_metadata(stream, &metadata);669 stream->metadata_set = true;670 671 return retval;672}673 674static inline int675snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)676{677 struct snd_compr_tstamp tstamp = {0};678 int ret;679 680 ret = snd_compr_update_tstamp(stream, &tstamp);681 if (ret == 0)682 ret = copy_to_user((struct snd_compr_tstamp __user *)arg,683 &tstamp, sizeof(tstamp)) ? -EFAULT : 0;684 return ret;685}686 687static int snd_compr_pause(struct snd_compr_stream *stream)688{689 int retval;690 691 switch (stream->runtime->state) {692 case SNDRV_PCM_STATE_RUNNING:693 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);694 if (!retval)695 stream->runtime->state = SNDRV_PCM_STATE_PAUSED;696 break;697 case SNDRV_PCM_STATE_DRAINING:698 if (!stream->device->use_pause_in_draining)699 return -EPERM;700 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);701 if (!retval)702 stream->pause_in_draining = true;703 break;704 default:705 return -EPERM;706 }707 return retval;708}709 710static int snd_compr_resume(struct snd_compr_stream *stream)711{712 int retval;713 714 switch (stream->runtime->state) {715 case SNDRV_PCM_STATE_PAUSED:716 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);717 if (!retval)718 stream->runtime->state = SNDRV_PCM_STATE_RUNNING;719 break;720 case SNDRV_PCM_STATE_DRAINING:721 if (!stream->pause_in_draining)722 return -EPERM;723 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);724 if (!retval)725 stream->pause_in_draining = false;726 break;727 default:728 return -EPERM;729 }730 return retval;731}732 733static int snd_compr_start(struct snd_compr_stream *stream)734{735 int retval;736 737 switch (stream->runtime->state) {738 case SNDRV_PCM_STATE_SETUP:739 if (stream->direction != SND_COMPRESS_CAPTURE)740 return -EPERM;741 break;742 case SNDRV_PCM_STATE_PREPARED:743 break;744 default:745 return -EPERM;746 }747 748 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);749 if (!retval)750 stream->runtime->state = SNDRV_PCM_STATE_RUNNING;751 return retval;752}753 754static int snd_compr_stop(struct snd_compr_stream *stream)755{756 int retval;757 758 switch (stream->runtime->state) {759 case SNDRV_PCM_STATE_OPEN:760 case SNDRV_PCM_STATE_SETUP:761 case SNDRV_PCM_STATE_PREPARED:762 return -EPERM;763 default:764 break;765 }766 767 retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);768 if (!retval) {769 /* clear flags and stop any drain wait */770 stream->partial_drain = false;771 stream->metadata_set = false;772 stream->pause_in_draining = false;773 snd_compr_drain_notify(stream);774 stream->runtime->total_bytes_available = 0;775 stream->runtime->total_bytes_transferred = 0;776 }777 return retval;778}779 780static void error_delayed_work(struct work_struct *work)781{782 struct snd_compr_stream *stream;783 784 stream = container_of(work, struct snd_compr_stream, error_work.work);785 786 guard(mutex)(&stream->device->lock);787 788 stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);789 wake_up(&stream->runtime->sleep);790}791 792/**793 * snd_compr_stop_error: Report a fatal error on a stream794 * @stream: pointer to stream795 * @state: state to transition the stream to796 *797 * Stop the stream and set its state.798 *799 * Should be called with compressed device lock held.800 *801 * Return: zero if successful, or a negative error code802 */803int snd_compr_stop_error(struct snd_compr_stream *stream,804 snd_pcm_state_t state)805{806 if (stream->runtime->state == state)807 return 0;808 809 stream->runtime->state = state;810 811 pr_debug("Changing state to: %d\n", state);812 813 queue_delayed_work(system_power_efficient_wq, &stream->error_work, 0);814 815 return 0;816}817EXPORT_SYMBOL_GPL(snd_compr_stop_error);818 819static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)820{821 int ret;822 823 /*824 * We are called with lock held. So drop the lock while we wait for825 * drain complete notification from the driver826 *827 * It is expected that driver will notify the drain completion and then828 * stream will be moved to SETUP state, even if draining resulted in an829 * error. We can trigger next track after this.830 */831 stream->runtime->state = SNDRV_PCM_STATE_DRAINING;832 mutex_unlock(&stream->device->lock);833 834 /* we wait for drain to complete here, drain can return when835 * interruption occurred, wait returned error or success.836 * For the first two cases we don't do anything different here and837 * return after waking up838 */839 840 ret = wait_event_interruptible(stream->runtime->sleep,841 (stream->runtime->state != SNDRV_PCM_STATE_DRAINING));842 if (ret == -ERESTARTSYS)843 pr_debug("wait aborted by a signal\n");844 else if (ret)845 pr_debug("wait for drain failed with %d\n", ret);846 847 848 wake_up(&stream->runtime->sleep);849 mutex_lock(&stream->device->lock);850 851 return ret;852}853 854static int snd_compr_drain(struct snd_compr_stream *stream)855{856 int retval;857 858 switch (stream->runtime->state) {859 case SNDRV_PCM_STATE_OPEN:860 case SNDRV_PCM_STATE_SETUP:861 case SNDRV_PCM_STATE_PREPARED:862 case SNDRV_PCM_STATE_PAUSED:863 return -EPERM;864 case SNDRV_PCM_STATE_XRUN:865 return -EPIPE;866 default:867 break;868 }869 870 retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);871 if (retval) {872 pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);873 wake_up(&stream->runtime->sleep);874 return retval;875 }876 877 return snd_compress_wait_for_drain(stream);878}879 880static int snd_compr_next_track(struct snd_compr_stream *stream)881{882 int retval;883 884 /* only a running stream can transition to next track */885 if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)886 return -EPERM;887 888 /* next track doesn't have any meaning for capture streams */889 if (stream->direction == SND_COMPRESS_CAPTURE)890 return -EPERM;891 892 /* you can signal next track if this is intended to be a gapless stream893 * and current track metadata is set894 */895 if (stream->metadata_set == false)896 return -EPERM;897 898 retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK);899 if (retval != 0)900 return retval;901 stream->metadata_set = false;902 stream->next_track = true;903 return 0;904}905 906static int snd_compr_partial_drain(struct snd_compr_stream *stream)907{908 int retval;909 910 switch (stream->runtime->state) {911 case SNDRV_PCM_STATE_OPEN:912 case SNDRV_PCM_STATE_SETUP:913 case SNDRV_PCM_STATE_PREPARED:914 case SNDRV_PCM_STATE_PAUSED:915 return -EPERM;916 case SNDRV_PCM_STATE_XRUN:917 return -EPIPE;918 default:919 break;920 }921 922 /* partial drain doesn't have any meaning for capture streams */923 if (stream->direction == SND_COMPRESS_CAPTURE)924 return -EPERM;925 926 /* stream can be drained only when next track has been signalled */927 if (stream->next_track == false)928 return -EPERM;929 930 stream->partial_drain = true;931 retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);932 if (retval) {933 pr_debug("Partial drain returned failure\n");934 wake_up(&stream->runtime->sleep);935 return retval;936 }937 938 stream->next_track = false;939 return snd_compress_wait_for_drain(stream);940}941 942static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)943{944 struct snd_compr_file *data = f->private_data;945 struct snd_compr_stream *stream;946 947 if (snd_BUG_ON(!data))948 return -EFAULT;949 950 stream = &data->stream;951 952 guard(mutex)(&stream->device->lock);953 switch (_IOC_NR(cmd)) {954 case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):955 return put_user(SNDRV_COMPRESS_VERSION,956 (int __user *)arg) ? -EFAULT : 0;957 case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):958 return snd_compr_get_caps(stream, arg);959#ifndef COMPR_CODEC_CAPS_OVERFLOW960 case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):961 return snd_compr_get_codec_caps(stream, arg);962#endif963 case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):964 return snd_compr_set_params(stream, arg);965 case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):966 return snd_compr_get_params(stream, arg);967 case _IOC_NR(SNDRV_COMPRESS_SET_METADATA):968 return snd_compr_set_metadata(stream, arg);969 case _IOC_NR(SNDRV_COMPRESS_GET_METADATA):970 return snd_compr_get_metadata(stream, arg);971 case _IOC_NR(SNDRV_COMPRESS_TSTAMP):972 return snd_compr_tstamp(stream, arg);973 case _IOC_NR(SNDRV_COMPRESS_AVAIL):974 return snd_compr_ioctl_avail(stream, arg);975 case _IOC_NR(SNDRV_COMPRESS_PAUSE):976 return snd_compr_pause(stream);977 case _IOC_NR(SNDRV_COMPRESS_RESUME):978 return snd_compr_resume(stream);979 case _IOC_NR(SNDRV_COMPRESS_START):980 return snd_compr_start(stream);981 case _IOC_NR(SNDRV_COMPRESS_STOP):982 return snd_compr_stop(stream);983 case _IOC_NR(SNDRV_COMPRESS_DRAIN):984 return snd_compr_drain(stream);985 case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN):986 return snd_compr_partial_drain(stream);987 case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK):988 return snd_compr_next_track(stream);989 }990 991 return -ENOTTY;992}993 994/* support of 32bit userspace on 64bit platforms */995#ifdef CONFIG_COMPAT996static long snd_compr_ioctl_compat(struct file *file, unsigned int cmd,997 unsigned long arg)998{999 return snd_compr_ioctl(file, cmd, (unsigned long)compat_ptr(arg));1000}1001#endif1002 1003static const struct file_operations snd_compr_file_ops = {1004 .owner = THIS_MODULE,1005 .open = snd_compr_open,1006 .release = snd_compr_free,1007 .write = snd_compr_write,1008 .read = snd_compr_read,1009 .unlocked_ioctl = snd_compr_ioctl,1010#ifdef CONFIG_COMPAT1011 .compat_ioctl = snd_compr_ioctl_compat,1012#endif1013 .mmap = snd_compr_mmap,1014 .poll = snd_compr_poll,1015};1016 1017static int snd_compress_dev_register(struct snd_device *device)1018{1019 int ret;1020 struct snd_compr *compr;1021 1022 if (snd_BUG_ON(!device || !device->device_data))1023 return -EBADFD;1024 compr = device->device_data;1025 1026 pr_debug("reg device %s, direction %d\n", compr->name,1027 compr->direction);1028 /* register compressed device */1029 ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS,1030 compr->card, compr->device,1031 &snd_compr_file_ops, compr, compr->dev);1032 if (ret < 0) {1033 pr_err("snd_register_device failed %d\n", ret);1034 return ret;1035 }1036 return ret;1037 1038}1039 1040static int snd_compress_dev_disconnect(struct snd_device *device)1041{1042 struct snd_compr *compr;1043 1044 compr = device->device_data;1045 snd_unregister_device(compr->dev);1046 return 0;1047}1048 1049#ifdef CONFIG_SND_VERBOSE_PROCFS1050static void snd_compress_proc_info_read(struct snd_info_entry *entry,1051 struct snd_info_buffer *buffer)1052{1053 struct snd_compr *compr = (struct snd_compr *)entry->private_data;1054 1055 snd_iprintf(buffer, "card: %d\n", compr->card->number);1056 snd_iprintf(buffer, "device: %d\n", compr->device);1057 snd_iprintf(buffer, "stream: %s\n",1058 compr->direction == SND_COMPRESS_PLAYBACK1059 ? "PLAYBACK" : "CAPTURE");1060 snd_iprintf(buffer, "id: %s\n", compr->id);1061}1062 1063static int snd_compress_proc_init(struct snd_compr *compr)1064{1065 struct snd_info_entry *entry;1066 char name[16];1067 1068 sprintf(name, "compr%i", compr->device);1069 entry = snd_info_create_card_entry(compr->card, name,1070 compr->card->proc_root);1071 if (!entry)1072 return -ENOMEM;1073 entry->mode = S_IFDIR | 0555;1074 compr->proc_root = entry;1075 1076 entry = snd_info_create_card_entry(compr->card, "info",1077 compr->proc_root);1078 if (entry)1079 snd_info_set_text_ops(entry, compr,1080 snd_compress_proc_info_read);1081 compr->proc_info_entry = entry;1082 1083 return 0;1084}1085 1086static void snd_compress_proc_done(struct snd_compr *compr)1087{1088 snd_info_free_entry(compr->proc_info_entry);1089 compr->proc_info_entry = NULL;1090 snd_info_free_entry(compr->proc_root);1091 compr->proc_root = NULL;1092}1093 1094static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)1095{1096 strscpy(compr->id, id, sizeof(compr->id));1097}1098#else1099static inline int snd_compress_proc_init(struct snd_compr *compr)1100{1101 return 0;1102}1103 1104static inline void snd_compress_proc_done(struct snd_compr *compr)1105{1106}1107 1108static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)1109{1110}1111#endif1112 1113static int snd_compress_dev_free(struct snd_device *device)1114{1115 struct snd_compr *compr;1116 1117 compr = device->device_data;1118 snd_compress_proc_done(compr);1119 put_device(compr->dev);1120 return 0;1121}1122 1123/**1124 * snd_compress_new: create new compress device1125 * @card: sound card pointer1126 * @device: device number1127 * @dirn: device direction, should be of type enum snd_compr_direction1128 * @id: ID string1129 * @compr: compress device pointer1130 *1131 * Return: zero if successful, or a negative error code1132 */1133int snd_compress_new(struct snd_card *card, int device,1134 int dirn, const char *id, struct snd_compr *compr)1135{1136 static const struct snd_device_ops ops = {1137 .dev_free = snd_compress_dev_free,1138 .dev_register = snd_compress_dev_register,1139 .dev_disconnect = snd_compress_dev_disconnect,1140 };1141 int ret;1142 1143 compr->card = card;1144 compr->device = device;1145 compr->direction = dirn;1146 mutex_init(&compr->lock);1147 1148 snd_compress_set_id(compr, id);1149 1150 ret = snd_device_alloc(&compr->dev, card);1151 if (ret)1152 return ret;1153 dev_set_name(compr->dev, "comprC%iD%i", card->number, device);1154 1155 ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);1156 if (ret == 0)1157 snd_compress_proc_init(compr);1158 else1159 put_device(compr->dev);1160 1161 return ret;1162}1163EXPORT_SYMBOL_GPL(snd_compress_new);1164 1165MODULE_DESCRIPTION("ALSA Compressed offload framework");1166MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>");1167MODULE_LICENSE("GPL v2");1168