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