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