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