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