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, count, size;
 223        unsigned int indirect;
 224
 225        if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
 226                return -EFAULT;
 227        if (get_user(indirect, &data32->indirect))
 228                return -EFAULT;
 229        if (indirect)
 230                return -EINVAL;
 231        type = get_ctl_type(card, &data->id, &count);
 232        if (type < 0)
 233                return type;
 234
 235        if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
 236            type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
 237                for (i = 0; i < count; i++) {
 238                        int val;
 239                        if (get_user(val, &data32->value.integer[i]))
 240                                return -EFAULT;
 241                        data->value.integer.value[i] = val;
 242                }
 243        } else {
 244                size = get_elem_size(type, count);
 245                if (size < 0) {
 246                        printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
 247                        return -EINVAL;
 248                }
 249                if (copy_from_user(data->value.bytes.data,
 250                                   data32->value.data, size))
 251                        return -EFAULT;
 252        }
 253
 254        *typep = type;
 255        *countp = count;
 256        return 0;
 257}
 258
 259/* restore the value to 32bit */
 260static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32,
 261                                  struct snd_ctl_elem_value *data,
 262                                  int type, int count)
 263{
 264        int i, size;
 265
 266        if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
 267            type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
 268                for (i = 0; i < count; i++) {
 269                        int val;
 270                        val = data->value.integer.value[i];
 271                        if (put_user(val, &data32->value.integer[i]))
 272                                return -EFAULT;
 273                }
 274        } else {
 275                size = get_elem_size(type, count);
 276                if (copy_to_user(data32->value.data,
 277                                 data->value.bytes.data, size))
 278                        return -EFAULT;
 279        }
 280        return 0;
 281}
 282
 283static int snd_ctl_elem_read_user_compat(struct snd_card *card, 
 284                                         struct snd_ctl_elem_value32 __user *data32)
 285{
 286        struct snd_ctl_elem_value *data;
 287        int err, type, count;
 288
 289        data = kzalloc(sizeof(*data), GFP_KERNEL);
 290        if (data == NULL)
 291                return -ENOMEM;
 292
 293        if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
 294                goto error;
 295
 296        snd_power_lock(card);
 297        err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
 298        if (err >= 0)
 299                err = snd_ctl_elem_read(card, data);
 300        snd_power_unlock(card);
 301        if (err >= 0)
 302                err = copy_ctl_value_to_user(data32, data, type, count);
 303 error:
 304        kfree(data);
 305        return err;
 306}
 307
 308static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
 309                                          struct snd_ctl_elem_value32 __user *data32)
 310{
 311        struct snd_ctl_elem_value *data;
 312        struct snd_card *card = file->card;
 313        int err, type, count;
 314
 315        data = kzalloc(sizeof(*data), GFP_KERNEL);
 316        if (data == NULL)
 317                return -ENOMEM;
 318
 319        if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
 320                goto error;
 321
 322        snd_power_lock(card);
 323        err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
 324        if (err >= 0)
 325                err = snd_ctl_elem_write(card, file, data);
 326        snd_power_unlock(card);
 327        if (err >= 0)
 328                err = copy_ctl_value_to_user(data32, data, type, count);
 329 error:
 330        kfree(data);
 331        return err;
 332}
 333
 334/* add or replace a user control */
 335static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
 336                                   struct snd_ctl_elem_info32 __user *data32,
 337                                   int replace)
 338{
 339        struct snd_ctl_elem_info *data;
 340        int err;
 341
 342        data = kzalloc(sizeof(*data), GFP_KERNEL);
 343        if (! data)
 344                return -ENOMEM;
 345
 346        err = -EFAULT;
 347        /* id, type, access, count */ \
 348        if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
 349            copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
 350                goto error;
 351        if (get_user(data->owner, &data32->owner) ||
 352            get_user(data->type, &data32->type))
 353                goto error;
 354        switch (data->type) {
 355        case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
 356        case SNDRV_CTL_ELEM_TYPE_INTEGER:
 357                if (get_user(data->value.integer.min, &data32->value.integer.min) ||
 358                    get_user(data->value.integer.max, &data32->value.integer.max) ||
 359                    get_user(data->value.integer.step, &data32->value.integer.step))
 360                        goto error;
 361                break;
 362        case SNDRV_CTL_ELEM_TYPE_INTEGER64:
 363                if (copy_from_user(&data->value.integer64,
 364                                   &data32->value.integer64,
 365                                   sizeof(data->value.integer64)))
 366                        goto error;
 367                break;
 368        case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
 369                if (copy_from_user(&data->value.enumerated,
 370                                   &data32->value.enumerated,
 371                                   sizeof(data->value.enumerated)))
 372                        goto error;
 373                break;
 374        default:
 375                break;
 376        }
 377        err = snd_ctl_elem_add(file, data, replace);
 378 error:
 379        kfree(data);
 380        return err;
 381}  
 382
 383enum {
 384        SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct snd_ctl_elem_list32),
 385        SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct snd_ctl_elem_info32),
 386        SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct snd_ctl_elem_value32),
 387        SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32),
 388        SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct snd_ctl_elem_info32),
 389        SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct snd_ctl_elem_info32),
 390};
 391
 392static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
 393{
 394        struct snd_ctl_file *ctl;
 395        struct snd_kctl_ioctl *p;
 396        void __user *argp = compat_ptr(arg);
 397        int err;
 398
 399        ctl = file->private_data;
 400        snd_assert(ctl && ctl->card, return -ENXIO);
 401
 402        switch (cmd) {
 403        case SNDRV_CTL_IOCTL_PVERSION:
 404        case SNDRV_CTL_IOCTL_CARD_INFO:
 405        case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
 406        case SNDRV_CTL_IOCTL_POWER:
 407        case SNDRV_CTL_IOCTL_POWER_STATE:
 408        case SNDRV_CTL_IOCTL_ELEM_LOCK:
 409        case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
 410        case SNDRV_CTL_IOCTL_ELEM_REMOVE:
 411        case SNDRV_CTL_IOCTL_TLV_READ:
 412        case SNDRV_CTL_IOCTL_TLV_WRITE:
 413        case SNDRV_CTL_IOCTL_TLV_COMMAND:
 414                return snd_ctl_ioctl(file, cmd, (unsigned long)argp);
 415        case SNDRV_CTL_IOCTL_ELEM_LIST32:
 416                return snd_ctl_elem_list_compat(ctl->card, argp);
 417        case SNDRV_CTL_IOCTL_ELEM_INFO32:
 418                return snd_ctl_elem_info_compat(ctl, argp);
 419        case SNDRV_CTL_IOCTL_ELEM_READ32:
 420                return snd_ctl_elem_read_user_compat(ctl->card, argp);
 421        case SNDRV_CTL_IOCTL_ELEM_WRITE32:
 422                return snd_ctl_elem_write_user_compat(ctl, argp);
 423        case SNDRV_CTL_IOCTL_ELEM_ADD32:
 424                return snd_ctl_elem_add_compat(ctl, argp, 0);
 425        case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
 426                return snd_ctl_elem_add_compat(ctl, argp, 1);
 427        }
 428
 429        down_read(&snd_ioctl_rwsem);
 430        list_for_each_entry(p, &snd_control_compat_ioctls, list) {
 431                if (p->fioctl) {
 432                        err = p->fioctl(ctl->card, ctl, cmd, arg);
 433                        if (err != -ENOIOCTLCMD) {
 434                                up_read(&snd_ioctl_rwsem);
 435                                return err;
 436                        }
 437                }
 438        }
 439        up_read(&snd_ioctl_rwsem);
 440        return -ENOIOCTLCMD;
 441}
 442