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
 186struct snd_pcm_status32 {
 187        s32 state;
 188        struct compat_timespec trigger_tstamp;
 189        struct compat_timespec tstamp;
 190        u32 appl_ptr;
 191        u32 hw_ptr;
 192        s32 delay;
 193        u32 avail;
 194        u32 avail_max;
 195        u32 overrange;
 196        s32 suspended_state;
 197        u32 reserved_alignment;
 198        struct compat_timespec audio_tstamp;
 199        unsigned char reserved[56-sizeof(struct compat_timespec)];
 200} __attribute__((packed));
 201
 202
 203static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
 204                                      struct snd_pcm_status32 __user *src)
 205{
 206        struct snd_pcm_status status;
 207        int err;
 208
 209        err = snd_pcm_status(substream, &status);
 210        if (err < 0)
 211                return err;
 212
 213        if (clear_user(src, sizeof(*src)))
 214                return -EFAULT;
 215        if (put_user(status.state, &src->state) ||
 216            compat_put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
 217            compat_put_timespec(&status.tstamp, &src->tstamp) ||
 218            put_user(status.appl_ptr, &src->appl_ptr) ||
 219            put_user(status.hw_ptr, &src->hw_ptr) ||
 220            put_user(status.delay, &src->delay) ||
 221            put_user(status.avail, &src->avail) ||
 222            put_user(status.avail_max, &src->avail_max) ||
 223            put_user(status.overrange, &src->overrange) ||
 224            put_user(status.suspended_state, &src->suspended_state) ||
 225            compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp))
 226                return -EFAULT;
 227
 228        return err;
 229}
 230
 231/* both for HW_PARAMS and HW_REFINE */
 232static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
 233                                          int refine, 
 234                                          struct snd_pcm_hw_params32 __user *data32)
 235{
 236        struct snd_pcm_hw_params *data;
 237        struct snd_pcm_runtime *runtime;
 238        int err;
 239
 240        if (! (runtime = substream->runtime))
 241                return -ENOTTY;
 242
 243        /* only fifo_size is different, so just copy all */
 244        data = memdup_user(data32, sizeof(*data32));
 245        if (IS_ERR(data))
 246                return PTR_ERR(data);
 247
 248        if (refine)
 249                err = snd_pcm_hw_refine(substream, data);
 250        else
 251                err = snd_pcm_hw_params(substream, data);
 252        if (err < 0)
 253                goto error;
 254        if (copy_to_user(data32, data, sizeof(*data32)) ||
 255            put_user(data->fifo_size, &data32->fifo_size)) {
 256                err = -EFAULT;
 257                goto error;
 258        }
 259
 260        if (! refine) {
 261                unsigned int new_boundary = recalculate_boundary(runtime);
 262                if (new_boundary)
 263                        runtime->boundary = new_boundary;
 264        }
 265 error:
 266        kfree(data);
 267        return err;
 268}
 269
 270
 271/*
 272 */
 273struct snd_xferi32 {
 274        s32 result;
 275        u32 buf;
 276        u32 frames;
 277};
 278
 279static int snd_pcm_ioctl_xferi_compat(struct snd_pcm_substream *substream,
 280                                      int dir, struct snd_xferi32 __user *data32)
 281{
 282        compat_caddr_t buf;
 283        u32 frames;
 284        int err;
 285
 286        if (! substream->runtime)
 287                return -ENOTTY;
 288        if (substream->stream != dir)
 289                return -EINVAL;
 290        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
 291                return -EBADFD;
 292
 293        if (get_user(buf, &data32->buf) ||
 294            get_user(frames, &data32->frames))
 295                return -EFAULT;
 296
 297        if (dir == SNDRV_PCM_STREAM_PLAYBACK)
 298                err = snd_pcm_lib_write(substream, compat_ptr(buf), frames);
 299        else
 300                err = snd_pcm_lib_read(substream, compat_ptr(buf), frames);
 301        if (err < 0)
 302                return err;
 303        /* copy the result */
 304        if (put_user(err, &data32->result))
 305                return -EFAULT;
 306        return 0;
 307}
 308
 309
 310/* snd_xfern needs remapping of bufs */
 311struct snd_xfern32 {
 312        s32 result;
 313        u32 bufs;  /* this is void **; */
 314        u32 frames;
 315};
 316
 317/*
 318 * xfern ioctl nees to copy (up to) 128 pointers on stack.
 319 * although we may pass the copied pointers through f_op->ioctl, but the ioctl
 320 * handler there expands again the same 128 pointers on stack, so it is better
 321 * to handle the function (calling pcm_readv/writev) directly in this handler.
 322 */
 323static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
 324                                      int dir, struct snd_xfern32 __user *data32)
 325{
 326        compat_caddr_t buf;
 327        compat_caddr_t __user *bufptr;
 328        u32 frames;
 329        void __user **bufs;
 330        int err, ch, i;
 331
 332        if (! substream->runtime)
 333                return -ENOTTY;
 334        if (substream->stream != dir)
 335                return -EINVAL;
 336
 337        if ((ch = substream->runtime->channels) > 128)
 338                return -EINVAL;
 339        if (get_user(buf, &data32->bufs) ||
 340            get_user(frames, &data32->frames))
 341                return -EFAULT;
 342        bufptr = compat_ptr(buf);
 343        bufs = kmalloc(sizeof(void __user *) * ch, GFP_KERNEL);
 344        if (bufs == NULL)
 345                return -ENOMEM;
 346        for (i = 0; i < ch; i++) {
 347                u32 ptr;
 348                if (get_user(ptr, bufptr)) {
 349                        kfree(bufs);
 350                        return -EFAULT;
 351                }
 352                bufs[i] = compat_ptr(ptr);
 353                bufptr++;
 354        }
 355        if (dir == SNDRV_PCM_STREAM_PLAYBACK)
 356                err = snd_pcm_lib_writev(substream, bufs, frames);
 357        else
 358                err = snd_pcm_lib_readv(substream, bufs, frames);
 359        if (err >= 0) {
 360                if (put_user(err, &data32->result))
 361                        err = -EFAULT;
 362        }
 363        kfree(bufs);
 364        return err;
 365}
 366
 367
 368struct snd_pcm_mmap_status32 {
 369        s32 state;
 370        s32 pad1;
 371        u32 hw_ptr;
 372        struct compat_timespec tstamp;
 373        s32 suspended_state;
 374        struct compat_timespec audio_tstamp;
 375} __attribute__((packed));
 376
 377struct snd_pcm_mmap_control32 {
 378        u32 appl_ptr;
 379        u32 avail_min;
 380};
 381
 382struct snd_pcm_sync_ptr32 {
 383        u32 flags;
 384        union {
 385                struct snd_pcm_mmap_status32 status;
 386                unsigned char reserved[64];
 387        } s;
 388        union {
 389                struct snd_pcm_mmap_control32 control;
 390                unsigned char reserved[64];
 391        } c;
 392} __attribute__((packed));
 393
 394static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
 395                                         struct snd_pcm_sync_ptr32 __user *src)
 396{
 397        struct snd_pcm_runtime *runtime = substream->runtime;
 398        volatile struct snd_pcm_mmap_status *status;
 399        volatile struct snd_pcm_mmap_control *control;
 400        u32 sflags;
 401        struct snd_pcm_mmap_control scontrol;
 402        struct snd_pcm_mmap_status sstatus;
 403        snd_pcm_uframes_t boundary;
 404        int err;
 405
 406        if (snd_BUG_ON(!runtime))
 407                return -EINVAL;
 408
 409        if (get_user(sflags, &src->flags) ||
 410            get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
 411            get_user(scontrol.avail_min, &src->c.control.avail_min))
 412                return -EFAULT;
 413        if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
 414                err = snd_pcm_hwsync(substream);
 415                if (err < 0)
 416                        return err;
 417        }
 418        status = runtime->status;
 419        control = runtime->control;
 420        boundary = recalculate_boundary(runtime);
 421        if (! boundary)
 422                boundary = 0x7fffffff;
 423        snd_pcm_stream_lock_irq(substream);
 424        /* FIXME: we should consider the boundary for the sync from app */
 425        if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
 426                control->appl_ptr = scontrol.appl_ptr;
 427        else
 428                scontrol.appl_ptr = control->appl_ptr % boundary;
 429        if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
 430                control->avail_min = scontrol.avail_min;
 431        else
 432                scontrol.avail_min = control->avail_min;
 433        sstatus.state = status->state;
 434        sstatus.hw_ptr = status->hw_ptr % boundary;
 435        sstatus.tstamp = status->tstamp;
 436        sstatus.suspended_state = status->suspended_state;
 437        sstatus.audio_tstamp = status->audio_tstamp;
 438        snd_pcm_stream_unlock_irq(substream);
 439        if (put_user(sstatus.state, &src->s.status.state) ||
 440            put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
 441            compat_put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
 442            put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
 443            compat_put_timespec(&sstatus.audio_tstamp,
 444                    &src->s.status.audio_tstamp) ||
 445            put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
 446            put_user(scontrol.avail_min, &src->c.control.avail_min))
 447                return -EFAULT;
 448
 449        return 0;
 450}
 451
 452
 453/*
 454 */
 455enum {
 456        SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct snd_pcm_hw_params32),
 457        SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32),
 458        SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32),
 459        SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct snd_pcm_status32),
 460        SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32),
 461        SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32),
 462        SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32),
 463        SNDRV_PCM_IOCTL_FORWARD32 = _IOW('A', 0x49, u32),
 464        SNDRV_PCM_IOCTL_WRITEI_FRAMES32 = _IOW('A', 0x50, struct snd_xferi32),
 465        SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct snd_xferi32),
 466        SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32),
 467        SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32),
 468        SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr32),
 469
 470};
 471
 472static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
 473{
 474        struct snd_pcm_file *pcm_file;
 475        struct snd_pcm_substream *substream;
 476        void __user *argp = compat_ptr(arg);
 477
 478        pcm_file = file->private_data;
 479        if (! pcm_file)
 480                return -ENOTTY;
 481        substream = pcm_file->substream;
 482        if (! substream)
 483                return -ENOTTY;
 484
 485        /*
 486         * When PCM is used on 32bit mode, we need to disable
 487         * mmap of PCM status/control records because of the size
 488         * incompatibility.
 489         */
 490        pcm_file->no_compat_mmap = 1;
 491
 492        switch (cmd) {
 493        case SNDRV_PCM_IOCTL_PVERSION:
 494        case SNDRV_PCM_IOCTL_INFO:
 495        case SNDRV_PCM_IOCTL_TSTAMP:
 496        case SNDRV_PCM_IOCTL_TTSTAMP:
 497        case SNDRV_PCM_IOCTL_HWSYNC:
 498        case SNDRV_PCM_IOCTL_PREPARE:
 499        case SNDRV_PCM_IOCTL_RESET:
 500        case SNDRV_PCM_IOCTL_START:
 501        case SNDRV_PCM_IOCTL_DROP:
 502        case SNDRV_PCM_IOCTL_DRAIN:
 503        case SNDRV_PCM_IOCTL_PAUSE:
 504        case SNDRV_PCM_IOCTL_HW_FREE:
 505        case SNDRV_PCM_IOCTL_RESUME:
 506        case SNDRV_PCM_IOCTL_XRUN:
 507        case SNDRV_PCM_IOCTL_LINK:
 508        case SNDRV_PCM_IOCTL_UNLINK:
 509                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 510                        return snd_pcm_playback_ioctl1(file, substream, cmd, argp);
 511                else
 512                        return snd_pcm_capture_ioctl1(file, substream, cmd, argp);
 513        case SNDRV_PCM_IOCTL_HW_REFINE32:
 514                return snd_pcm_ioctl_hw_params_compat(substream, 1, argp);
 515        case SNDRV_PCM_IOCTL_HW_PARAMS32:
 516                return snd_pcm_ioctl_hw_params_compat(substream, 0, argp);
 517        case SNDRV_PCM_IOCTL_SW_PARAMS32:
 518                return snd_pcm_ioctl_sw_params_compat(substream, argp);
 519        case SNDRV_PCM_IOCTL_STATUS32:
 520                return snd_pcm_status_user_compat(substream, argp);
 521        case SNDRV_PCM_IOCTL_SYNC_PTR32:
 522                return snd_pcm_ioctl_sync_ptr_compat(substream, argp);
 523        case SNDRV_PCM_IOCTL_CHANNEL_INFO32:
 524                return snd_pcm_ioctl_channel_info_compat(substream, argp);
 525        case SNDRV_PCM_IOCTL_WRITEI_FRAMES32:
 526                return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
 527        case SNDRV_PCM_IOCTL_READI_FRAMES32:
 528                return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
 529        case SNDRV_PCM_IOCTL_WRITEN_FRAMES32:
 530                return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
 531        case SNDRV_PCM_IOCTL_READN_FRAMES32:
 532                return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
 533        case SNDRV_PCM_IOCTL_DELAY32:
 534                return snd_pcm_ioctl_delay_compat(substream, argp);
 535        case SNDRV_PCM_IOCTL_REWIND32:
 536                return snd_pcm_ioctl_rewind_compat(substream, argp);
 537        case SNDRV_PCM_IOCTL_FORWARD32:
 538                return snd_pcm_ioctl_forward_compat(substream, argp);
 539        }
 540
 541        return -ENOIOCTLCMD;
 542}
 543