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
  86static int snd_pcm_ioctl_sw_params_compat(struct snd_pcm_substream *substream,
  87                                          struct snd_pcm_sw_params32 __user *src)
  88{
  89        struct snd_pcm_sw_params params;
  90        snd_pcm_uframes_t boundary;
  91        int err;
  92
  93        memset(&params, 0, sizeof(params));
  94        if (get_user(params.tstamp_mode, &src->tstamp_mode) ||
  95            get_user(params.period_step, &src->period_step) ||
  96            get_user(params.sleep_min, &src->sleep_min) ||
  97            get_user(params.avail_min, &src->avail_min) ||
  98            get_user(params.xfer_align, &src->xfer_align) ||
  99            get_user(params.start_threshold, &src->start_threshold) ||
 100            get_user(params.stop_threshold, &src->stop_threshold) ||
 101            get_user(params.silence_threshold, &src->silence_threshold) ||
 102            get_user(params.silence_size, &src->silence_size) ||
 103            get_user(params.tstamp_type, &src->tstamp_type) ||
 104            get_user(params.proto, &src->proto))
 105                return -EFAULT;
 106        /*
 107         * Check silent_size parameter.  Since we have 64bit boundary,
 108         * silence_size must be compared with the 32bit boundary.
 109         */
 110        boundary = recalculate_boundary(substream->runtime);
 111        if (boundary && params.silence_size >= boundary)
 112                params.silence_size = substream->runtime->boundary;
 113        err = snd_pcm_sw_params(substream, &params);
 114        if (err < 0)
 115                return err;
 116        if (boundary && put_user(boundary, &src->boundary))
 117                return -EFAULT;
 118        return err;
 119}
 120
 121struct snd_pcm_channel_info32 {
 122        u32 channel;
 123        u32 offset;
 124        u32 first;
 125        u32 step;
 126};
 127
 128static int snd_pcm_ioctl_channel_info_compat(struct snd_pcm_substream *substream,
 129                                             struct snd_pcm_channel_info32 __user *src)
 130{
 131        struct snd_pcm_channel_info info;
 132        int err;
 133
 134        if (get_user(info.channel, &src->channel) ||
 135            get_user(info.offset, &src->offset) ||
 136            get_user(info.first, &src->first) ||
 137            get_user(info.step, &src->step))
 138                return -EFAULT;
 139        err = snd_pcm_channel_info(substream, &info);
 140        if (err < 0)
 141                return err;
 142        if (put_user(info.channel, &src->channel) ||
 143            put_user(info.offset, &src->offset) ||
 144            put_user(info.first, &src->first) ||
 145            put_user(info.step, &src->step))
 146                return -EFAULT;
 147        return err;
 148}
 149
 150#ifdef CONFIG_X86_X32
 151/* X32 ABI has the same struct as x86-64 for snd_pcm_channel_info */
 152static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
 153                                     struct snd_pcm_channel_info __user *src);
 154#define snd_pcm_ioctl_channel_info_x32(s, p)    \
 155        snd_pcm_channel_info_user(s, p)
 156#endif /* CONFIG_X86_X32 */
 157
 158struct compat_snd_pcm_status64 {
 159        snd_pcm_state_t state;
 160        u8 rsvd[4]; /* alignment */
 161        s64 trigger_tstamp_sec;
 162        s64 trigger_tstamp_nsec;
 163        s64 tstamp_sec;
 164        s64 tstamp_nsec;
 165        u32 appl_ptr;
 166        u32 hw_ptr;
 167        s32 delay;
 168        u32 avail;
 169        u32 avail_max;
 170        u32 overrange;
 171        snd_pcm_state_t suspended_state;
 172        u32 audio_tstamp_data;
 173        s64 audio_tstamp_sec;
 174        s64 audio_tstamp_nsec;
 175        s64 driver_tstamp_sec;
 176        s64 driver_tstamp_nsec;
 177        u32 audio_tstamp_accuracy;
 178        unsigned char reserved[52-4*sizeof(s64)];
 179} __packed;
 180
 181static int snd_pcm_status_user_compat64(struct snd_pcm_substream *substream,
 182                                        struct compat_snd_pcm_status64 __user *src,
 183                                        bool ext)
 184{
 185        struct snd_pcm_status64 status;
 186        struct compat_snd_pcm_status64 compat_status64;
 187        int err;
 188
 189        memset(&status, 0, sizeof(status));
 190        memset(&compat_status64, 0, sizeof(compat_status64));
 191        /*
 192         * with extension, parameters are read/write,
 193         * get audio_tstamp_data from user,
 194         * ignore rest of status structure
 195         */
 196        if (ext && get_user(status.audio_tstamp_data,
 197                                (u32 __user *)(&src->audio_tstamp_data)))
 198                return -EFAULT;
 199        err = snd_pcm_status64(substream, &status);
 200        if (err < 0)
 201                return err;
 202
 203        if (clear_user(src, sizeof(*src)))
 204                return -EFAULT;
 205
 206        compat_status64 = (struct compat_snd_pcm_status64) {
 207                .state = status.state,
 208                .trigger_tstamp_sec = status.trigger_tstamp_sec,
 209                .trigger_tstamp_nsec = status.trigger_tstamp_nsec,
 210                .tstamp_sec = status.tstamp_sec,
 211                .tstamp_nsec = status.tstamp_nsec,
 212                .appl_ptr = status.appl_ptr,
 213                .hw_ptr = status.hw_ptr,
 214                .delay = status.delay,
 215                .avail = status.avail,
 216                .avail_max = status.avail_max,
 217                .overrange = status.overrange,
 218                .suspended_state = status.suspended_state,
 219                .audio_tstamp_data = status.audio_tstamp_data,
 220                .audio_tstamp_sec = status.audio_tstamp_sec,
 221                .audio_tstamp_nsec = status.audio_tstamp_nsec,
 222                .driver_tstamp_sec = status.audio_tstamp_sec,
 223                .driver_tstamp_nsec = status.audio_tstamp_nsec,
 224                .audio_tstamp_accuracy = status.audio_tstamp_accuracy,
 225        };
 226
 227        if (copy_to_user(src, &compat_status64, sizeof(compat_status64)))
 228                return -EFAULT;
 229
 230        return err;
 231}
 232
 233/* both for HW_PARAMS and HW_REFINE */
 234static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
 235                                          int refine, 
 236                                          struct snd_pcm_hw_params32 __user *data32)
 237{
 238        struct snd_pcm_hw_params *data;
 239        struct snd_pcm_runtime *runtime;
 240        int err;
 241
 242        runtime = substream->runtime;
 243        if (!runtime)
 244                return -ENOTTY;
 245
 246        data = kmalloc(sizeof(*data), GFP_KERNEL);
 247        if (!data)
 248                return -ENOMEM;
 249
 250        /* only fifo_size (RO from userspace) is different, so just copy all */
 251        if (copy_from_user(data, data32, sizeof(*data32))) {
 252                err = -EFAULT;
 253                goto error;
 254        }
 255
 256        if (refine)
 257                err = snd_pcm_hw_refine(substream, data);
 258        else
 259                err = snd_pcm_hw_params(substream, data);
 260        if (err < 0)
 261                goto error;
 262        if (copy_to_user(data32, data, sizeof(*data32)) ||
 263            put_user(data->fifo_size, &data32->fifo_size)) {
 264                err = -EFAULT;
 265                goto error;
 266        }
 267
 268        if (! refine) {
 269                unsigned int new_boundary = recalculate_boundary(runtime);
 270                if (new_boundary)
 271                        runtime->boundary = new_boundary;
 272        }
 273 error:
 274        kfree(data);
 275        return err;
 276}
 277
 278
 279/*
 280 */
 281struct snd_xferi32 {
 282        s32 result;
 283        u32 buf;
 284        u32 frames;
 285};
 286
 287static int snd_pcm_ioctl_xferi_compat(struct snd_pcm_substream *substream,
 288                                      int dir, struct snd_xferi32 __user *data32)
 289{
 290        compat_caddr_t buf;
 291        u32 frames;
 292        int err;
 293
 294        if (! substream->runtime)
 295                return -ENOTTY;
 296        if (substream->stream != dir)
 297                return -EINVAL;
 298        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
 299                return -EBADFD;
 300
 301        if (get_user(buf, &data32->buf) ||
 302            get_user(frames, &data32->frames))
 303                return -EFAULT;
 304
 305        if (dir == SNDRV_PCM_STREAM_PLAYBACK)
 306                err = snd_pcm_lib_write(substream, compat_ptr(buf), frames);
 307        else
 308                err = snd_pcm_lib_read(substream, compat_ptr(buf), frames);
 309        if (err < 0)
 310                return err;
 311        /* copy the result */
 312        if (put_user(err, &data32->result))
 313                return -EFAULT;
 314        return 0;
 315}
 316
 317
 318/* snd_xfern needs remapping of bufs */
 319struct snd_xfern32 {
 320        s32 result;
 321        u32 bufs;  /* this is void **; */
 322        u32 frames;
 323};
 324
 325/*
 326 * xfern ioctl nees to copy (up to) 128 pointers on stack.
 327 * although we may pass the copied pointers through f_op->ioctl, but the ioctl
 328 * handler there expands again the same 128 pointers on stack, so it is better
 329 * to handle the function (calling pcm_readv/writev) directly in this handler.
 330 */
 331static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
 332                                      int dir, struct snd_xfern32 __user *data32)
 333{
 334        compat_caddr_t buf;
 335        compat_caddr_t __user *bufptr;
 336        u32 frames;
 337        void __user **bufs;
 338        int err, ch, i;
 339
 340        if (! substream->runtime)
 341                return -ENOTTY;
 342        if (substream->stream != dir)
 343                return -EINVAL;
 344        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
 345                return -EBADFD;
 346
 347        ch = substream->runtime->channels;
 348        if (ch > 128)
 349                return -EINVAL;
 350        if (get_user(buf, &data32->bufs) ||
 351            get_user(frames, &data32->frames))
 352                return -EFAULT;
 353        bufptr = compat_ptr(buf);
 354        bufs = kmalloc_array(ch, sizeof(void __user *), GFP_KERNEL);
 355        if (bufs == NULL)
 356                return -ENOMEM;
 357        for (i = 0; i < ch; i++) {
 358                u32 ptr;
 359                if (get_user(ptr, bufptr)) {
 360                        kfree(bufs);
 361                        return -EFAULT;
 362                }
 363                bufs[i] = compat_ptr(ptr);
 364                bufptr++;
 365        }
 366        if (dir == SNDRV_PCM_STREAM_PLAYBACK)
 367                err = snd_pcm_lib_writev(substream, bufs, frames);
 368        else
 369                err = snd_pcm_lib_readv(substream, bufs, frames);
 370        if (err >= 0) {
 371                if (put_user(err, &data32->result))
 372                        err = -EFAULT;
 373        }
 374        kfree(bufs);
 375        return err;
 376}
 377
 378#ifdef CONFIG_X86_X32
 379/* X32 ABI has 64bit timespec and 64bit alignment */
 380struct snd_pcm_mmap_status_x32 {
 381        snd_pcm_state_t state;
 382        s32 pad1;
 383        u32 hw_ptr;
 384        u32 pad2; /* alignment */
 385        s64 tstamp_sec;
 386        s64 tstamp_nsec;
 387        snd_pcm_state_t suspended_state;
 388        s32 pad3;
 389        s64 audio_tstamp_sec;
 390        s64 audio_tstamp_nsec;
 391} __packed;
 392
 393struct snd_pcm_mmap_control_x32 {
 394        u32 appl_ptr;
 395        u32 avail_min;
 396};
 397
 398struct snd_pcm_sync_ptr_x32 {
 399        u32 flags;
 400        u32 rsvd; /* alignment */
 401        union {
 402                struct snd_pcm_mmap_status_x32 status;
 403                unsigned char reserved[64];
 404        } s;
 405        union {
 406                struct snd_pcm_mmap_control_x32 control;
 407                unsigned char reserved[64];
 408        } c;
 409} __packed;
 410
 411static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
 412                                      struct snd_pcm_sync_ptr_x32 __user *src)
 413{
 414        struct snd_pcm_runtime *runtime = substream->runtime;
 415        volatile struct snd_pcm_mmap_status *status;
 416        volatile struct snd_pcm_mmap_control *control;
 417        u32 sflags;
 418        struct snd_pcm_mmap_control scontrol;
 419        struct snd_pcm_mmap_status sstatus;
 420        snd_pcm_uframes_t boundary;
 421        int err;
 422
 423        if (snd_BUG_ON(!runtime))
 424                return -EINVAL;
 425
 426        if (get_user(sflags, &src->flags) ||
 427            get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
 428            get_user(scontrol.avail_min, &src->c.control.avail_min))
 429                return -EFAULT;
 430        if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
 431                err = snd_pcm_hwsync(substream);
 432                if (err < 0)
 433                        return err;
 434        }
 435        status = runtime->status;
 436        control = runtime->control;
 437        boundary = recalculate_boundary(runtime);
 438        if (!boundary)
 439                boundary = 0x7fffffff;
 440        snd_pcm_stream_lock_irq(substream);
 441        /* FIXME: we should consider the boundary for the sync from app */
 442        if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
 443                control->appl_ptr = scontrol.appl_ptr;
 444        else
 445                scontrol.appl_ptr = control->appl_ptr % boundary;
 446        if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
 447                control->avail_min = scontrol.avail_min;
 448        else
 449                scontrol.avail_min = control->avail_min;
 450        sstatus.state = status->state;
 451        sstatus.hw_ptr = status->hw_ptr % boundary;
 452        sstatus.tstamp = status->tstamp;
 453        sstatus.suspended_state = status->suspended_state;
 454        sstatus.audio_tstamp = status->audio_tstamp;
 455        snd_pcm_stream_unlock_irq(substream);
 456        if (put_user(sstatus.state, &src->s.status.state) ||
 457            put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
 458            put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) ||
 459            put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp_nsec) ||
 460            put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
 461            put_user(sstatus.audio_tstamp.tv_sec, &src->s.status.audio_tstamp_sec) ||
 462            put_user(sstatus.audio_tstamp.tv_nsec, &src->s.status.audio_tstamp_nsec) ||
 463            put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
 464            put_user(scontrol.avail_min, &src->c.control.avail_min))
 465                return -EFAULT;
 466
 467        return 0;
 468}
 469#endif /* CONFIG_X86_X32 */
 470
 471/*
 472 */
 473enum {
 474        SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct snd_pcm_hw_params32),
 475        SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32),
 476        SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32),
 477        SNDRV_PCM_IOCTL_STATUS_COMPAT32 = _IOR('A', 0x20, struct snd_pcm_status32),
 478        SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT32 = _IOWR('A', 0x24, struct snd_pcm_status32),
 479        SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32),
 480        SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32),
 481        SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32),
 482        SNDRV_PCM_IOCTL_FORWARD32 = _IOW('A', 0x49, u32),
 483        SNDRV_PCM_IOCTL_WRITEI_FRAMES32 = _IOW('A', 0x50, struct snd_xferi32),
 484        SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct snd_xferi32),
 485        SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32),
 486        SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32),
 487        SNDRV_PCM_IOCTL_STATUS_COMPAT64 = _IOR('A', 0x20, struct compat_snd_pcm_status64),
 488        SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64 = _IOWR('A', 0x24, struct compat_snd_pcm_status64),
 489#ifdef CONFIG_X86_X32
 490        SNDRV_PCM_IOCTL_CHANNEL_INFO_X32 = _IOR('A', 0x32, struct snd_pcm_channel_info),
 491        SNDRV_PCM_IOCTL_SYNC_PTR_X32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr_x32),
 492#endif /* CONFIG_X86_X32 */
 493};
 494
 495static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
 496{
 497        struct snd_pcm_file *pcm_file;
 498        struct snd_pcm_substream *substream;
 499        void __user *argp = compat_ptr(arg);
 500
 501        pcm_file = file->private_data;
 502        if (! pcm_file)
 503                return -ENOTTY;
 504        substream = pcm_file->substream;
 505        if (! substream)
 506                return -ENOTTY;
 507
 508        /*
 509         * When PCM is used on 32bit mode, we need to disable
 510         * mmap of the old PCM status/control records because
 511         * of the size incompatibility.
 512         */
 513        pcm_file->no_compat_mmap = 1;
 514
 515        switch (cmd) {
 516        case SNDRV_PCM_IOCTL_PVERSION:
 517        case SNDRV_PCM_IOCTL_INFO:
 518        case SNDRV_PCM_IOCTL_TSTAMP:
 519        case SNDRV_PCM_IOCTL_TTSTAMP:
 520        case SNDRV_PCM_IOCTL_USER_PVERSION:
 521        case SNDRV_PCM_IOCTL_HWSYNC:
 522        case SNDRV_PCM_IOCTL_PREPARE:
 523        case SNDRV_PCM_IOCTL_RESET:
 524        case SNDRV_PCM_IOCTL_START:
 525        case SNDRV_PCM_IOCTL_DROP:
 526        case SNDRV_PCM_IOCTL_DRAIN:
 527        case SNDRV_PCM_IOCTL_PAUSE:
 528        case SNDRV_PCM_IOCTL_HW_FREE:
 529        case SNDRV_PCM_IOCTL_RESUME:
 530        case SNDRV_PCM_IOCTL_XRUN:
 531        case SNDRV_PCM_IOCTL_LINK:
 532        case SNDRV_PCM_IOCTL_UNLINK:
 533        case __SNDRV_PCM_IOCTL_SYNC_PTR32:
 534                return snd_pcm_common_ioctl(file, substream, cmd, argp);
 535        case __SNDRV_PCM_IOCTL_SYNC_PTR64:
 536#ifdef CONFIG_X86_X32
 537                if (in_x32_syscall())
 538                        return snd_pcm_ioctl_sync_ptr_x32(substream, argp);
 539#endif /* CONFIG_X86_X32 */
 540                return snd_pcm_common_ioctl(file, substream, cmd, argp);
 541        case SNDRV_PCM_IOCTL_HW_REFINE32:
 542                return snd_pcm_ioctl_hw_params_compat(substream, 1, argp);
 543        case SNDRV_PCM_IOCTL_HW_PARAMS32:
 544                return snd_pcm_ioctl_hw_params_compat(substream, 0, argp);
 545        case SNDRV_PCM_IOCTL_SW_PARAMS32:
 546                return snd_pcm_ioctl_sw_params_compat(substream, argp);
 547        case SNDRV_PCM_IOCTL_STATUS_COMPAT32:
 548                return snd_pcm_status_user32(substream, argp, false);
 549        case SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT32:
 550                return snd_pcm_status_user32(substream, argp, true);
 551        case SNDRV_PCM_IOCTL_CHANNEL_INFO32:
 552                return snd_pcm_ioctl_channel_info_compat(substream, argp);
 553        case SNDRV_PCM_IOCTL_WRITEI_FRAMES32:
 554                return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
 555        case SNDRV_PCM_IOCTL_READI_FRAMES32:
 556                return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
 557        case SNDRV_PCM_IOCTL_WRITEN_FRAMES32:
 558                return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
 559        case SNDRV_PCM_IOCTL_READN_FRAMES32:
 560                return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
 561        case SNDRV_PCM_IOCTL_DELAY32:
 562                return snd_pcm_ioctl_delay_compat(substream, argp);
 563        case SNDRV_PCM_IOCTL_REWIND32:
 564                return snd_pcm_ioctl_rewind_compat(substream, argp);
 565        case SNDRV_PCM_IOCTL_FORWARD32:
 566                return snd_pcm_ioctl_forward_compat(substream, argp);
 567        case SNDRV_PCM_IOCTL_STATUS_COMPAT64:
 568                return snd_pcm_status_user_compat64(substream, argp, false);
 569        case SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64:
 570                return snd_pcm_status_user_compat64(substream, argp, true);
 571#ifdef CONFIG_X86_X32
 572        case SNDRV_PCM_IOCTL_CHANNEL_INFO_X32:
 573                return snd_pcm_ioctl_channel_info_x32(substream, argp);
 574#endif /* CONFIG_X86_X32 */
 575        }
 576
 577        return -ENOIOCTLCMD;
 578}
 579