Logo Search packages:      
Sourcecode: alsa-driver version File versions  Download package

ioctl32_new.c

/*
 *   32bit -> 64bit ioctl wrapper for control API
 *   Copyright (c) by Takashi Iwai <tiwai@suse.de>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */

#include "adriver.h"
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/minors.h>
#include <asm/uaccess.h>
#include "ioctl32.h"


/*
 * register/unregister mappers
 * exported for other modules
 */

MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("ioctl32 wrapper for ALSA");
MODULE_LICENSE("GPL");

int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *));
int unregister_ioctl32_conversion(unsigned int cmd);


int snd_ioctl32_register(struct ioctl32_mapper *mappers)
{
      int err;
      struct ioctl32_mapper *m;

      for (m = mappers; m->cmd; m++) {
            err = register_ioctl32_conversion(m->cmd, m->handler);
            if (err >= 0)
                  m->registered++;
      }
      return 0;
}

void snd_ioctl32_unregister(struct ioctl32_mapper *mappers)
{
      struct ioctl32_mapper *m;

      for (m = mappers; m->cmd; m++) {
            if (m->registered) {
                  unregister_ioctl32_conversion(m->cmd);
                  m->registered = 0;
            }
      }
}


/*
 * compatible wrapper
 */
int snd_ioctl32_compat(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *filp)
{
      if (! filp->f_op || ! filp->f_op->ioctl)
            return -ENOTTY;
      return filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
}


/*
 * Controls
 */

00089 struct snd_ctl_elem_list32 {
      u32 offset;
      u32 space;
      u32 used;
      u32 count;
      u32 pids;
      unsigned char reserved[50];
} /* don't set packed attribute here */;

static inline int _snd_ioctl32_ctl_elem_list(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl)
{
      struct snd_ctl_elem_list32 __user *data32;
      struct snd_ctl_elem_list __user *data;
      compat_caddr_t ptr;
      int err;

      data32 = compat_ptr(arg);
      data = compat_alloc_user_space(sizeof(*data));

      /* offset, space, used, count */
      if (copy_in_user(data, data32, 4 * sizeof(u32)))
            return -EFAULT;
      /* pids */
      if (__get_user(ptr, &data32->pids) ||
          __put_user(compat_ptr(ptr), &data->pids))
            return -EFAULT;
      err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)data);
      if (err < 0)
            return err;
      /* copy the result */
      if (copy_in_user(data32, data, 4 * sizeof(u32)))
            return -EFAULT;
      return 0;
}

DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_list, ctl_elem_list, SNDRV_CTL_IOCTL_ELEM_LIST);

/*
 * control element info
 * it uses union, so the things are not easy..
 */

00131 struct snd_ctl_elem_info32 {
      struct snd_ctl_elem_id id; // the size of struct is same
      s32 type;
      u32 access;
      u32 count;
      s32 owner;
      union {
            struct {
                  s32 min;
                  s32 max;
                  s32 step;
            } integer;
            struct {
                  u64 min;
                  u64 max;
                  u64 step;
            } integer64;
            struct {
                  u32 items;
                  u32 item;
                  char name[64];
            } enumerated;
            unsigned char reserved[128];
      } value;
      unsigned char reserved[64];
} __attribute__((packed));

static inline int _snd_ioctl32_ctl_elem_info(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl)
{
      struct snd_ctl_elem_info __user *data, *src;
      struct snd_ctl_elem_info32 __user *data32, *dst;
      unsigned int type;
      int err;

      data32 = compat_ptr(arg);
      data = compat_alloc_user_space(sizeof(*data));

      /* copy id */
      if (copy_in_user(&data->id, &data32->id, sizeof(data->id)))
            return -EFAULT;
      /* we need to copy the item index.
       * hope this doesn't break anything..
       */
      if (copy_in_user(&data->value.enumerated.item,
                   &data32->value.enumerated.item,
                   sizeof(data->value.enumerated.item)))
            return -EFAULT;
      err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)data);
      if (err < 0)
            return err;
      /* restore info to 32bit */
      /* for COPY_CVT macro */
      src = data;
      dst = data32;
      /* id, type, access, count */
      if (copy_in_user(&data32->id, &data->id, sizeof(data->id)) ||
          copy_in_user(&data32->type, &data->type, 3 * sizeof(u32)))
            return -EFAULT;
      COPY_CVT(owner);
      __get_user(type, &data->type);
      switch (type) {
      case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
      case SNDRV_CTL_ELEM_TYPE_INTEGER:
            COPY_CVT(value.integer.min);
            COPY_CVT(value.integer.max);
            COPY_CVT(value.integer.step);
            break;
      case SNDRV_CTL_ELEM_TYPE_INTEGER64:
            if (copy_in_user(&data32->value.integer64,
                         &data->value.integer64,
                         sizeof(data->value.integer64)))
                  return -EFAULT;
            break;
      case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
            if (copy_in_user(&data32->value.enumerated,
                         &data->value.enumerated,
                         sizeof(data->value.enumerated)))
                  return -EFAULT;
            break;
      default:
            break;
      }
      return 0;
}

DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_info, ctl_elem_info, SNDRV_CTL_IOCTL_ELEM_INFO);

00218 struct snd_ctl_elem_value32 {
      struct snd_ctl_elem_id id;
      unsigned int indirect;  /* bit-field causes misalignment */
        union {
            s32 integer[128]; /* integer and boolean need conversion */
#ifndef CONFIG_X86_64
            s64 integer64[64];      /* for alignment */
#endif
            unsigned char data[512];      /* others should be compatible */
        } value;
        unsigned char reserved[128];      /* not used */
};


/* hmm, it's so hard to retrieve the value type from the control id.. */
static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id)
{
      struct snd_kcontrol *kctl;
      struct snd_ctl_elem_info info;
      int err;

      down_read(&card->controls_rwsem);
      kctl = snd_ctl_find_id(card, id);
      if (! kctl) {
            up_read(&card->controls_rwsem);
            return -ENXIO;
      }
      info.id = *id;
      snd_power_lock(card);
      err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
      if (err >= 0)
            err = kctl->info(kctl, &info);
      snd_power_unlock(card);
      up_read(&card->controls_rwsem);
      if (err >= 0)
            err = info.type;
      return err;
}

static inline int _snd_ioctl32_ctl_elem_value(unsigned int fd, unsigned int cmd, unsigned long arg, struct file *file, unsigned int native_ctl)
{
      struct snd_ctl_elem_value *data;
      struct snd_ctl_elem_value32 __user *data32;
      struct snd_ctl_file *ctl;
      struct snd_card *card;
      int err, i, indirect;
      int type;

      /* sanity check */
      if (imajor(file->f_dentry->d_inode) != snd_major ||
          !snd_lookup_minor_data(iminor(file->f_dentry->d_inode),
                           SNDRV_DEVICE_TYPE_CONTROL))
            return -ENOTTY;

      if ((ctl = file->private_data) == NULL)
            return -ENOTTY;

      data32 = compat_ptr(arg);
      data = kzalloc(sizeof(*data), GFP_KERNEL);
      if (data == NULL)
            return -ENOMEM;

      if (copy_from_user(&data->id, &data32->id, sizeof(data->id))) {
            err = -EFAULT;
            goto __end;
      }
      if (__get_user(indirect, &data32->indirect)) {
            err = -EFAULT;
            goto __end;
      }
      /* FIXME: indirect access is not supported */
      if (indirect) {
            err = -EINVAL;
            goto __end;
      }
      card = ctl->card;
      type = get_ctl_type(card, &data->id);
      if (type < 0) {
            err = type;
            goto __end;
      }

      switch (type) {
      case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
      case SNDRV_CTL_ELEM_TYPE_INTEGER:
            for (i = 0; i < 128; i++) {
                  int val;
                  if (__get_user(val, &data32->value.integer[i])) {
                        err = -EFAULT;
                        goto __end;
                  }
                  data->value.integer.value[i] = val;
            }
            break;
      case SNDRV_CTL_ELEM_TYPE_INTEGER64:
      case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
      case SNDRV_CTL_ELEM_TYPE_BYTES:
      case SNDRV_CTL_ELEM_TYPE_IEC958:
            if (__copy_from_user(data->value.bytes.data,
                             data32->value.data,
                             sizeof(data32->value.data))) {
                  err = -EFAULT;
                  goto __end;
            }
            break;
      default:
            printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
            err = -EINVAL;
            goto __end;
      }

      snd_power_lock(card);
      err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
      if (err >= 0) {
            if (native_ctl == SNDRV_CTL_IOCTL_ELEM_READ)
                  err = snd_ctl_elem_read(card, data);
            else
                  err = snd_ctl_elem_write(card, ctl, data);
      }
      snd_power_unlock(card);
      if (err < 0)
            goto __end;
      /* restore info to 32bit */
      switch (type) {
      case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
      case SNDRV_CTL_ELEM_TYPE_INTEGER:
            for (i = 0; i < 128; i++) {
                  int val;
                  val = data->value.integer.value[i];
                  if (__put_user(val, &data32->value.integer[i])) {
                        err = -EFAULT;
                        goto __end;
                  }
            }
            break;
      default:
            if (__copy_to_user(data32->value.data,
                           data->value.bytes.data,
                           sizeof(data32->value.data))) {
                  err = -EFAULT;
                  goto __end;
            }
            break;
      }
      err = 0;
      __end:
      kfree(data);
      return err;
}

DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_read, ctl_elem_value, SNDRV_CTL_IOCTL_ELEM_READ);
DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_write, ctl_elem_value, SNDRV_CTL_IOCTL_ELEM_WRITE);

/*
 */

#define AP(x) snd_ioctl32_##x

enum {
      SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct snd_ctl_elem_list32),
      SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct snd_ctl_elem_info32),
      SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct snd_ctl_elem_value32),
      SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32),
};

static struct ioctl32_mapper control_mappers[] = {
      /* controls (without rawmidi, hwdep, timer releated ones) */
      MAP_COMPAT(SNDRV_CTL_IOCTL_PVERSION),
      MAP_COMPAT(SNDRV_CTL_IOCTL_CARD_INFO),
      { SNDRV_CTL_IOCTL_ELEM_LIST32, AP(ctl_elem_list) },
      { SNDRV_CTL_IOCTL_ELEM_INFO32, AP(ctl_elem_info) },
      { SNDRV_CTL_IOCTL_ELEM_READ32, AP(ctl_elem_read) },
      { SNDRV_CTL_IOCTL_ELEM_WRITE32, AP(ctl_elem_write) },
      MAP_COMPAT(SNDRV_CTL_IOCTL_ELEM_LOCK),
      MAP_COMPAT(SNDRV_CTL_IOCTL_ELEM_UNLOCK),
      MAP_COMPAT(SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS),
      MAP_COMPAT(SNDRV_CTL_IOCTL_HWDEP_INFO),
      MAP_COMPAT(SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE),
      MAP_COMPAT(SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE),
      MAP_COMPAT(SNDRV_CTL_IOCTL_PCM_INFO),
      MAP_COMPAT(SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE),
      MAP_COMPAT(SNDRV_CTL_IOCTL_POWER),
      MAP_COMPAT(SNDRV_CTL_IOCTL_POWER_STATE),
      { 0 }
};


/*
 */

extern struct ioctl32_mapper pcm_mappers[];
extern struct ioctl32_mapper rawmidi_mappers[];
extern struct ioctl32_mapper timer_mappers[];
extern struct ioctl32_mapper hwdep_mappers[];
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
extern struct ioctl32_mapper seq_mappers[];
#endif

static void snd_ioctl32_done(void)
{
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
      snd_ioctl32_unregister(seq_mappers);
#endif
      snd_ioctl32_unregister(hwdep_mappers);
      snd_ioctl32_unregister(timer_mappers);
      snd_ioctl32_unregister(rawmidi_mappers);
      snd_ioctl32_unregister(pcm_mappers);
      snd_ioctl32_unregister(control_mappers);
}

static int __init snd_ioctl32_init(void)
{
      snd_ioctl32_register(control_mappers);
      snd_ioctl32_register(pcm_mappers);
      snd_ioctl32_register(rawmidi_mappers);
      snd_ioctl32_register(timer_mappers);
      snd_ioctl32_register(hwdep_mappers);
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
      snd_ioctl32_register(seq_mappers);
#endif
      return 0;
}

module_init(snd_ioctl32_init)
module_exit(snd_ioctl32_done)

Generated by  Doxygen 1.6.0   Back to index