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                        u64 names_ptr;
  87                        u32 names_length;
  88                } enumerated;
  89                unsigned char reserved[128];
  90        } value;
  91        unsigned char reserved[64];
  92} __attribute__((packed));
  93
  94static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl,
  95                                    struct snd_ctl_elem_info32 __user *data32)
  96{
  97        struct snd_ctl_elem_info *data;
  98        int err;
  99
 100        data = kzalloc(sizeof(*data), GFP_KERNEL);
 101        if (! data)
 102                return -ENOMEM;
 103
 104        err = -EFAULT;
 105        /* copy id */
 106        if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
 107                goto error;
 108        /* we need to copy the item index.
 109         * hope this doesn't break anything..
 110         */
 111        if (get_user(data->value.enumerated.item, &data32->value.enumerated.item))
 112                goto error;
 113
 114        snd_power_lock(ctl->card);
 115        err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
 116        if (err >= 0)
 117                err = snd_ctl_elem_info(ctl, data);
 118        snd_power_unlock(ctl->card);
 119
 120        if (err < 0)
 121                goto error;
 122        /* restore info to 32bit */
 123        err = -EFAULT;
 124        /* id, type, access, count */
 125        if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) ||
 126            copy_to_user(&data32->type, &data->type, 3 * sizeof(u32)))
 127                goto error;
 128        if (put_user(data->owner, &data32->owner))
 129                goto error;
 130        switch (data->type) {
 131        case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
 132        case SNDRV_CTL_ELEM_TYPE_INTEGER:
 133                if (put_user(data->value.integer.min, &data32->value.integer.min) ||
 134                    put_user(data->value.integer.max, &data32->value.integer.max) ||
 135                    put_user(data->value.integer.step, &data32->value.integer.step))
 136                        goto error;
 137                break;
 138        case SNDRV_CTL_ELEM_TYPE_INTEGER64:
 139                if (copy_to_user(&data32->value.integer64,
 140                                 &data->value.integer64,
 141                                 sizeof(data->value.integer64)))
 142                        goto error;
 143                break;
 144        case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
 145                if (copy_to_user(&data32->value.enumerated,
 146                                 &data->value.enumerated,
 147                                 sizeof(data->value.enumerated)))
 148                        goto error;
 149                break;
 150        default:
 151                break;
 152        }
 153        err = 0;
 154 error:
 155        kfree(data);
 156        return err;
 157}
 158
 159/* read / write */
 160struct snd_ctl_elem_value32 {
 161        struct snd_ctl_elem_id id;
 162        unsigned int indirect;  /* bit-field causes misalignment */
 163        union {
 164                s32 integer[128];
 165                unsigned char data[512];
 166#ifndef CONFIG_X86_64
 167                s64 integer64[64];
 168#endif
 169        } value;
 170        unsigned char reserved[128];
 171};
 172
 173
 174/* get the value type and count of the control */
 175static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
 176                        int *countp)
 177{
 178        struct snd_kcontrol *kctl;
 179        struct snd_ctl_elem_info *info;
 180        int err;
 181
 182        down_read(&card->controls_rwsem);
 183        kctl = snd_ctl_find_id(card, id);
 184        if (! kctl) {
 185                up_read(&card->controls_rwsem);
 186                return -ENXIO;
 187        }
 188        info = kzalloc(sizeof(*info), GFP_KERNEL);
 189        if (info == NULL) {
 190                up_read(&card->controls_rwsem);
 191                return -ENOMEM;
 192        }
 193        info->id = *id;
 194        err = kctl->info(kctl, info);
 195        up_read(&card->controls_rwsem);
 196        if (err >= 0) {
 197                err = info->type;
 198                *countp = info->count;
 199        }
 200        kfree(info);
 201        return err;
 202}
 203
 204static int get_elem_size(int type, int count)
 205{
 206        switch (type) {
 207        case SNDRV_CTL_ELEM_TYPE_INTEGER64:
 208                return sizeof(s64) * count;
 209        case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
 210                return sizeof(int) * count;
 211        case SNDRV_CTL_ELEM_TYPE_BYTES:
 212                return 512;
 213        case SNDRV_CTL_ELEM_TYPE_IEC958:
 214                return sizeof(struct snd_aes_iec958);
 215        default:
 216                return -1;
 217        }
 218}
 219
 220static int copy_ctl_value_from_user(struct snd_card *card,
 221                                    struct snd_ctl_elem_value *data,
 222                                    struct snd_ctl_elem_value32 __user *data32,
 223                                    int *typep, int *countp)
 224{
 225        int i, type, size;
 226        int uninitialized_var(count);
 227        unsigned int indirect;
 228
 229        if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
 230                return -EFAULT;
 231        if (get_user(indirect, &data32->indirect))
 232                return -EFAULT;
 233        if (indirect)
 234                return -EINVAL;
 235        type = get_ctl_type(card, &data->id, &count);
 236        if (type < 0)
 237                return type;
 238
 239        if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
 240            type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
 241                for (i = 0; i < count; i++) {
 242                        int val;
 243                        if (get_user(val, &data32->value.integer[i]))
 244                                return -EFAULT;
 245                        data->value.integer.value[i] = val;
 246                }
 247        } else {
 248                size = get_elem_size(type, count);
 249                if (size < 0) {
 250                        printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
 251                        return -EINVAL;
 252                }
 253                if (copy_from_user(data->value.bytes.data,
 254                                   data32->value.data, size))
 255                        return -EFAULT;
 256        }
 257
 258        *typep = type;
 259        *countp = count;
 260        return 0;
 261}
 262
 263/* restore the value to 32bit */
 264static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32,
 265                                  struct snd_ctl_elem_value *data,
 266                                  int type, int count)
 267{
 268        int i, size;
 269
 270        if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
 271            type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
 272                for (i = 0; i < count; i++) {
 273                        int val;
 274                        val = data->value.integer.value[i];
 275                        if (put_user(val, &data32->value.integer[i]))
 276                                return -EFAULT;
 277                }
 278        } else {
 279                size = get_elem_size(type, count);
 280                if (copy_to_user(data32->value.data,
 281                                 data->value.bytes.data, size))
 282                        return -EFAULT;
 283        }
 284        return 0;
 285}
 286
 287static int snd_ctl_elem_read_user_compat(struct snd_card *card, 
 288                                         struct snd_ctl_elem_value32 __user *data32)
 289{
 290        struct snd_ctl_elem_value *data;
 291        int err, type, count;
 292
 293        data = kzalloc(sizeof(*data), GFP_KERNEL);
 294        if (data == NULL)
 295                return -ENOMEM;
 296
 297        if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
 298                goto error;
 299
 300        snd_power_lock(card);
 301        err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
 302        if (err >= 0)
 303                err = snd_ctl_elem_read(card, data);
 304        snd_power_unlock(card);
 305        if (err >= 0)
 306                err = copy_ctl_value_to_user(data32, data, type, count);
 307 error:
 308        kfree(data);
 309        return err;
 310}
 311
 312static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
 313                                          struct snd_ctl_elem_value32 __user *data32)
 314{
 315        struct snd_ctl_elem_value *data;
 316        struct snd_card *card = file->card;
 317        int err, type, count;
 318
 319        data = kzalloc(sizeof(*data), GFP_KERNEL);
 320        if (data == NULL)
 321                return -ENOMEM;
 322
 323        if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
 324                goto error;
 325
 326        snd_power_lock(card);
 327        err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
 328        if (err >= 0)
 329                err = snd_ctl_elem_write(card, file, data);
 330        snd_power_unlock(card);
 331        if (err >= 0)
 332                err = copy_ctl_value_to_user(data32, data, type, count);
 333 error:
 334        kfree(data);
 335        return err;
 336}
 337
 338/* add or replace a user control */
 339static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
 340                                   struct snd_ctl_elem_info32 __user *data32,
 341                                   int replace)
 342{
 343        struct snd_ctl_elem_info *data;
 344        int err;
 345
 346        data = kzalloc(sizeof(*data), GFP_KERNEL);
 347        if (! data)
 348                return -ENOMEM;
 349
 350        err = -EFAULT;
 351        /* id, type, access, count */ \
 352        if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
 353            copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
 354                goto error;
 355        if (get_user(data->owner, &data32->owner) ||
 356            get_user(data->type, &data32->type))
 357                goto error;
 358        switch (data->type) {
 359        case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
 360        case SNDRV_CTL_ELEM_TYPE_INTEGER:
 361                if (get_user(data->value.integer.min, &data32->value.integer.min) ||
 362                    get_user(data->value.integer.max, &data32->value.integer.max) ||
 363                    get_user(data->value.integer.step, &data32->value.integer.step))
 364                        goto error;
 365                break;
 366        case SNDRV_CTL_ELEM_TYPE_INTEGER64:
 367                if (copy_from_user(&data->value.integer64,
 368                                   &data32->value.integer64,
 369                                   sizeof(data->value.integer64)))
 370                        goto error;
 371                break;
 372        case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
 373                if (copy_from_user(&data->value.enumerated,
 374                                   &data32->value.enumerated,
 375                                   sizeof(data->value.enumerated)))
 376                        goto error;
 377                data->value.enumerated.names_ptr =
 378                        (uintptr_t)compat_ptr(data->value.enumerated.names_ptr);
 379                break;
 380        default:
 381                break;
 382        }
 383        err = snd_ctl_elem_add(file, data, replace);
 384 error:
 385        kfree(data);
 386        return err;
 387}  
 388
 389enum {
 390        SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct snd_ctl_elem_list32),
 391        SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct snd_ctl_elem_info32),
 392        SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct snd_ctl_elem_value32),
 393        SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32),
 394        SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct snd_ctl_elem_info32),
 395        SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct snd_ctl_elem_info32),
 396};
 397
 398static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
 399{
 400        struct snd_ctl_file *ctl;
 401        struct snd_kctl_ioctl *p;
 402        void __user *argp = compat_ptr(arg);
 403        int err;
 404
 405        ctl = file->private_data;
 406        if (snd_BUG_ON(!ctl || !ctl->card))
 407                return -ENXIO;
 408
 409        switch (cmd) {
 410        case SNDRV_CTL_IOCTL_PVERSION:
 411        case SNDRV_CTL_IOCTL_CARD_INFO:
 412        case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
 413        case SNDRV_CTL_IOCTL_POWER:
 414        case SNDRV_CTL_IOCTL_POWER_STATE:
 415        case SNDRV_CTL_IOCTL_ELEM_LOCK:
 416        case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
 417        case SNDRV_CTL_IOCTL_ELEM_REMOVE:
 418        case SNDRV_CTL_IOCTL_TLV_READ:
 419        case SNDRV_CTL_IOCTL_TLV_WRITE:
 420        case SNDRV_CTL_IOCTL_TLV_COMMAND:
 421                return snd_ctl_ioctl(file, cmd, (unsigned long)argp);
 422        case SNDRV_CTL_IOCTL_ELEM_LIST32:
 423                return snd_ctl_elem_list_compat(ctl->card, argp);
 424        case SNDRV_CTL_IOCTL_ELEM_INFO32:
 425                return snd_ctl_elem_info_compat(ctl, argp);
 426        case SNDRV_CTL_IOCTL_ELEM_READ32:
 427                return snd_ctl_elem_read_user_compat(ctl->card, argp);
 428        case SNDRV_CTL_IOCTL_ELEM_WRITE32:
 429                return snd_ctl_elem_write_user_compat(ctl, argp);
 430        case SNDRV_CTL_IOCTL_ELEM_ADD32:
 431                return snd_ctl_elem_add_compat(ctl, argp, 0);
 432        case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
 433                return snd_ctl_elem_add_compat(ctl, argp, 1);
 434        }
 435
 436        down_read(&snd_ioctl_rwsem);
 437        list_for_each_entry(p, &snd_control_compat_ioctls, list) {
 438                if (p->fioctl) {
 439                        err = p->fioctl(ctl->card, ctl, cmd, arg);
 440                        if (err != -ENOIOCTLCMD) {
 441                                up_read(&snd_ioctl_rwsem);
 442                                return err;
 443                        }
 444                }
 445        }
 446        up_read(&snd_ioctl_rwsem);
 447        return -ENOIOCTLCMD;
 448}
 449