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