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