linux/sound/core/pcm_compat.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *   32bit -> 64bit ioctl wrapper for PCM API
   4 *   Copyright (c) by Takashi Iwai <tiwai@suse.de>
   5 */
   6
   7/* This file included from pcm_native.c */
   8
   9#include <linux/compat.h>
  10#include <linux/slab.h>
  11
  12static int snd_pcm_ioctl_delay_compat(struct snd_pcm_substream *substream,
  13                                      s32 __user *src)
  14{
  15        snd_pcm_sframes_t delay;
  16        int err;
  17
  18        err = snd_pcm_delay(substream, &delay);
  19        if (err)
  20                return err;
  21        if (put_user(delay, src))
  22                return -EFAULT;
  23        return 0;
  24}
  25
  26static int snd_pcm_ioctl_rewind_compat(struct snd_pcm_substream *substream,
  27                                       u32 __user *src)
  28{
  29        snd_pcm_uframes_t frames;
  30        int err;
  31
  32        if (get_user(frames, src))
  33                return -EFAULT;
  34        err = snd_pcm_rewind(substream, frames);
  35        if (put_user(err, src))
  36                return -EFAULT;
  37        return err < 0 ? err : 0;
  38}
  39
  40static int snd_pcm_ioctl_forward_compat(struct snd_pcm_substream *substream,
  41                                       u32 __user *src)
  42{
  43        snd_pcm_uframes_t frames;
  44        int err;
  45
  46        if (get_user(frames, src))
  47                return -EFAULT;
  48        err = snd_pcm_forward(substream, frames);
  49        if (put_user(err, src))
  50                return -EFAULT;
  51        return err < 0 ? err : 0;
  52}
  53
  54struct snd_pcm_hw_params32 {
  55        u32 flags;
  56        struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; /* this must be identical */
  57        struct snd_mask mres[5];        /* reserved masks */
  58        struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
  59        struct snd_interval ires[9];    /* reserved intervals */
  60        u32 rmask;
  61        u32 cmask;
  62        u32 info;
  63        u32 msbits;
  64        u32 rate_num;
  65        u32 rate_den;
  66        u32 fifo_size;
  67        unsigned char reserved[64];
  68};
  69
  70struct snd_pcm_sw_params32 {
  71        s32 tstamp_mode;
  72        u32 period_step;
  73        u32 sleep_min;
  74        u32 avail_min;
  75        u32 xfer_align;
  76        u32 start_threshold;
  77        u32 stop_threshold;
  78        u32 silence_threshold;
  79        u32 silence_size;
  80        u32 boundary;
  81        u32 proto;
  82        u32 tstamp_type;
  83        unsigned char reserved[56];
  84};
  85
  86/* recalcuate the boundary within 32bit */
  87static snd_pcm_uframes_t recalculate_boundary(struct snd_pcm_runtime *runtime)
  88{
  89        snd_pcm_uframes_t boundary;
  90
  91        if (! runtime->buffer_size)
  92                return 0;
  93        boundary = runtime->buffer_size;
  94        while (boundary * 2 <= 0x7fffffffUL - runtime->buffer_size)
  95                boundary *= 2;
  96        return boundary;
  97}
  98
  99static int snd_pcm_ioctl_sw_params_compat(struct snd_pcm_substream *substream,
 100                                          struct snd_pcm_sw_params32 __user *src)
 101{
 102        struct snd_pcm_sw_params params;
 103        snd_pcm_uframes_t boundary;
 104        int err;
 105
 106        memset(&params, 0, sizeof(params));
 107        if (get_user(params.tstamp_mode, &src->tstamp_mode) ||
 108            get_user(params.period_step, &src->period_step) ||
 109            get_user(params.sleep_min, &src->sleep_min) ||
 110            get_user(params.avail_min, &src->avail_min) ||
 111            get_user(params.xfer_align, &src->xfer_align) ||
 112            get_user(params.start_threshold, &src->start_threshold) ||
 113            get_user(params.stop_threshold, &src->stop_threshold) ||
 114            get_user(params.silence_threshold, &src->silence_threshold) ||
 115            get_user(params.silence_size, &src->silence_size) ||
 116            get_user(params.tstamp_type, &src->tstamp_type) ||
 117            get_user(params.proto, &src->proto))
 118                return -EFAULT;
 119        /*
 120         * Check silent_size parameter.  Since we have 64bit boundary,
 121         * silence_size must be compared with the 32bit boundary.
 122         */
 123        boundary = recalculate_boundary(substream->runtime);
 124        if (boundary && params.silence_size >= boundary)
 125                params.silence_size = substream->runtime->boundary;
 126        err = snd_pcm_sw_params(substream, &params);
 127        if (err < 0)
 128                return err;
 129        if (boundary && put_user(boundary, &src->boundary))
 130                return -EFAULT;
 131        return err;
 132}
 133
 134struct snd_pcm_channel_info32 {
 135        u32 channel;
 136        u32 offset;
 137        u32 first;
 138        u32 step;
 139};
 140
 141static int snd_pcm_ioctl_channel_info_compat(struct snd_pcm_substream *substream,
 142                                             struct snd_pcm_channel_info32 __user *src)
 143{
 144        struct snd_pcm_channel_info info;
 145        int err;
 146
 147        if (get_user(info.channel, &src->channel) ||
 148            get_user(info.offset, &src->offset) ||
 149            get_user(info.first, &src->first) ||
 150            get_user(info.step, &src->step))
 151                return -EFAULT;
 152        err = snd_pcm_channel_info(substream, &info);
 153        if (err < 0)
 154                return err;
 155        if (put_user(info.channel, &src->channel) ||
 156            put_user(info.offset, &src->offset) ||
 157            put_user(info.first, &src->first) ||
 158            put_user(info.step, &src->step))
 159                return -EFAULT;
 160        return err;
 161}
 162
 163#ifdef CONFIG_X86_X32
 164/* X32 ABI has the same struct as x86-64 for snd_pcm_channel_info */
 165static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
 166                                     struct snd_pcm_channel_info __user *src);
 167#define snd_pcm_ioctl_channel_info_x32(s, p)    \
 168        snd_pcm_channel_info_user(s, p)
 169#endif /* CONFIG_X86_X32 */
 170
 171struct snd_pcm_status32 {
 172        s32 state;
 173        struct compat_timespec trigger_tstamp;
 174        struct compat_timespec tstamp;
 175        u32 appl_ptr;
 176        u32 hw_ptr;
 177        s32 delay;
 178        u32 avail;
 179        u32 avail_max;
 180        u32 overrange;
 181        s32 suspended_state;
 182        u32 audio_tstamp_data;
 183        struct compat_timespec audio_tstamp;
 184        struct compat_timespec driver_tstamp;
 185        u32 audio_tstamp_accuracy;
 186        unsigned char reserved[52-2*sizeof(struct compat_timespec)];
 187} __attribute__((packed));
 188
 189
 190static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
 191                                      struct snd_pcm_status32 __user *src,
 192                                      bool ext)
 193{
 194        struct snd_pcm_status status;
 195        int err;
 196
 197        memset(&status, 0, sizeof(status));
 198        /*
 199         * with extension, parameters are read/write,
 200         * get audio_tstamp_data from user,
 201         * ignore rest of status structure
 202         */
 203        if (ext && get_user(status.audio_tstamp_data,
 204                                (u32 __user *)(&src->audio_tstamp_data)))
 205                return -EFAULT;
 206        err = snd_pcm_status(substream, &status);
 207        if (err < 0)
 208                return err;
 209
 210        if (clear_user(src, sizeof(*src)))
 211                return -EFAULT;
 212        if (put_user(status.state, &src->state) ||
 213            compat_put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
 214            compat_put_timespec(&status.tstamp, &src->tstamp) ||
 215            put_user(status.appl_ptr, &src->appl_ptr) ||
 216            put_user(status.hw_ptr, &src->hw_ptr) ||
 217            put_user(status.delay, &src->delay) ||
 218            put_user(status.avail, &src->avail) ||
 219            put_user(status.avail_max, &src->avail_max) ||
 220            put_user(status.overrange, &src->overrange) ||
 221            put_user(status.suspended_state, &src->suspended_state) ||
 222            put_user(status.audio_tstamp_data, &src->audio_tstamp_data) ||
 223            compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp) ||
 224            compat_put_timespec(&status.driver_tstamp, &src->driver_tstamp) ||
 225            put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy))
 226                return -EFAULT;
 227
 228        return err;
 229}
 230
 231#ifdef CONFIG_X86_X32
 232/* X32 ABI has 64bit timespec and 64bit alignment */
 233struct snd_pcm_status_x32 {
 234        s32 state;
 235        u32 rsvd; /* alignment */
 236        struct timespec trigger_tstamp;
 237        struct timespec tstamp;
 238        u32 appl_ptr;
 239        u32 hw_ptr;
 240        s32 delay;
 241        u32 avail;
 242        u32 avail_max;
 243        u32 overrange;
 244        s32 suspended_state;
 245        u32 audio_tstamp_data;
 246        struct timespec audio_tstamp;
 247        struct timespec driver_tstamp;
 248        u32 audio_tstamp_accuracy;
 249        unsigned char reserved[52-2*sizeof(struct timespec)];
 250} __packed;
 251
 252#define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst))
 253
 254static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream,
 255                                   struct snd_pcm_status_x32 __user *src,
 256                                   bool ext)
 257{
 258        struct snd_pcm_status status;
 259        int err;
 260
 261        memset(&status, 0, sizeof(status));
 262        /*
 263         * with extension, parameters are read/write,
 264         * get audio_tstamp_data from user,
 265         * ignore rest of status structure
 266         */
 267        if (ext && get_user(status.audio_tstamp_data,
 268                                (u32 __user *)(&src->audio_tstamp_data)))
 269                return -EFAULT;
 270        err = snd_pcm_status(substream, &status);
 271        if (err < 0)
 272                return err;
 273
 274        if (clear_user(src, sizeof(*src)))
 275                return -EFAULT;
 276        if (put_user(status.state, &src->state) ||
 277            put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
 278            put_timespec(&status.tstamp, &src->tstamp) ||
 279            put_user(status.appl_ptr, &src->appl_ptr) ||
 280            put_user(status.hw_ptr, &src->hw_ptr) ||
 281            put_user(status.delay, &src->delay) ||
 282            put_user(status.avail, &src->avail) ||
 283            put_user(status.avail_max, &src->avail_max) ||
 284            put_user(status.overrange, &src->overrange) ||
 285            put_user(status.suspended_state, &src->suspended_state) ||
 286            put_user(status.audio_tstamp_data, &src->audio_tstamp_data) ||
 287            put_timespec(&status.audio_tstamp, &src->audio_tstamp) ||
 288            put_timespec(&status.driver_tstamp, &src->driver_tstamp) ||
 289            put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy))
 290                return -EFAULT;
 291
 292        return err;
 293}
 294#endif /* CONFIG_X86_X32 */
 295
 296/* both for HW_PARAMS and HW_REFINE */
 297static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
 298                                          int refine, 
 299                                          struct snd_pcm_hw_params32 __user *data32)
 300{
 301        struct snd_pcm_hw_params *data;
 302        struct snd_pcm_runtime *runtime;
 303        int err;
 304
 305        if (! (runtime = substream->runtime))
 306                return -ENOTTY;
 307
 308        data = kmalloc(sizeof(*data), GFP_KERNEL);
 309        if (!data)
 310                return -ENOMEM;
 311
 312        /* only fifo_size (RO from userspace) is different, so just copy all */
 313        if (copy_from_user(data, data32, sizeof(*data32))) {
 314                err = -EFAULT;
 315                goto error;
 316        }
 317
 318        if (refine)
 319                err = snd_pcm_hw_refine(substream, data);
 320        else
 321                err = snd_pcm_hw_params(substream, data);
 322        if (err < 0)
 323                goto error;
 324        if (copy_to_user(data32, data, sizeof(*data32)) ||
 325            put_user(data->fifo_size, &data32->fifo_size)) {
 326                err = -EFAULT;
 327                goto error;
 328        }
 329
 330        if (! refine) {
 331                unsigned int new_boundary = recalculate_boundary(runtime);
 332                if (new_boundary)
 333                        runtime->boundary = new_boundary;
 334        }
 335 error:
 336        kfree(data);
 337        return err;
 338}
 339
 340
 341/*
 342 */
 343struct snd_xferi32 {
 344        s32 result;
 345        u32 buf;
 346        u32 frames;
 347};
 348
 349static int snd_pcm_ioctl_xferi_compat(struct snd_pcm_substream *substream,
 350                                      int dir, struct snd_xferi32 __user *data32)
 351{
 352        compat_caddr_t buf;
 353        u32 frames;
 354        int err;
 355
 356        if (! substream->runtime)
 357                return -ENOTTY;
 358        if (substream->stream != dir)
 359                return -EINVAL;
 360        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
 361                return -EBADFD;
 362
 363        if (get_user(buf, &data32->buf) ||
 364            get_user(frames, &data32->frames))
 365                return -EFAULT;
 366
 367        if (dir == SNDRV_PCM_STREAM_PLAYBACK)
 368                err = snd_pcm_lib_write(substream, compat_ptr(buf), frames);
 369        else
 370                err = snd_pcm_lib_read(substream, compat_ptr(buf), frames);
 371        if (err < 0)
 372                return err;
 373        /* copy the result */
 374        if (put_user(err, &data32->result))
 375                return -EFAULT;
 376        return 0;
 377}
 378
 379
 380/* snd_xfern needs remapping of bufs */
 381struct snd_xfern32 {
 382        s32 result;
 383        u32 bufs;  /* this is void **; */
 384        u32 frames;
 385};
 386
 387/*
 388 * xfern ioctl nees to copy (up to) 128 pointers on stack.
 389 * although we may pass the copied pointers through f_op->ioctl, but the ioctl
 390 * handler there expands again the same 128 pointers on stack, so it is better
 391 * to handle the function (calling pcm_readv/writev) directly in this handler.
 392 */
 393static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
 394                                      int dir, struct snd_xfern32 __user *data32)
 395{
 396        compat_caddr_t buf;
 397        compat_caddr_t __user *bufptr;
 398        u32 frames;
 399        void __user **bufs;
 400        int err, ch, i;
 401
 402        if (! substream->runtime)
 403                return -ENOTTY;
 404        if (substream->stream != dir)
 405                return -EINVAL;
 406        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
 407                return -EBADFD;
 408
 409        if ((ch = substream->runtime->channels) > 128)
 410                return -EINVAL;
 411        if (get_user(buf, &data32->bufs) ||
 412            get_user(frames, &data32->frames))
 413                return -EFAULT;
 414        bufptr = compat_ptr(buf);
 415        bufs = kmalloc_array(ch, sizeof(void __user *), GFP_KERNEL);
 416        if (bufs == NULL)
 417                return -ENOMEM;
 418        for (i = 0; i < ch; i++) {
 419                u32 ptr;
 420                if (get_user(ptr, bufptr)) {
 421                        kfree(bufs);
 422                        return -EFAULT;
 423                }
 424                bufs[i] = compat_ptr(ptr);
 425                bufptr++;
 426        }
 427        if (dir == SNDRV_PCM_STREAM_PLAYBACK)
 428                err = snd_pcm_lib_writev(substream, bufs, frames);
 429        else
 430                err = snd_pcm_lib_readv(substream, bufs, frames);
 431        if (err >= 0) {
 432                if (put_user(err, &data32->result))
 433                        err = -EFAULT;
 434        }
 435        kfree(bufs);
 436        return err;
 437}
 438
 439
 440struct snd_pcm_mmap_status32 {
 441        s32 state;
 442        s32 pad1;
 443        u32 hw_ptr;
 444        struct compat_timespec tstamp;
 445        s32 suspended_state;
 446        struct compat_timespec audio_tstamp;
 447} __attribute__((packed));
 448
 449struct snd_pcm_mmap_control32 {
 450        u32 appl_ptr;
 451        u32 avail_min;
 452};
 453
 454struct snd_pcm_sync_ptr32 {
 455        u32 flags;
 456        union {
 457                struct snd_pcm_mmap_status32 status;
 458                unsigned char reserved[64];
 459        } s;
 460        union {
 461                struct snd_pcm_mmap_control32 control;
 462                unsigned char reserved[64];
 463        } c;
 464} __attribute__((packed));
 465
 466static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
 467                                         struct snd_pcm_sync_ptr32 __user *src)
 468{
 469        struct snd_pcm_runtime *runtime = substream->runtime;
 470        volatile struct snd_pcm_mmap_status *status;
 471        volatile struct snd_pcm_mmap_control *control;
 472        u32 sflags;
 473        struct snd_pcm_mmap_control scontrol;
 474        struct snd_pcm_mmap_status sstatus;
 475        snd_pcm_uframes_t boundary;
 476        int err;
 477
 478        if (snd_BUG_ON(!runtime))
 479                return -EINVAL;
 480
 481        if (get_user(sflags, &src->flags) ||
 482            get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
 483            get_user(scontrol.avail_min, &src->c.control.avail_min))
 484                return -EFAULT;
 485        if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
 486                err = snd_pcm_hwsync(substream);
 487                if (err < 0)
 488                        return err;
 489        }
 490        status = runtime->status;
 491        control = runtime->control;
 492        boundary = recalculate_boundary(runtime);
 493        if (! boundary)
 494                boundary = 0x7fffffff;
 495        snd_pcm_stream_lock_irq(substream);
 496        /* FIXME: we should consider the boundary for the sync from app */
 497        if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
 498                control->appl_ptr = scontrol.appl_ptr;
 499        else
 500                scontrol.appl_ptr = control->appl_ptr % boundary;
 501        if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
 502                control->avail_min = scontrol.avail_min;
 503        else
 504                scontrol.avail_min = control->avail_min;
 505        sstatus.state = status->state;
 506        sstatus.hw_ptr = status->hw_ptr % boundary;
 507        sstatus.tstamp = status->tstamp;
 508        sstatus.suspended_state = status->suspended_state;
 509        sstatus.audio_tstamp = status->audio_tstamp;
 510        snd_pcm_stream_unlock_irq(substream);
 511        if (put_user(sstatus.state, &src->s.status.state) ||
 512            put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
 513            compat_put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
 514            put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
 515            compat_put_timespec(&sstatus.audio_tstamp,
 516                    &src->s.status.audio_tstamp) ||
 517            put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
 518            put_user(scontrol.avail_min, &src->c.control.avail_min))
 519                return -EFAULT;
 520
 521        return 0;
 522}
 523
 524#ifdef CONFIG_X86_X32
 525/* X32 ABI has 64bit timespec and 64bit alignment */
 526struct snd_pcm_mmap_status_x32 {
 527        s32 state;
 528        s32 pad1;
 529        u32 hw_ptr;
 530        u32 pad2; /* alignment */
 531        struct timespec tstamp;
 532        s32 suspended_state;
 533        s32 pad3;
 534        struct timespec audio_tstamp;
 535} __packed;
 536
 537struct snd_pcm_mmap_control_x32 {
 538        u32 appl_ptr;
 539        u32 avail_min;
 540};
 541
 542struct snd_pcm_sync_ptr_x32 {
 543        u32 flags;
 544        u32 rsvd; /* alignment */
 545        union {
 546                struct snd_pcm_mmap_status_x32 status;
 547                unsigned char reserved[64];
 548        } s;
 549        union {
 550                struct snd_pcm_mmap_control_x32 control;
 551                unsigned char reserved[64];
 552        } c;
 553} __packed;
 554
 555static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
 556                                      struct snd_pcm_sync_ptr_x32 __user *src)
 557{
 558        struct snd_pcm_runtime *runtime = substream->runtime;
 559        volatile struct snd_pcm_mmap_status *status;
 560        volatile struct snd_pcm_mmap_control *control;
 561        u32 sflags;
 562        struct snd_pcm_mmap_control scontrol;
 563        struct snd_pcm_mmap_status sstatus;
 564        snd_pcm_uframes_t boundary;
 565        int err;
 566
 567        if (snd_BUG_ON(!runtime))
 568                return -EINVAL;
 569
 570        if (get_user(sflags, &src->flags) ||
 571            get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
 572            get_user(scontrol.avail_min, &src->c.control.avail_min))
 573                return -EFAULT;
 574        if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
 575                err = snd_pcm_hwsync(substream);
 576                if (err < 0)
 577                        return err;
 578        }
 579        status = runtime->status;
 580        control = runtime->control;
 581        boundary = recalculate_boundary(runtime);
 582        if (!boundary)
 583                boundary = 0x7fffffff;
 584        snd_pcm_stream_lock_irq(substream);
 585        /* FIXME: we should consider the boundary for the sync from app */
 586        if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
 587                control->appl_ptr = scontrol.appl_ptr;
 588        else
 589                scontrol.appl_ptr = control->appl_ptr % boundary;
 590        if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
 591                control->avail_min = scontrol.avail_min;
 592        else
 593                scontrol.avail_min = control->avail_min;
 594        sstatus.state = status->state;
 595        sstatus.hw_ptr = status->hw_ptr % boundary;
 596        sstatus.tstamp = status->tstamp;
 597        sstatus.suspended_state = status->suspended_state;
 598        sstatus.audio_tstamp = status->audio_tstamp;
 599        snd_pcm_stream_unlock_irq(substream);
 600        if (put_user(sstatus.state, &src->s.status.state) ||
 601            put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
 602            put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
 603            put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
 604            put_timespec(&sstatus.audio_tstamp, &src->s.status.audio_tstamp) ||
 605            put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
 606            put_user(scontrol.avail_min, &src->c.control.avail_min))
 607                return -EFAULT;
 608
 609        return 0;
 610}
 611#endif /* CONFIG_X86_X32 */
 612
 613/*
 614 */
 615enum {
 616        SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct snd_pcm_hw_params32),
 617        SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32),
 618        SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32),
 619        SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct snd_pcm_status32),
 620        SNDRV_PCM_IOCTL_STATUS_EXT32 = _IOWR('A', 0x24, struct snd_pcm_status32),
 621        SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32),
 622        SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32),
 623        SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32),
 624        SNDRV_PCM_IOCTL_FORWARD32 = _IOW('A', 0x49, u32),
 625        SNDRV_PCM_IOCTL_WRITEI_FRAMES32 = _IOW('A', 0x50, struct snd_xferi32),
 626        SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct snd_xferi32),
 627        SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32),
 628        SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32),
 629        SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr32),
 630#ifdef CONFIG_X86_X32
 631        SNDRV_PCM_IOCTL_CHANNEL_INFO_X32 = _IOR('A', 0x32, struct snd_pcm_channel_info),
 632        SNDRV_PCM_IOCTL_STATUS_X32 = _IOR('A', 0x20, struct snd_pcm_status_x32),
 633        SNDRV_PCM_IOCTL_STATUS_EXT_X32 = _IOWR('A', 0x24, struct snd_pcm_status_x32),
 634        SNDRV_PCM_IOCTL_SYNC_PTR_X32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr_x32),
 635#endif /* CONFIG_X86_X32 */
 636};
 637
 638static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
 639{
 640        struct snd_pcm_file *pcm_file;
 641        struct snd_pcm_substream *substream;
 642        void __user *argp = compat_ptr(arg);
 643
 644        pcm_file = file->private_data;
 645        if (! pcm_file)
 646                return -ENOTTY;
 647        substream = pcm_file->substream;
 648        if (! substream)
 649                return -ENOTTY;
 650
 651        /*
 652         * When PCM is used on 32bit mode, we need to disable
 653         * mmap of PCM status/control records because of the size
 654         * incompatibility.
 655         */
 656        pcm_file->no_compat_mmap = 1;
 657
 658        switch (cmd) {
 659        case SNDRV_PCM_IOCTL_PVERSION:
 660        case SNDRV_PCM_IOCTL_INFO:
 661        case SNDRV_PCM_IOCTL_TSTAMP:
 662        case SNDRV_PCM_IOCTL_TTSTAMP:
 663        case SNDRV_PCM_IOCTL_USER_PVERSION:
 664        case SNDRV_PCM_IOCTL_HWSYNC:
 665        case SNDRV_PCM_IOCTL_PREPARE:
 666        case SNDRV_PCM_IOCTL_RESET:
 667        case SNDRV_PCM_IOCTL_START:
 668        case SNDRV_PCM_IOCTL_DROP:
 669        case SNDRV_PCM_IOCTL_DRAIN:
 670        case SNDRV_PCM_IOCTL_PAUSE:
 671        case SNDRV_PCM_IOCTL_HW_FREE:
 672        case SNDRV_PCM_IOCTL_RESUME:
 673        case SNDRV_PCM_IOCTL_XRUN:
 674        case SNDRV_PCM_IOCTL_LINK:
 675        case SNDRV_PCM_IOCTL_UNLINK:
 676                return snd_pcm_common_ioctl(file, substream, cmd, argp);
 677        case SNDRV_PCM_IOCTL_HW_REFINE32:
 678                return snd_pcm_ioctl_hw_params_compat(substream, 1, argp);
 679        case SNDRV_PCM_IOCTL_HW_PARAMS32:
 680                return snd_pcm_ioctl_hw_params_compat(substream, 0, argp);
 681        case SNDRV_PCM_IOCTL_SW_PARAMS32:
 682                return snd_pcm_ioctl_sw_params_compat(substream, argp);
 683        case SNDRV_PCM_IOCTL_STATUS32:
 684                return snd_pcm_status_user_compat(substream, argp, false);
 685        case SNDRV_PCM_IOCTL_STATUS_EXT32:
 686                return snd_pcm_status_user_compat(substream, argp, true);
 687        case SNDRV_PCM_IOCTL_SYNC_PTR32:
 688                return snd_pcm_ioctl_sync_ptr_compat(substream, argp);
 689        case SNDRV_PCM_IOCTL_CHANNEL_INFO32:
 690                return snd_pcm_ioctl_channel_info_compat(substream, argp);
 691        case SNDRV_PCM_IOCTL_WRITEI_FRAMES32:
 692                return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
 693        case SNDRV_PCM_IOCTL_READI_FRAMES32:
 694                return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
 695        case SNDRV_PCM_IOCTL_WRITEN_FRAMES32:
 696                return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
 697        case SNDRV_PCM_IOCTL_READN_FRAMES32:
 698                return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
 699        case SNDRV_PCM_IOCTL_DELAY32:
 700                return snd_pcm_ioctl_delay_compat(substream, argp);
 701        case SNDRV_PCM_IOCTL_REWIND32:
 702                return snd_pcm_ioctl_rewind_compat(substream, argp);
 703        case SNDRV_PCM_IOCTL_FORWARD32:
 704                return snd_pcm_ioctl_forward_compat(substream, argp);
 705#ifdef CONFIG_X86_X32
 706        case SNDRV_PCM_IOCTL_STATUS_X32:
 707                return snd_pcm_status_user_x32(substream, argp, false);
 708        case SNDRV_PCM_IOCTL_STATUS_EXT_X32:
 709                return snd_pcm_status_user_x32(substream, argp, true);
 710        case SNDRV_PCM_IOCTL_SYNC_PTR_X32:
 711                return snd_pcm_ioctl_sync_ptr_x32(substream, argp);
 712        case SNDRV_PCM_IOCTL_CHANNEL_INFO_X32:
 713                return snd_pcm_ioctl_channel_info_x32(substream, argp);
 714#endif /* CONFIG_X86_X32 */
 715        }
 716
 717        return -ENOIOCTLCMD;
 718}
 719