linux/sound/core/control_compat.c
<<
>>
Prefs
   1/*
   2 * compat ioctls for control API
   3 *
   4 *   Copyright (c) by Takashi Iwai <tiwai@suse.de>
   5 *
   6 *   This program is free software; you can redistribute it and/or modify
   7 *   it under the terms of the GNU General Public License as published by
   8 *   the Free Software Foundation; either version 2 of the License, or
   9 *   (at your option) any later version.
  10 *
  11 *   This program is distributed in the hope that it will be useful,
  12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *   GNU General Public License for more details.
  15 *
  16 *   You should have received a copy of the GNU General Public License
  17 *   along with this program; if not, write to the Free Software
  18 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19 */
  20
  21/* this file included from control.c */
  22
  23#include <linux/compat.h>
  24#include <linux/slab.h>
  25
  26struct snd_ctl_elem_list32 {
  27        u32 offset;
  28        u32 space;
  29        u32 used;
  30        u32 count;
  31        u32 pids;
  32        unsigned char reserved[50];
  33} /* don't set packed attribute here */;
  34
  35static int snd_ctl_elem_list_compat(struct snd_card *card,
  36                                    struct snd_ctl_elem_list32 __user *data32)
  37{
  38        struct snd_ctl_elem_list __user *data;
  39        compat_caddr_t ptr;
  40        int err;
  41
  42        data = compat_alloc_user_space(sizeof(*data));
  43
  44        /* offset, space, used, count */
  45        if (copy_in_user(data, data32, 4 * sizeof(u32)))
  46                return -EFAULT;
  47        /* pids */
  48        if (get_user(ptr, &data32->pids) ||
  49            put_user(compat_ptr(ptr), &data->pids))
  50                return -EFAULT;
  51        err = snd_ctl_elem_list(card, data);
  52        if (err < 0)
  53                return err;
  54        /* copy the result */
  55        if (copy_in_user(data32, data, 4 * sizeof(u32)))
  56                return -EFAULT;
  57        return 0;
  58}
  59
  60/*
  61 * control element info
  62 * it uses union, so the things are not easy..
  63 */
  64
  65struct snd_ctl_elem_info32 {
  66        struct snd_ctl_elem_id id; // the size of struct is same
  67        s32 type;
  68        u32 access;
  69        u32 count;
  70        s32 owner;
  71        union {
  72                struct {
  73                        s32 min;
  74                        s32 max;
  75                        s32 step;
  76                } integer;
  77                struct {
  78                        u64 min;
  79                        u64 max;
  80                        u64 step;
  81                } integer64;
  82                struct {
  83                        u32 items;
  84                        u32 item;
  85                        char name[64];
  86                } enumerated;
  87                unsigned char reserved[128];
  88        } value;
  89        unsigned char reserved[64];
  90} __attribute__((packed));
  91
  92static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl,
  93                                    struct snd_ctl_elem_info32 __user *data32)
  94{
  95        struct snd_ctl_elem_info *data;
  96        int err;
  97
  98        data = kzalloc(sizeof(*data), GFP_KERNEL);
  99        if (! data)
 100                return -ENOMEM;
 101
 102        err = -EFAULT;
 103        /* copy id */
 104        if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
 105                goto error;
 106        /* we need to copy the item index.
 107         * hope this doesn't break anything..
 108         */
 109        if (get_user(data->value.enumerated.item, &data32->value.enumerated.item))
 110                goto error;
 111
 112        snd_power_lock(ctl->card);
 113        err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
 114        if (err >= 0)
 115                err = snd_ctl_elem_info(ctl, data);
 116        snd_power_unlock(ctl->card);
 117
 118        if (err < 0)
 119                goto error;
 120        /* restore info to 32bit */
 121        err = -EFAULT;
 122        /* id, type, access, count */
 123        if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) ||
 124            copy_to_user(&data32->type, &data->type, 3 * sizeof(u32)))
 125                goto error;
 126        if (put_user(data->owner, &data32->owner))
 127                goto error;
 128        switch (data->type) {
 129        case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
 130        case SNDRV_CTL_ELEM_TYPE_INTEGER:
 131                if (put_user(data->value.integer.min, &data32->value.integer.min) ||
 132                    put_user(data->value.integer.max, &data32->value.integer.max) ||
 133                    put_user(data->value.integer.step, &data32->value.integer.step))
 134                        goto error;
 135                break;
 136        case SNDRV_CTL_ELEM_TYPE_INTEGER64:
 137                if (copy_to_user(&data32->value.integer64,
 138                                 &data->value.integer64,
 139                                 sizeof(data->value.integer64)))
 140                        goto error;
 141                break;
 142        case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
 143                if (copy_to_user(&data32->value.enumerated,
 144                                 &data->value.enumerated,
 145                                 sizeof(data->value.enumerated)))
 146                        goto error;
 147                break;
 148        default:
 149                break;
 150        }
 151        err = 0;
 152 error:
 153        kfree(data);
 154        return err;
 155}
 156
 157/* read / write */
 158struct snd_ctl_elem_value32 {
 159        struct snd_ctl_elem_id id;
 160        unsigned int indirect;  /* bit-field causes misalignment */
 161        union {
 162                s32 integer[128];
 163                unsigned char data[512];
 164#ifndef CONFIG_X86_64
 165                s64 integer64[64];
 166#endif
 167        } value;
 168        unsigned char reserved[128];
 169};
 170
 171
 172/* get the value type and count of the control */
 173static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
 174                        int *countp)
 175{
 176        struct snd_kcontrol *kctl;
 177        struct snd_ctl_elem_info *info;
 178        int err;
 179
 180        down_read(&card->controls_rwsem);
 181        kctl = snd_ctl_find_id(card, id);
 182        if (! kctl) {
 183                up_read(&card->controls_rwsem);
 184                return -ENXIO;
 185        }
 186        info = kzalloc(sizeof(*info), GFP_KERNEL);
 187        if (info == NULL) {
 188                up_read(&card->controls_rwsem);
 189                return -ENOMEM;
 190        }
 191        info->id = *id;
 192        err = kctl->info(kctl, info);
 193        up_read(&card->controls_rwsem);
 194        if (err >= 0) {
 195                err = info->type;
 196                *countp = info->count;
 197        }
 198        kfree(info);
 199        return err;
 200}
 201
 202static int get_elem_size(int type, int count)
 203{
 204        switch (type) {
 205        case SNDRV_CTL_ELEM_TYPE_INTEGER64:
 206                return sizeof(s64) * count;
 207        case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
 208                return sizeof(int) * count;
 209        case SNDRV_CTL_ELEM_TYPE_BYTES:
 210                return 512;
 211        case SNDRV_CTL_ELEM_TYPE_IEC958:
 212                return sizeof(struct snd_aes_iec958);
 213        default:
 214                return -1;
 215        }
 216}
 217
 218static int copy_ctl_value_from_user(struct snd_card *card,
 219                                    struct snd_ctl_elem_value *data,
 220                                    struct snd_ctl_elem_value32 __user *data32,
 221                                    int *typep, int *countp)
 222{
 223        int i, type, size;
 224        int uninitialized_var(count);
 225        unsigned int indirect;
 226
 227        if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
 228                return -EFAULT;
 229        if (get_user(indirect, &data32->indirect))
 230                return -EFAULT;
 231        if (indirect)
 232                return -EINVAL;
 233        type = get_ctl_type(card, &data->id, &count);
 234        if (type < 0)
 235                return type;
 236
 237        if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
 238            type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
 239                for (i = 0; i < count; i++) {
 240                        int val;
 241                        if (get_user(val, &data32->value.integer[i]))
 242                                return -EFAULT;
 243                        data->value.integer.value[i] = val;
 244                }
 245        } else {
 246                size = get_elem_size(type, count);
 247                if (size < 0) {
 248                        printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
 249                        return -EINVAL;
 250                }
 251                if (copy_from_user(data->value.bytes.data,
 252                                   data32->value.data, size))
 253                        return -EFAULT;
 254        }
 255
 256        *typep = type;
 257        *countp = count;
 258        return 0;
 259}
 260
 261/* restore the value to 32bit */
 262static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32,
 263                                  struct snd_ctl_elem_value *data,
 264                                  int type, int count)
 265{
 266        int i, size;
 267
 268        if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
 269            type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
 270                for (i = 0; i < count; i++) {
 271                        int val;
 272                        val = data->value.integer.value[i];
 273                        if (put_user(val, &data32->value.integer[i]))
 274                                return -EFAULT;
 275                }
 276        } else {
 277                size = get_elem_size(type, count);
 278                if (copy_to_user(data32->value.data,
 279                                 data->value.bytes.data, size))
 280                        return -EFAULT;
 281        }
 282        return 0;
 283}
 284
 285static int snd_ctl_elem_read_user_compat(struct snd_card *card, 
 286                                         struct snd_ctl_elem_value32 __user *data32)
 287{
 288        struct snd_ctl_elem_value *data;
 289        int err, type, count;
 290
 291        data = kzalloc(sizeof(*data), GFP_KERNEL);
 292        if (data == NULL)
 293                return -ENOMEM;
 294
 295        if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
 296                goto error;
 297
 298        snd_power_lock(card);
 299        err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
 300        if (err >= 0)
 301                err = snd_ctl_elem_read(card, data);
 302        snd_power_unlock(card);
 303        if (err >= 0)
 304                err = copy_ctl_value_to_user(data32, data, type, count);
 305 error:
 306        kfree(data);
 307        return err;
 308}
 309
 310static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
 311                                          struct snd_ctl_elem_value32 __user *data32)
 312{
 313        struct snd_ctl_elem_value *data;
 314        struct snd_card *card = file->card;
 315        int err, type, count;
 316
 317        data = kzalloc(sizeof(*data), GFP_KERNEL);
 318        if (data == NULL)
 319                return -ENOMEM;
 320
 321        if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
 322                goto error;
 323
 324        snd_power_lock(card);
 325        err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
 326        if (err >= 0)
 327                err = snd_ctl_elem_write(card, file, data);
 328        snd_power_unlock(card);
 329        if (err >= 0)
 330                err = copy_ctl_value_to_user(data32, data, type, count);
 331 error:
 332        kfree(data);
 333        return err;
 334}
 335
 336/* add or replace a user control */
 337static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
 338                                   struct snd_ctl_elem_info32 __user *data32,
 339                                   int replace)
 340{
 341        struct snd_ctl_elem_info *data;
 342        int err;
 343
 344        data = kzalloc(sizeof(*data), GFP_KERNEL);
 345        if (! data)
 346                return -ENOMEM;
 347
 348        err = -EFAULT;
 349        /* id, type, access, count */ \
 350        if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
 351            copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
 352                goto error;
 353        if (get_user(data->owner, &data32->owner) ||
 354            get_user(data->type, &data32->type))
 355                goto error;
 356        switch (data->type) {
 357        case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
 358        case SNDRV_CTL_ELEM_TYPE_INTEGER:
 359                if (get_user(data->value.integer.min, &data32->value.integer.min) ||
 360                    get_user(data->value.integer.max, &data32->value.integer.max) ||
 361                    get_user(data->value.integer.step, &data32->value.integer.step))
 362                        goto error;
 363                break;
 364        case SNDRV_CTL_ELEM_TYPE_INTEGER64:
 365                if (copy_from_user(&data->value.integer64,
 366                                   &data32->value.integer64,
 367                                   sizeof(data->value.integer64)))
 368                        goto error;
 369                break;
 370        case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
 371                if (copy_from_user(&data->value.enumerated,
 372                                   &data32->value.enumerated,
 373                                   sizeof(data->value.enumerated)))
 374                        goto error;
 375                break;
 376        default:
 377                break;
 378        }
 379        err = snd_ctl_elem_add(file, data, replace);
 380 error:
 381        kfree(data);
 382        return err;
 383}  
 384
 385enum {
 386        SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct snd_ctl_elem_list32),
 387        SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct snd_ctl_elem_info32),
 388        SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct snd_ctl_elem_value32),
 389        SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32),
 390        SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct snd_ctl_elem_info32),
 391        SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct snd_ctl_elem_info32),
 392};
 393
 394static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
 395{
 396        struct snd_ctl_file *ctl;
 397        struct snd_kctl_ioctl *p;
 398        void __user *argp = compat_ptr(arg);
 399        int err;
 400
 401        ctl = file->private_data;
 402        if (snd_BUG_ON(!ctl || !ctl->card))
 403                return -ENXIO;
 404
 405        switch (cmd) {
 406        case SNDRV_CTL_IOCTL_PVERSION:
 407        case SNDRV_CTL_IOCTL_CARD_INFO:
 408        case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
 409        case SNDRV_CTL_IOCTL_POWER:
 410        case SNDRV_CTL_IOCTL_POWER_STATE:
 411        case SNDRV_CTL_IOCTL_ELEM_LOCK:
 412        case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
 413        case SNDRV_CTL_IOCTL_ELEM_REMOVE:
 414        case SNDRV_CTL_IOCTL_TLV_READ:
 415        case SNDRV_CTL_IOCTL_TLV_WRITE:
 416        case SNDRV_CTL_IOCTL_TLV_COMMAND:
 417                return snd_ctl_ioctl(file, cmd, (unsigned long)argp);
 418        case SNDRV_CTL_IOCTL_ELEM_LIST32:
 419                return snd_ctl_elem_list_compat(ctl->card, argp);
 420        case SNDRV_CTL_IOCTL_ELEM_INFO32:
 421                return snd_ctl_elem_info_compat(ctl, argp);
 422        case SNDRV_CTL_IOCTL_ELEM_READ32:
 423                return snd_ctl_elem_read_user_compat(ctl->card, argp);
 424        case SNDRV_CTL_IOCTL_ELEM_WRITE32:
 425                return snd_ctl_elem_write_user_compat(ctl, argp);
 426        case SNDRV_CTL_IOCTL_ELEM_ADD32:
 427                return snd_ctl_elem_add_compat(ctl, argp, 0);
 428        case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
 429                return snd_ctl_elem_add_compat(ctl, argp, 1);
 430        }
 431
 432        down_read(&snd_ioctl_rwsem);
 433        list_for_each_entry(p, &snd_control_compat_ioctls, list) {
 434                if (p->fioctl) {
 435                        err = p->fioctl(ctl->card, ctl, cmd, arg);
 436                        if (err != -ENOIOCTLCMD) {
 437                                up_read(&snd_ioctl_rwsem);
 438                                return err;
 439                        }
 440                }
 441        }
 442        up_read(&snd_ioctl_rwsem);
 443        return -ENOIOCTLCMD;
 444}
 445