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        err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
 115        if (err < 0)
 116                goto error;
 117        err = snd_ctl_elem_info(ctl, data);
 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#ifdef CONFIG_X86_X32
 172/* x32 has a different alignment for 64bit values from ia32 */
 173struct snd_ctl_elem_value_x32 {
 174        struct snd_ctl_elem_id id;
 175        unsigned int indirect;  /* bit-field causes misalignment */
 176        union {
 177                s32 integer[128];
 178                unsigned char data[512];
 179                s64 integer64[64];
 180        } value;
 181        unsigned char reserved[128];
 182};
 183#endif /* CONFIG_X86_X32 */
 184
 185/* get the value type and count of the control */
 186static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
 187                        int *countp)
 188{
 189        struct snd_kcontrol *kctl;
 190        struct snd_ctl_elem_info *info;
 191        int err;
 192
 193        down_read(&card->controls_rwsem);
 194        kctl = snd_ctl_find_id(card, id);
 195        if (! kctl) {
 196                up_read(&card->controls_rwsem);
 197                return -ENOENT;
 198        }
 199        info = kzalloc(sizeof(*info), GFP_KERNEL);
 200        if (info == NULL) {
 201                up_read(&card->controls_rwsem);
 202                return -ENOMEM;
 203        }
 204        info->id = *id;
 205        err = kctl->info(kctl, info);
 206        up_read(&card->controls_rwsem);
 207        if (err >= 0) {
 208                err = info->type;
 209                *countp = info->count;
 210        }
 211        kfree(info);
 212        return err;
 213}
 214
 215static int get_elem_size(int type, int count)
 216{
 217        switch (type) {
 218        case SNDRV_CTL_ELEM_TYPE_INTEGER64:
 219                return sizeof(s64) * count;
 220        case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
 221                return sizeof(int) * count;
 222        case SNDRV_CTL_ELEM_TYPE_BYTES:
 223                return 512;
 224        case SNDRV_CTL_ELEM_TYPE_IEC958:
 225                return sizeof(struct snd_aes_iec958);
 226        default:
 227                return -1;
 228        }
 229}
 230
 231static int copy_ctl_value_from_user(struct snd_card *card,
 232                                    struct snd_ctl_elem_value *data,
 233                                    void __user *userdata,
 234                                    void __user *valuep,
 235                                    int *typep, int *countp)
 236{
 237        struct snd_ctl_elem_value32 __user *data32 = userdata;
 238        int i, type, size;
 239        int uninitialized_var(count);
 240        unsigned int indirect;
 241
 242        if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
 243                return -EFAULT;
 244        if (get_user(indirect, &data32->indirect))
 245                return -EFAULT;
 246        if (indirect)
 247                return -EINVAL;
 248        type = get_ctl_type(card, &data->id, &count);
 249        if (type < 0)
 250                return type;
 251
 252        if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
 253            type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
 254                for (i = 0; i < count; i++) {
 255                        s32 __user *intp = valuep;
 256                        int val;
 257                        if (get_user(val, &intp[i]))
 258                                return -EFAULT;
 259                        data->value.integer.value[i] = val;
 260                }
 261        } else {
 262                size = get_elem_size(type, count);
 263                if (size < 0) {
 264                        dev_err(card->dev, "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
 265                        return -EINVAL;
 266                }
 267                if (copy_from_user(data->value.bytes.data, valuep, size))
 268                        return -EFAULT;
 269        }
 270
 271        *typep = type;
 272        *countp = count;
 273        return 0;
 274}
 275
 276/* restore the value to 32bit */
 277static int copy_ctl_value_to_user(void __user *userdata,
 278                                  void __user *valuep,
 279                                  struct snd_ctl_elem_value *data,
 280                                  int type, int count)
 281{
 282        int i, size;
 283
 284        if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
 285            type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
 286                for (i = 0; i < count; i++) {
 287                        s32 __user *intp = valuep;
 288                        int val;
 289                        val = data->value.integer.value[i];
 290                        if (put_user(val, &intp[i]))
 291                                return -EFAULT;
 292                }
 293        } else {
 294                size = get_elem_size(type, count);
 295                if (copy_to_user(valuep, data->value.bytes.data, size))
 296                        return -EFAULT;
 297        }
 298        return 0;
 299}
 300
 301static int ctl_elem_read_user(struct snd_card *card,
 302                              void __user *userdata, void __user *valuep)
 303{
 304        struct snd_ctl_elem_value *data;
 305        int err, type, count;
 306
 307        data = kzalloc(sizeof(*data), GFP_KERNEL);
 308        if (data == NULL)
 309                return -ENOMEM;
 310
 311        err = copy_ctl_value_from_user(card, data, userdata, valuep,
 312                                       &type, &count);
 313        if (err < 0)
 314                goto error;
 315
 316        err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
 317        if (err < 0)
 318                goto error;
 319        err = snd_ctl_elem_read(card, data);
 320        if (err < 0)
 321                goto error;
 322        err = copy_ctl_value_to_user(userdata, valuep, data, type, count);
 323 error:
 324        kfree(data);
 325        return err;
 326}
 327
 328static int ctl_elem_write_user(struct snd_ctl_file *file,
 329                               void __user *userdata, void __user *valuep)
 330{
 331        struct snd_ctl_elem_value *data;
 332        struct snd_card *card = file->card;
 333        int err, type, count;
 334
 335        data = kzalloc(sizeof(*data), GFP_KERNEL);
 336        if (data == NULL)
 337                return -ENOMEM;
 338
 339        err = copy_ctl_value_from_user(card, data, userdata, valuep,
 340                                       &type, &count);
 341        if (err < 0)
 342                goto error;
 343
 344        err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
 345        if (err < 0)
 346                goto error;
 347        err = snd_ctl_elem_write(card, file, data);
 348        if (err < 0)
 349                goto error;
 350        err = copy_ctl_value_to_user(userdata, valuep, data, type, count);
 351 error:
 352        kfree(data);
 353        return err;
 354}
 355
 356static int snd_ctl_elem_read_user_compat(struct snd_card *card,
 357                                         struct snd_ctl_elem_value32 __user *data32)
 358{
 359        return ctl_elem_read_user(card, data32, &data32->value);
 360}
 361
 362static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
 363                                          struct snd_ctl_elem_value32 __user *data32)
 364{
 365        return ctl_elem_write_user(file, data32, &data32->value);
 366}
 367
 368#ifdef CONFIG_X86_X32
 369static int snd_ctl_elem_read_user_x32(struct snd_card *card,
 370                                      struct snd_ctl_elem_value_x32 __user *data32)
 371{
 372        return ctl_elem_read_user(card, data32, &data32->value);
 373}
 374
 375static int snd_ctl_elem_write_user_x32(struct snd_ctl_file *file,
 376                                       struct snd_ctl_elem_value_x32 __user *data32)
 377{
 378        return ctl_elem_write_user(file, data32, &data32->value);
 379}
 380#endif /* CONFIG_X86_X32 */
 381
 382/* add or replace a user control */
 383static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
 384                                   struct snd_ctl_elem_info32 __user *data32,
 385                                   int replace)
 386{
 387        struct snd_ctl_elem_info *data;
 388        int err;
 389
 390        data = kzalloc(sizeof(*data), GFP_KERNEL);
 391        if (! data)
 392                return -ENOMEM;
 393
 394        err = -EFAULT;
 395        /* id, type, access, count */ \
 396        if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
 397            copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
 398                goto error;
 399        if (get_user(data->owner, &data32->owner) ||
 400            get_user(data->type, &data32->type))
 401                goto error;
 402        switch (data->type) {
 403        case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
 404        case SNDRV_CTL_ELEM_TYPE_INTEGER:
 405                if (get_user(data->value.integer.min, &data32->value.integer.min) ||
 406                    get_user(data->value.integer.max, &data32->value.integer.max) ||
 407                    get_user(data->value.integer.step, &data32->value.integer.step))
 408                        goto error;
 409                break;
 410        case SNDRV_CTL_ELEM_TYPE_INTEGER64:
 411                if (copy_from_user(&data->value.integer64,
 412                                   &data32->value.integer64,
 413                                   sizeof(data->value.integer64)))
 414                        goto error;
 415                break;
 416        case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
 417                if (copy_from_user(&data->value.enumerated,
 418                                   &data32->value.enumerated,
 419                                   sizeof(data->value.enumerated)))
 420                        goto error;
 421                data->value.enumerated.names_ptr =
 422                        (uintptr_t)compat_ptr(data->value.enumerated.names_ptr);
 423                break;
 424        default:
 425                break;
 426        }
 427        err = snd_ctl_elem_add(file, data, replace);
 428 error:
 429        kfree(data);
 430        return err;
 431}  
 432
 433enum {
 434        SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct snd_ctl_elem_list32),
 435        SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct snd_ctl_elem_info32),
 436        SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct snd_ctl_elem_value32),
 437        SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32),
 438        SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct snd_ctl_elem_info32),
 439        SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct snd_ctl_elem_info32),
 440#ifdef CONFIG_X86_X32
 441        SNDRV_CTL_IOCTL_ELEM_READ_X32 = _IOWR('U', 0x12, struct snd_ctl_elem_value_x32),
 442        SNDRV_CTL_IOCTL_ELEM_WRITE_X32 = _IOWR('U', 0x13, struct snd_ctl_elem_value_x32),
 443#endif /* CONFIG_X86_X32 */
 444};
 445
 446static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
 447{
 448        struct snd_ctl_file *ctl;
 449        struct snd_kctl_ioctl *p;
 450        void __user *argp = compat_ptr(arg);
 451        int err;
 452
 453        ctl = file->private_data;
 454        if (snd_BUG_ON(!ctl || !ctl->card))
 455                return -ENXIO;
 456
 457        switch (cmd) {
 458        case SNDRV_CTL_IOCTL_PVERSION:
 459        case SNDRV_CTL_IOCTL_CARD_INFO:
 460        case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
 461        case SNDRV_CTL_IOCTL_POWER:
 462        case SNDRV_CTL_IOCTL_POWER_STATE:
 463        case SNDRV_CTL_IOCTL_ELEM_LOCK:
 464        case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
 465        case SNDRV_CTL_IOCTL_ELEM_REMOVE:
 466        case SNDRV_CTL_IOCTL_TLV_READ:
 467        case SNDRV_CTL_IOCTL_TLV_WRITE:
 468        case SNDRV_CTL_IOCTL_TLV_COMMAND:
 469                return snd_ctl_ioctl(file, cmd, (unsigned long)argp);
 470        case SNDRV_CTL_IOCTL_ELEM_LIST32:
 471                return snd_ctl_elem_list_compat(ctl->card, argp);
 472        case SNDRV_CTL_IOCTL_ELEM_INFO32:
 473                return snd_ctl_elem_info_compat(ctl, argp);
 474        case SNDRV_CTL_IOCTL_ELEM_READ32:
 475                return snd_ctl_elem_read_user_compat(ctl->card, argp);
 476        case SNDRV_CTL_IOCTL_ELEM_WRITE32:
 477                return snd_ctl_elem_write_user_compat(ctl, argp);
 478        case SNDRV_CTL_IOCTL_ELEM_ADD32:
 479                return snd_ctl_elem_add_compat(ctl, argp, 0);
 480        case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
 481                return snd_ctl_elem_add_compat(ctl, argp, 1);
 482#ifdef CONFIG_X86_X32
 483        case SNDRV_CTL_IOCTL_ELEM_READ_X32:
 484                return snd_ctl_elem_read_user_x32(ctl->card, argp);
 485        case SNDRV_CTL_IOCTL_ELEM_WRITE_X32:
 486                return snd_ctl_elem_write_user_x32(ctl, argp);
 487#endif /* CONFIG_X86_X32 */
 488        }
 489
 490        down_read(&snd_ioctl_rwsem);
 491        list_for_each_entry(p, &snd_control_compat_ioctls, list) {
 492                if (p->fioctl) {
 493                        err = p->fioctl(ctl->card, ctl, cmd, arg);
 494                        if (err != -ENOIOCTLCMD) {
 495                                up_read(&snd_ioctl_rwsem);
 496                                return err;
 497                        }
 498                }
 499        }
 500        up_read(&snd_ioctl_rwsem);
 501        return -ENOIOCTLCMD;
 502}
 503