linux/sound/core/compress_offload.c
<<
>>
Prefs
   1/*
   2 *  compress_core.c - compress offload core
   3 *
   4 *  Copyright (C) 2011 Intel Corporation
   5 *  Authors:    Vinod Koul <vinod.koul@linux.intel.com>
   6 *              Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
   7 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   8 *
   9 *  This program is free software; you can redistribute it and/or modify
  10 *  it under the terms of the GNU General Public License as published by
  11 *  the Free Software Foundation; version 2 of the License.
  12 *
  13 *  This program is distributed in the hope that it will be useful, but
  14 *  WITHOUT ANY WARRANTY; without even the implied warranty of
  15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16 *  General Public License for more details.
  17 *
  18 *  You should have received a copy of the GNU General Public License along
  19 *  with this program; if not, write to the Free Software Foundation, Inc.,
  20 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  21 *
  22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  23 *
  24 */
  25#define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__
  26#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
  27
  28#include <linux/file.h>
  29#include <linux/fs.h>
  30#include <linux/list.h>
  31#include <linux/mm.h>
  32#include <linux/mutex.h>
  33#include <linux/poll.h>
  34#include <linux/slab.h>
  35#include <linux/sched.h>
  36#include <linux/uio.h>
  37#include <linux/uaccess.h>
  38#include <linux/module.h>
  39#include <sound/core.h>
  40#include <sound/initval.h>
  41#include <sound/compress_params.h>
  42#include <sound/compress_offload.h>
  43#include <sound/compress_driver.h>
  44
  45/* TODO:
  46 * - add substream support for multiple devices in case of
  47 *      SND_DYNAMIC_MINORS is not used
  48 * - Multiple node representation
  49 *      driver should be able to register multiple nodes
  50 */
  51
  52static DEFINE_MUTEX(device_mutex);
  53
  54struct snd_compr_file {
  55        unsigned long caps;
  56        struct snd_compr_stream stream;
  57};
  58
  59/*
  60 * a note on stream states used:
  61 * we use follwing states in the compressed core
  62 * SNDRV_PCM_STATE_OPEN: When stream has been opened.
  63 * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
  64 *      calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this
  65 *      state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
  66 * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
  67 *      decoding/encoding and rendering/capturing data.
  68 * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
  69 *      by calling SNDRV_COMPRESS_DRAIN.
  70 * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling
  71 *      SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling
  72 *      SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively.
  73 */
  74static int snd_compr_open(struct inode *inode, struct file *f)
  75{
  76        struct snd_compr *compr;
  77        struct snd_compr_file *data;
  78        struct snd_compr_runtime *runtime;
  79        enum snd_compr_direction dirn;
  80        int maj = imajor(inode);
  81        int ret;
  82
  83        if ((f->f_flags & O_ACCMODE) == O_WRONLY)
  84                dirn = SND_COMPRESS_PLAYBACK;
  85        else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
  86                dirn = SND_COMPRESS_CAPTURE;
  87        else
  88                return -EINVAL;
  89
  90        if (maj == snd_major)
  91                compr = snd_lookup_minor_data(iminor(inode),
  92                                        SNDRV_DEVICE_TYPE_COMPRESS);
  93        else
  94                return -EBADFD;
  95
  96        if (compr == NULL) {
  97                pr_err("no device data!!!\n");
  98                return -ENODEV;
  99        }
 100
 101        if (dirn != compr->direction) {
 102                pr_err("this device doesn't support this direction\n");
 103                return -EINVAL;
 104        }
 105
 106        data = kzalloc(sizeof(*data), GFP_KERNEL);
 107        if (!data)
 108                return -ENOMEM;
 109        data->stream.ops = compr->ops;
 110        data->stream.direction = dirn;
 111        data->stream.private_data = compr->private_data;
 112        data->stream.device = compr;
 113        runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
 114        if (!runtime) {
 115                kfree(data);
 116                return -ENOMEM;
 117        }
 118        runtime->state = SNDRV_PCM_STATE_OPEN;
 119        init_waitqueue_head(&runtime->sleep);
 120        data->stream.runtime = runtime;
 121        f->private_data = (void *)data;
 122        mutex_lock(&compr->lock);
 123        ret = compr->ops->open(&data->stream);
 124        mutex_unlock(&compr->lock);
 125        if (ret) {
 126                kfree(runtime);
 127                kfree(data);
 128        }
 129        return ret;
 130}
 131
 132static int snd_compr_free(struct inode *inode, struct file *f)
 133{
 134        struct snd_compr_file *data = f->private_data;
 135        data->stream.ops->free(&data->stream);
 136        kfree(data->stream.runtime->buffer);
 137        kfree(data->stream.runtime);
 138        kfree(data);
 139        return 0;
 140}
 141
 142static void snd_compr_update_tstamp(struct snd_compr_stream *stream,
 143                struct snd_compr_tstamp *tstamp)
 144{
 145        if (!stream->ops->pointer)
 146                return;
 147        stream->ops->pointer(stream, tstamp);
 148        pr_debug("dsp consumed till %d total %d bytes\n",
 149                tstamp->byte_offset, tstamp->copied_total);
 150        stream->runtime->hw_pointer = tstamp->byte_offset;
 151        stream->runtime->total_bytes_transferred = tstamp->copied_total;
 152}
 153
 154static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
 155                struct snd_compr_avail *avail)
 156{
 157        long avail_calc; /*this needs to be signed variable */
 158
 159        snd_compr_update_tstamp(stream, &avail->tstamp);
 160
 161        /* FIXME: This needs to be different for capture stream,
 162           available is # of compressed data, for playback it's
 163           remainder of buffer */
 164
 165        if (stream->runtime->total_bytes_available == 0 &&
 166                        stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
 167                pr_debug("detected init and someone forgot to do a write\n");
 168                return stream->runtime->buffer_size;
 169        }
 170        pr_debug("app wrote %lld, DSP consumed %lld\n",
 171                        stream->runtime->total_bytes_available,
 172                        stream->runtime->total_bytes_transferred);
 173        if (stream->runtime->total_bytes_available ==
 174                                stream->runtime->total_bytes_transferred) {
 175                pr_debug("both pointers are same, returning full avail\n");
 176                return stream->runtime->buffer_size;
 177        }
 178
 179        /* FIXME: this routine isn't consistent, in one test we use
 180         * cumulative values and in the other byte offsets. Do we
 181         * really need the byte offsets if the cumulative values have
 182         * been updated? In the PCM interface app_ptr and hw_ptr are
 183         * already cumulative */
 184
 185        avail_calc = stream->runtime->buffer_size -
 186                (stream->runtime->app_pointer - stream->runtime->hw_pointer);
 187        pr_debug("calc avail as %ld, app_ptr %lld, hw+ptr %lld\n", avail_calc,
 188                        stream->runtime->app_pointer,
 189                        stream->runtime->hw_pointer);
 190        if (avail_calc >= stream->runtime->buffer_size)
 191                avail_calc -= stream->runtime->buffer_size;
 192        pr_debug("ret avail as %ld\n", avail_calc);
 193        avail->avail = avail_calc;
 194        return avail_calc;
 195}
 196
 197static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)
 198{
 199        struct snd_compr_avail avail;
 200
 201        return snd_compr_calc_avail(stream, &avail);
 202}
 203
 204static int
 205snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
 206{
 207        struct snd_compr_avail ioctl_avail;
 208        size_t avail;
 209
 210        avail = snd_compr_calc_avail(stream, &ioctl_avail);
 211        ioctl_avail.avail = avail;
 212
 213        if (copy_to_user((__u64 __user *)arg,
 214                                &ioctl_avail, sizeof(ioctl_avail)))
 215                return -EFAULT;
 216        return 0;
 217}
 218
 219static int snd_compr_write_data(struct snd_compr_stream *stream,
 220               const char __user *buf, size_t count)
 221{
 222        void *dstn;
 223        size_t copy;
 224        struct snd_compr_runtime *runtime = stream->runtime;
 225
 226        dstn = runtime->buffer + runtime->app_pointer;
 227        pr_debug("copying %ld at %lld\n",
 228                        (unsigned long)count, runtime->app_pointer);
 229        if (count < runtime->buffer_size - runtime->app_pointer) {
 230                if (copy_from_user(dstn, buf, count))
 231                        return -EFAULT;
 232                runtime->app_pointer += count;
 233        } else {
 234                copy = runtime->buffer_size - runtime->app_pointer;
 235                if (copy_from_user(dstn, buf, copy))
 236                        return -EFAULT;
 237                if (copy_from_user(runtime->buffer, buf + copy, count - copy))
 238                        return -EFAULT;
 239                runtime->app_pointer = count - copy;
 240        }
 241        /* if DSP cares, let it know data has been written */
 242        if (stream->ops->ack)
 243                stream->ops->ack(stream, count);
 244        return count;
 245}
 246
 247static ssize_t snd_compr_write(struct file *f, const char __user *buf,
 248                size_t count, loff_t *offset)
 249{
 250        struct snd_compr_file *data = f->private_data;
 251        struct snd_compr_stream *stream;
 252        size_t avail;
 253        int retval;
 254
 255        if (snd_BUG_ON(!data))
 256                return -EFAULT;
 257
 258        stream = &data->stream;
 259        mutex_lock(&stream->device->lock);
 260        /* write is allowed when stream is running or has been steup */
 261        if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
 262                        stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
 263                mutex_unlock(&stream->device->lock);
 264                return -EBADFD;
 265        }
 266
 267        avail = snd_compr_get_avail(stream);
 268        pr_debug("avail returned %ld\n", (unsigned long)avail);
 269        /* calculate how much we can write to buffer */
 270        if (avail > count)
 271                avail = count;
 272
 273        if (stream->ops->copy)
 274                retval = stream->ops->copy(stream, buf, avail);
 275        else
 276                retval = snd_compr_write_data(stream, buf, avail);
 277        if (retval > 0)
 278                stream->runtime->total_bytes_available += retval;
 279
 280        /* while initiating the stream, write should be called before START
 281         * call, so in setup move state */
 282        if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
 283                stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
 284                pr_debug("stream prepared, Houston we are good to go\n");
 285        }
 286
 287        mutex_unlock(&stream->device->lock);
 288        return retval;
 289}
 290
 291
 292static ssize_t snd_compr_read(struct file *f, char __user *buf,
 293                size_t count, loff_t *offset)
 294{
 295        return -ENXIO;
 296}
 297
 298static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
 299{
 300        return -ENXIO;
 301}
 302
 303static inline int snd_compr_get_poll(struct snd_compr_stream *stream)
 304{
 305        if (stream->direction == SND_COMPRESS_PLAYBACK)
 306                return POLLOUT | POLLWRNORM;
 307        else
 308                return POLLIN | POLLRDNORM;
 309}
 310
 311static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
 312{
 313        struct snd_compr_file *data = f->private_data;
 314        struct snd_compr_stream *stream;
 315        size_t avail;
 316        int retval = 0;
 317
 318        if (snd_BUG_ON(!data))
 319                return -EFAULT;
 320        stream = &data->stream;
 321        if (snd_BUG_ON(!stream))
 322                return -EFAULT;
 323
 324        mutex_lock(&stream->device->lock);
 325        if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED ||
 326                        stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
 327                retval = -EBADFD;
 328                goto out;
 329        }
 330        poll_wait(f, &stream->runtime->sleep, wait);
 331
 332        avail = snd_compr_get_avail(stream);
 333        pr_debug("avail is %ld\n", (unsigned long)avail);
 334        /* check if we have at least one fragment to fill */
 335        switch (stream->runtime->state) {
 336        case SNDRV_PCM_STATE_DRAINING:
 337                /* stream has been woken up after drain is complete
 338                 * draining done so set stream state to stopped
 339                 */
 340                retval = snd_compr_get_poll(stream);
 341                stream->runtime->state = SNDRV_PCM_STATE_SETUP;
 342                break;
 343        case SNDRV_PCM_STATE_RUNNING:
 344        case SNDRV_PCM_STATE_PREPARED:
 345        case SNDRV_PCM_STATE_PAUSED:
 346                if (avail >= stream->runtime->fragment_size)
 347                        retval = snd_compr_get_poll(stream);
 348                break;
 349        default:
 350                if (stream->direction == SND_COMPRESS_PLAYBACK)
 351                        retval = POLLOUT | POLLWRNORM | POLLERR;
 352                else
 353                        retval = POLLIN | POLLRDNORM | POLLERR;
 354                break;
 355        }
 356out:
 357        mutex_unlock(&stream->device->lock);
 358        return retval;
 359}
 360
 361static int
 362snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
 363{
 364        int retval;
 365        struct snd_compr_caps caps;
 366
 367        if (!stream->ops->get_caps)
 368                return -ENXIO;
 369
 370        retval = stream->ops->get_caps(stream, &caps);
 371        if (retval)
 372                goto out;
 373        if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
 374                retval = -EFAULT;
 375out:
 376        return retval;
 377}
 378
 379static int
 380snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
 381{
 382        int retval;
 383        struct snd_compr_codec_caps *caps;
 384
 385        if (!stream->ops->get_codec_caps)
 386                return -ENXIO;
 387
 388        caps = kmalloc(sizeof(*caps), GFP_KERNEL);
 389        if (!caps)
 390                return -ENOMEM;
 391
 392        retval = stream->ops->get_codec_caps(stream, caps);
 393        if (retval)
 394                goto out;
 395        if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
 396                retval = -EFAULT;
 397
 398out:
 399        kfree(caps);
 400        return retval;
 401}
 402
 403/* revisit this with snd_pcm_preallocate_xxx */
 404static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
 405                struct snd_compr_params *params)
 406{
 407        unsigned int buffer_size;
 408        void *buffer;
 409
 410        buffer_size = params->buffer.fragment_size * params->buffer.fragments;
 411        if (stream->ops->copy) {
 412                buffer = NULL;
 413                /* if copy is defined the driver will be required to copy
 414                 * the data from core
 415                 */
 416        } else {
 417                buffer = kmalloc(buffer_size, GFP_KERNEL);
 418                if (!buffer)
 419                        return -ENOMEM;
 420        }
 421        stream->runtime->fragment_size = params->buffer.fragment_size;
 422        stream->runtime->fragments = params->buffer.fragments;
 423        stream->runtime->buffer = buffer;
 424        stream->runtime->buffer_size = buffer_size;
 425        return 0;
 426}
 427
 428static int
 429snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
 430{
 431        struct snd_compr_params *params;
 432        int retval;
 433
 434        if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
 435                /*
 436                 * we should allow parameter change only when stream has been
 437                 * opened not in other cases
 438                 */
 439                params = kmalloc(sizeof(*params), GFP_KERNEL);
 440                if (!params)
 441                        return -ENOMEM;
 442                if (copy_from_user(params, (void __user *)arg, sizeof(*params))) {
 443                        retval = -EFAULT;
 444                        goto out;
 445                }
 446                retval = snd_compr_allocate_buffer(stream, params);
 447                if (retval) {
 448                        retval = -ENOMEM;
 449                        goto out;
 450                }
 451                retval = stream->ops->set_params(stream, params);
 452                if (retval)
 453                        goto out;
 454                stream->runtime->state = SNDRV_PCM_STATE_SETUP;
 455        } else {
 456                return -EPERM;
 457        }
 458out:
 459        kfree(params);
 460        return retval;
 461}
 462
 463static int
 464snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
 465{
 466        struct snd_codec *params;
 467        int retval;
 468
 469        if (!stream->ops->get_params)
 470                return -EBADFD;
 471
 472        params = kmalloc(sizeof(*params), GFP_KERNEL);
 473        if (!params)
 474                return -ENOMEM;
 475        retval = stream->ops->get_params(stream, params);
 476        if (retval)
 477                goto out;
 478        if (copy_to_user((char __user *)arg, params, sizeof(*params)))
 479                retval = -EFAULT;
 480
 481out:
 482        kfree(params);
 483        return retval;
 484}
 485
 486static inline int
 487snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
 488{
 489        struct snd_compr_tstamp tstamp;
 490
 491        snd_compr_update_tstamp(stream, &tstamp);
 492        return copy_to_user((struct snd_compr_tstamp __user *)arg,
 493                &tstamp, sizeof(tstamp)) ? -EFAULT : 0;
 494}
 495
 496static int snd_compr_pause(struct snd_compr_stream *stream)
 497{
 498        int retval;
 499
 500        if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
 501                return -EPERM;
 502        retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
 503        if (!retval)
 504                stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
 505        return retval;
 506}
 507
 508static int snd_compr_resume(struct snd_compr_stream *stream)
 509{
 510        int retval;
 511
 512        if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
 513                return -EPERM;
 514        retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
 515        if (!retval)
 516                stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
 517        return retval;
 518}
 519
 520static int snd_compr_start(struct snd_compr_stream *stream)
 521{
 522        int retval;
 523
 524        if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
 525                return -EPERM;
 526        retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
 527        if (!retval)
 528                stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
 529        return retval;
 530}
 531
 532static int snd_compr_stop(struct snd_compr_stream *stream)
 533{
 534        int retval;
 535
 536        if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
 537                        stream->runtime->state == SNDRV_PCM_STATE_SETUP)
 538                return -EPERM;
 539        retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
 540        if (!retval) {
 541                stream->runtime->state = SNDRV_PCM_STATE_SETUP;
 542                wake_up(&stream->runtime->sleep);
 543                stream->runtime->hw_pointer = 0;
 544                stream->runtime->app_pointer = 0;
 545                stream->runtime->total_bytes_available = 0;
 546                stream->runtime->total_bytes_transferred = 0;
 547        }
 548        return retval;
 549}
 550
 551static int snd_compr_drain(struct snd_compr_stream *stream)
 552{
 553        int retval;
 554
 555        if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
 556                        stream->runtime->state == SNDRV_PCM_STATE_SETUP)
 557                return -EPERM;
 558        retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
 559        if (!retval) {
 560                stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
 561                wake_up(&stream->runtime->sleep);
 562        }
 563        return retval;
 564}
 565
 566static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
 567{
 568        struct snd_compr_file *data = f->private_data;
 569        struct snd_compr_stream *stream;
 570        int retval = -ENOTTY;
 571
 572        if (snd_BUG_ON(!data))
 573                return -EFAULT;
 574        stream = &data->stream;
 575        if (snd_BUG_ON(!stream))
 576                return -EFAULT;
 577        mutex_lock(&stream->device->lock);
 578        switch (_IOC_NR(cmd)) {
 579        case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
 580                put_user(SNDRV_COMPRESS_VERSION,
 581                                (int __user *)arg) ? -EFAULT : 0;
 582                break;
 583        case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
 584                retval = snd_compr_get_caps(stream, arg);
 585                break;
 586        case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
 587                retval = snd_compr_get_codec_caps(stream, arg);
 588                break;
 589        case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
 590                retval = snd_compr_set_params(stream, arg);
 591                break;
 592        case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
 593                retval = snd_compr_get_params(stream, arg);
 594                break;
 595        case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
 596                retval = snd_compr_tstamp(stream, arg);
 597                break;
 598        case _IOC_NR(SNDRV_COMPRESS_AVAIL):
 599                retval = snd_compr_ioctl_avail(stream, arg);
 600                break;
 601        case _IOC_NR(SNDRV_COMPRESS_PAUSE):
 602                retval = snd_compr_pause(stream);
 603                break;
 604        case _IOC_NR(SNDRV_COMPRESS_RESUME):
 605                retval = snd_compr_resume(stream);
 606                break;
 607        case _IOC_NR(SNDRV_COMPRESS_START):
 608                retval = snd_compr_start(stream);
 609                break;
 610        case _IOC_NR(SNDRV_COMPRESS_STOP):
 611                retval = snd_compr_stop(stream);
 612                break;
 613        case _IOC_NR(SNDRV_COMPRESS_DRAIN):
 614                retval = snd_compr_drain(stream);
 615                break;
 616        }
 617        mutex_unlock(&stream->device->lock);
 618        return retval;
 619}
 620
 621static const struct file_operations snd_compr_file_ops = {
 622                .owner =        THIS_MODULE,
 623                .open =         snd_compr_open,
 624                .release =      snd_compr_free,
 625                .write =        snd_compr_write,
 626                .read =         snd_compr_read,
 627                .unlocked_ioctl = snd_compr_ioctl,
 628                .mmap =         snd_compr_mmap,
 629                .poll =         snd_compr_poll,
 630};
 631
 632static int snd_compress_dev_register(struct snd_device *device)
 633{
 634        int ret = -EINVAL;
 635        char str[16];
 636        struct snd_compr *compr;
 637
 638        if (snd_BUG_ON(!device || !device->device_data))
 639                return -EBADFD;
 640        compr = device->device_data;
 641
 642        sprintf(str, "comprC%iD%i", compr->card->number, compr->device);
 643        pr_debug("reg %s for device %s, direction %d\n", str, compr->name,
 644                        compr->direction);
 645        /* register compressed device */
 646        ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card,
 647                        compr->device, &snd_compr_file_ops, compr, str);
 648        if (ret < 0) {
 649                pr_err("snd_register_device failed\n %d", ret);
 650                return ret;
 651        }
 652        return ret;
 653
 654}
 655
 656static int snd_compress_dev_disconnect(struct snd_device *device)
 657{
 658        struct snd_compr *compr;
 659
 660        compr = device->device_data;
 661        snd_unregister_device(compr->direction, compr->card, compr->device);
 662        return 0;
 663}
 664
 665/*
 666 * snd_compress_new: create new compress device
 667 * @card: sound card pointer
 668 * @device: device number
 669 * @dirn: device direction, should be of type enum snd_compr_direction
 670 * @compr: compress device pointer
 671 */
 672int snd_compress_new(struct snd_card *card, int device,
 673                        int dirn, struct snd_compr *compr)
 674{
 675        static struct snd_device_ops ops = {
 676                .dev_free = NULL,
 677                .dev_register = snd_compress_dev_register,
 678                .dev_disconnect = snd_compress_dev_disconnect,
 679        };
 680
 681        compr->card = card;
 682        compr->device = device;
 683        compr->direction = dirn;
 684        return snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
 685}
 686EXPORT_SYMBOL_GPL(snd_compress_new);
 687
 688static int snd_compress_add_device(struct snd_compr *device)
 689{
 690        int ret;
 691
 692        if (!device->card)
 693                return -EINVAL;
 694
 695        /* register the card */
 696        ret = snd_card_register(device->card);
 697        if (ret)
 698                goto out;
 699        return 0;
 700
 701out:
 702        pr_err("failed with %d\n", ret);
 703        return ret;
 704
 705}
 706
 707static int snd_compress_remove_device(struct snd_compr *device)
 708{
 709        return snd_card_free(device->card);
 710}
 711
 712/**
 713 * snd_compress_register - register compressed device
 714 *
 715 * @device: compressed device to register
 716 */
 717int snd_compress_register(struct snd_compr *device)
 718{
 719        int retval;
 720
 721        if (device->name == NULL || device->dev == NULL || device->ops == NULL)
 722                return -EINVAL;
 723
 724        pr_debug("Registering compressed device %s\n", device->name);
 725        if (snd_BUG_ON(!device->ops->open))
 726                return -EINVAL;
 727        if (snd_BUG_ON(!device->ops->free))
 728                return -EINVAL;
 729        if (snd_BUG_ON(!device->ops->set_params))
 730                return -EINVAL;
 731        if (snd_BUG_ON(!device->ops->trigger))
 732                return -EINVAL;
 733
 734        mutex_init(&device->lock);
 735
 736        /* register a compressed card */
 737        mutex_lock(&device_mutex);
 738        retval = snd_compress_add_device(device);
 739        mutex_unlock(&device_mutex);
 740        return retval;
 741}
 742EXPORT_SYMBOL_GPL(snd_compress_register);
 743
 744int snd_compress_deregister(struct snd_compr *device)
 745{
 746        pr_debug("Removing compressed device %s\n", device->name);
 747        mutex_lock(&device_mutex);
 748        snd_compress_remove_device(device);
 749        mutex_unlock(&device_mutex);
 750        return 0;
 751}
 752EXPORT_SYMBOL_GPL(snd_compress_deregister);
 753
 754static int __init snd_compress_init(void)
 755{
 756        return 0;
 757}
 758
 759static void __exit snd_compress_exit(void)
 760{
 761}
 762
 763module_init(snd_compress_init);
 764module_exit(snd_compress_exit);
 765
 766MODULE_DESCRIPTION("ALSA Compressed offload framework");
 767MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>");
 768MODULE_LICENSE("GPL v2");
 769