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_WRONLY)
  84                dirn = SND_COMPRESS_PLAYBACK;
  85        else if (f->f_flags & O_RDONLY)
  86                dirn = SND_COMPRESS_CAPTURE;
  87        else {
  88                pr_err("invalid direction\n");
  89                return -EINVAL;
  90        }
  91
  92        if (maj == snd_major)
  93                compr = snd_lookup_minor_data(iminor(inode),
  94                                        SNDRV_DEVICE_TYPE_COMPRESS);
  95        else
  96                return -EBADFD;
  97
  98        if (compr == NULL) {
  99                pr_err("no device data!!!\n");
 100                return -ENODEV;
 101        }
 102
 103        if (dirn != compr->direction) {
 104                pr_err("this device doesn't support this direction\n");
 105                return -EINVAL;
 106        }
 107
 108        data = kzalloc(sizeof(*data), GFP_KERNEL);
 109        if (!data)
 110                return -ENOMEM;
 111        data->stream.ops = compr->ops;
 112        data->stream.direction = dirn;
 113        data->stream.private_data = compr->private_data;
 114        data->stream.device = compr;
 115        runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
 116        if (!runtime) {
 117                kfree(data);
 118                return -ENOMEM;
 119        }
 120        runtime->state = SNDRV_PCM_STATE_OPEN;
 121        init_waitqueue_head(&runtime->sleep);
 122        data->stream.runtime = runtime;
 123        f->private_data = (void *)data;
 124        mutex_lock(&compr->lock);
 125        ret = compr->ops->open(&data->stream);
 126        mutex_unlock(&compr->lock);
 127        if (ret) {
 128                kfree(runtime);
 129                kfree(data);
 130        }
 131        return ret;
 132}
 133
 134static int snd_compr_free(struct inode *inode, struct file *f)
 135{
 136        struct snd_compr_file *data = f->private_data;
 137        data->stream.ops->free(&data->stream);
 138        kfree(data->stream.runtime->buffer);
 139        kfree(data->stream.runtime);
 140        kfree(data);
 141        return 0;
 142}
 143
 144static void snd_compr_update_tstamp(struct snd_compr_stream *stream,
 145                struct snd_compr_tstamp *tstamp)
 146{
 147        if (!stream->ops->pointer)
 148                return;
 149        stream->ops->pointer(stream, tstamp);
 150        pr_debug("dsp consumed till %d total %d bytes\n",
 151                tstamp->byte_offset, tstamp->copied_total);
 152        stream->runtime->hw_pointer = tstamp->byte_offset;
 153        stream->runtime->total_bytes_transferred = tstamp->copied_total;
 154}
 155
 156static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
 157                struct snd_compr_avail *avail)
 158{
 159        long avail_calc; /*this needs to be signed variable */
 160
 161        snd_compr_update_tstamp(stream, &avail->tstamp);
 162
 163        /* FIXME: This needs to be different for capture stream,
 164           available is # of compressed data, for playback it's
 165           remainder of buffer */
 166
 167        if (stream->runtime->total_bytes_available == 0 &&
 168                        stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
 169                pr_debug("detected init and someone forgot to do a write\n");
 170                return stream->runtime->buffer_size;
 171        }
 172        pr_debug("app wrote %lld, DSP consumed %lld\n",
 173                        stream->runtime->total_bytes_available,
 174                        stream->runtime->total_bytes_transferred);
 175        if (stream->runtime->total_bytes_available ==
 176                                stream->runtime->total_bytes_transferred) {
 177                pr_debug("both pointers are same, returning full avail\n");
 178                return stream->runtime->buffer_size;
 179        }
 180
 181        /* FIXME: this routine isn't consistent, in one test we use
 182         * cumulative values and in the other byte offsets. Do we
 183         * really need the byte offsets if the cumulative values have
 184         * been updated? In the PCM interface app_ptr and hw_ptr are
 185         * already cumulative */
 186
 187        avail_calc = stream->runtime->buffer_size -
 188                (stream->runtime->app_pointer - stream->runtime->hw_pointer);
 189        pr_debug("calc avail as %ld, app_ptr %lld, hw+ptr %lld\n", avail_calc,
 190                        stream->runtime->app_pointer,
 191                        stream->runtime->hw_pointer);
 192        if (avail_calc >= stream->runtime->buffer_size)
 193                avail_calc -= stream->runtime->buffer_size;
 194        pr_debug("ret avail as %ld\n", avail_calc);
 195        avail->avail = avail_calc;
 196        return avail_calc;
 197}
 198
 199static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)
 200{
 201        struct snd_compr_avail avail;
 202
 203        return snd_compr_calc_avail(stream, &avail);
 204}
 205
 206static int
 207snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
 208{
 209        struct snd_compr_avail ioctl_avail;
 210        size_t avail;
 211
 212        avail = snd_compr_calc_avail(stream, &ioctl_avail);
 213        ioctl_avail.avail = avail;
 214
 215        if (copy_to_user((__u64 __user *)arg,
 216                                &ioctl_avail, sizeof(ioctl_avail)))
 217                return -EFAULT;
 218        return 0;
 219}
 220
 221static int snd_compr_write_data(struct snd_compr_stream *stream,
 222               const char __user *buf, size_t count)
 223{
 224        void *dstn;
 225        size_t copy;
 226        struct snd_compr_runtime *runtime = stream->runtime;
 227
 228        dstn = runtime->buffer + runtime->app_pointer;
 229        pr_debug("copying %ld at %lld\n",
 230                        (unsigned long)count, runtime->app_pointer);
 231        if (count < runtime->buffer_size - runtime->app_pointer) {
 232                if (copy_from_user(dstn, buf, count))
 233                        return -EFAULT;
 234                runtime->app_pointer += count;
 235        } else {
 236                copy = runtime->buffer_size - runtime->app_pointer;
 237                if (copy_from_user(dstn, buf, copy))
 238                        return -EFAULT;
 239                if (copy_from_user(runtime->buffer, buf + copy, count - copy))
 240                        return -EFAULT;
 241                runtime->app_pointer = count - copy;
 242        }
 243        /* if DSP cares, let it know data has been written */
 244        if (stream->ops->ack)
 245                stream->ops->ack(stream, count);
 246        return count;
 247}
 248
 249static ssize_t snd_compr_write(struct file *f, const char __user *buf,
 250                size_t count, loff_t *offset)
 251{
 252        struct snd_compr_file *data = f->private_data;
 253        struct snd_compr_stream *stream;
 254        size_t avail;
 255        int retval;
 256
 257        if (snd_BUG_ON(!data))
 258                return -EFAULT;
 259
 260        stream = &data->stream;
 261        mutex_lock(&stream->device->lock);
 262        /* write is allowed when stream is running or has been steup */
 263        if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
 264                        stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
 265                mutex_unlock(&stream->device->lock);
 266                return -EBADFD;
 267        }
 268
 269        avail = snd_compr_get_avail(stream);
 270        pr_debug("avail returned %ld\n", (unsigned long)avail);
 271        /* calculate how much we can write to buffer */
 272        if (avail > count)
 273                avail = count;
 274
 275        if (stream->ops->copy)
 276                retval = stream->ops->copy(stream, buf, avail);
 277        else
 278                retval = snd_compr_write_data(stream, buf, avail);
 279        if (retval > 0)
 280                stream->runtime->total_bytes_available += retval;
 281
 282        /* while initiating the stream, write should be called before START
 283         * call, so in setup move state */
 284        if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
 285                stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
 286                pr_debug("stream prepared, Houston we are good to go\n");
 287        }
 288
 289        mutex_unlock(&stream->device->lock);
 290        return retval;
 291}
 292
 293
 294static ssize_t snd_compr_read(struct file *f, char __user *buf,
 295                size_t count, loff_t *offset)
 296{
 297        return -ENXIO;
 298}
 299
 300static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
 301{
 302        return -ENXIO;
 303}
 304
 305static inline int snd_compr_get_poll(struct snd_compr_stream *stream)
 306{
 307        if (stream->direction == SND_COMPRESS_PLAYBACK)
 308                return POLLOUT | POLLWRNORM;
 309        else
 310                return POLLIN | POLLRDNORM;
 311}
 312
 313static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
 314{
 315        struct snd_compr_file *data = f->private_data;
 316        struct snd_compr_stream *stream;
 317        size_t avail;
 318        int retval = 0;
 319
 320        if (snd_BUG_ON(!data))
 321                return -EFAULT;
 322        stream = &data->stream;
 323        if (snd_BUG_ON(!stream))
 324                return -EFAULT;
 325
 326        mutex_lock(&stream->device->lock);
 327        if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED ||
 328                        stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
 329                retval = -EBADFD;
 330                goto out;
 331        }
 332        poll_wait(f, &stream->runtime->sleep, wait);
 333
 334        avail = snd_compr_get_avail(stream);
 335        pr_debug("avail is %ld\n", (unsigned long)avail);
 336        /* check if we have at least one fragment to fill */
 337        switch (stream->runtime->state) {
 338        case SNDRV_PCM_STATE_DRAINING:
 339                /* stream has been woken up after drain is complete
 340                 * draining done so set stream state to stopped
 341                 */
 342                retval = snd_compr_get_poll(stream);
 343                stream->runtime->state = SNDRV_PCM_STATE_SETUP;
 344                break;
 345        case SNDRV_PCM_STATE_RUNNING:
 346        case SNDRV_PCM_STATE_PREPARED:
 347        case SNDRV_PCM_STATE_PAUSED:
 348                if (avail >= stream->runtime->fragment_size)
 349                        retval = snd_compr_get_poll(stream);
 350                break;
 351        default:
 352                if (stream->direction == SND_COMPRESS_PLAYBACK)
 353                        retval = POLLOUT | POLLWRNORM | POLLERR;
 354                else
 355                        retval = POLLIN | POLLRDNORM | POLLERR;
 356                break;
 357        }
 358out:
 359        mutex_unlock(&stream->device->lock);
 360        return retval;
 361}
 362
 363static int
 364snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
 365{
 366        int retval;
 367        struct snd_compr_caps caps;
 368
 369        if (!stream->ops->get_caps)
 370                return -ENXIO;
 371
 372        retval = stream->ops->get_caps(stream, &caps);
 373        if (retval)
 374                goto out;
 375        if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
 376                retval = -EFAULT;
 377out:
 378        return retval;
 379}
 380
 381static int
 382snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
 383{
 384        int retval;
 385        struct snd_compr_codec_caps *caps;
 386
 387        if (!stream->ops->get_codec_caps)
 388                return -ENXIO;
 389
 390        caps = kmalloc(sizeof(*caps), GFP_KERNEL);
 391        if (!caps)
 392                return -ENOMEM;
 393
 394        retval = stream->ops->get_codec_caps(stream, caps);
 395        if (retval)
 396                goto out;
 397        if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
 398                retval = -EFAULT;
 399
 400out:
 401        kfree(caps);
 402        return retval;
 403}
 404
 405/* revisit this with snd_pcm_preallocate_xxx */
 406static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
 407                struct snd_compr_params *params)
 408{
 409        unsigned int buffer_size;
 410        void *buffer;
 411
 412        buffer_size = params->buffer.fragment_size * params->buffer.fragments;
 413        if (stream->ops->copy) {
 414                buffer = NULL;
 415                /* if copy is defined the driver will be required to copy
 416                 * the data from core
 417                 */
 418        } else {
 419                buffer = kmalloc(buffer_size, GFP_KERNEL);
 420                if (!buffer)
 421                        return -ENOMEM;
 422        }
 423        stream->runtime->fragment_size = params->buffer.fragment_size;
 424        stream->runtime->fragments = params->buffer.fragments;
 425        stream->runtime->buffer = buffer;
 426        stream->runtime->buffer_size = buffer_size;
 427        return 0;
 428}
 429
 430static int
 431snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
 432{
 433        struct snd_compr_params *params;
 434        int retval;
 435
 436        if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
 437                /*
 438                 * we should allow parameter change only when stream has been
 439                 * opened not in other cases
 440                 */
 441                params = kmalloc(sizeof(*params), GFP_KERNEL);
 442                if (!params)
 443                        return -ENOMEM;
 444                if (copy_from_user(params, (void __user *)arg, sizeof(*params))) {
 445                        retval = -EFAULT;
 446                        goto out;
 447                }
 448                retval = snd_compr_allocate_buffer(stream, params);
 449                if (retval) {
 450                        retval = -ENOMEM;
 451                        goto out;
 452                }
 453                retval = stream->ops->set_params(stream, params);
 454                if (retval)
 455                        goto out;
 456                stream->runtime->state = SNDRV_PCM_STATE_SETUP;
 457        } else {
 458                return -EPERM;
 459        }
 460out:
 461        kfree(params);
 462        return retval;
 463}
 464
 465static int
 466snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
 467{
 468        struct snd_codec *params;
 469        int retval;
 470
 471        if (!stream->ops->get_params)
 472                return -EBADFD;
 473
 474        params = kmalloc(sizeof(*params), GFP_KERNEL);
 475        if (!params)
 476                return -ENOMEM;
 477        retval = stream->ops->get_params(stream, params);
 478        if (retval)
 479                goto out;
 480        if (copy_to_user((char __user *)arg, params, sizeof(*params)))
 481                retval = -EFAULT;
 482
 483out:
 484        kfree(params);
 485        return retval;
 486}
 487
 488static inline int
 489snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
 490{
 491        struct snd_compr_tstamp tstamp;
 492
 493        snd_compr_update_tstamp(stream, &tstamp);
 494        return copy_to_user((struct snd_compr_tstamp __user *)arg,
 495                &tstamp, sizeof(tstamp)) ? -EFAULT : 0;
 496}
 497
 498static int snd_compr_pause(struct snd_compr_stream *stream)
 499{
 500        int retval;
 501
 502        if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
 503                return -EPERM;
 504        retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
 505        if (!retval) {
 506                stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
 507                wake_up(&stream->runtime->sleep);
 508        }
 509        return retval;
 510}
 511
 512static int snd_compr_resume(struct snd_compr_stream *stream)
 513{
 514        int retval;
 515
 516        if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED)
 517                return -EPERM;
 518        retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
 519        if (!retval)
 520                stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
 521        return retval;
 522}
 523
 524static int snd_compr_start(struct snd_compr_stream *stream)
 525{
 526        int retval;
 527
 528        if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED)
 529                return -EPERM;
 530        retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
 531        if (!retval)
 532                stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
 533        return retval;
 534}
 535
 536static int snd_compr_stop(struct snd_compr_stream *stream)
 537{
 538        int retval;
 539
 540        if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
 541                        stream->runtime->state == SNDRV_PCM_STATE_SETUP)
 542                return -EPERM;
 543        retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
 544        if (!retval) {
 545                stream->runtime->state = SNDRV_PCM_STATE_SETUP;
 546                wake_up(&stream->runtime->sleep);
 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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.