Commit 9f25159b authored by Jaroslav Kysela's avatar Jaroslav Kysela

[ALSA] unlocked/compat_ioctl rewrite for control API

Control Midlevel
ioctl handler for control API is rewritten using unlocked/compat_ioctl.
The 32bit wrapper is merged to the core module.

Added a new register/unregister function for compat control ioctls.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 7440c4f2
...@@ -119,6 +119,13 @@ int snd_ctl_create(snd_card_t *card); ...@@ -119,6 +119,13 @@ int snd_ctl_create(snd_card_t *card);
int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn); int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn);
int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn); int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn);
#ifdef CONFIG_COMPAT
int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn);
int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn);
#else
#define snd_ctl_register_ioctl_compat(fcn)
#define snd_ctl_unregister_ioctl_compat(fcn)
#endif
int snd_ctl_elem_read(snd_card_t *card, snd_ctl_elem_value_t *control); int snd_ctl_elem_read(snd_card_t *card, snd_ctl_elem_value_t *control);
int snd_ctl_elem_write(snd_card_t *card, snd_ctl_file_t *file, snd_ctl_elem_value_t *control); int snd_ctl_elem_write(snd_card_t *card, snd_ctl_file_t *file, snd_ctl_elem_value_t *control);
......
...@@ -43,6 +43,9 @@ typedef struct _snd_kctl_ioctl { ...@@ -43,6 +43,9 @@ typedef struct _snd_kctl_ioctl {
static DECLARE_RWSEM(snd_ioctl_rwsem); static DECLARE_RWSEM(snd_ioctl_rwsem);
static LIST_HEAD(snd_control_ioctls); static LIST_HEAD(snd_control_ioctls);
#ifdef CONFIG_COMPAT
static LIST_HEAD(snd_control_compat_ioctls);
#endif
static int snd_ctl_open(struct inode *inode, struct file *file) static int snd_ctl_open(struct inode *inode, struct file *file)
{ {
...@@ -595,43 +598,51 @@ static int snd_ctl_elem_list(snd_card_t *card, snd_ctl_elem_list_t __user *_list ...@@ -595,43 +598,51 @@ static int snd_ctl_elem_list(snd_card_t *card, snd_ctl_elem_list_t __user *_list
return 0; return 0;
} }
static int snd_ctl_elem_info(snd_ctl_file_t *ctl, snd_ctl_elem_info_t __user *_info) static int snd_ctl_elem_info(snd_ctl_file_t *ctl, snd_ctl_elem_info_t *info)
{ {
snd_card_t *card = ctl->card; snd_card_t *card = ctl->card;
snd_ctl_elem_info_t info;
snd_kcontrol_t *kctl; snd_kcontrol_t *kctl;
snd_kcontrol_volatile_t *vd; snd_kcontrol_volatile_t *vd;
unsigned int index_offset; unsigned int index_offset;
int result; int result;
if (copy_from_user(&info, _info, sizeof(info)))
return -EFAULT;
down_read(&card->controls_rwsem); down_read(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, &info.id); kctl = snd_ctl_find_id(card, &info->id);
if (kctl == NULL) { if (kctl == NULL) {
up_read(&card->controls_rwsem); up_read(&card->controls_rwsem);
return -ENOENT; return -ENOENT;
} }
#ifdef CONFIG_SND_DEBUG #ifdef CONFIG_SND_DEBUG
info.access = 0; info->access = 0;
#endif #endif
result = kctl->info(kctl, &info); result = kctl->info(kctl, info);
if (result >= 0) { if (result >= 0) {
snd_assert(info.access == 0, ); snd_assert(info->access == 0, );
index_offset = snd_ctl_get_ioff(kctl, &info.id); index_offset = snd_ctl_get_ioff(kctl, &info->id);
vd = &kctl->vd[index_offset]; vd = &kctl->vd[index_offset];
snd_ctl_build_ioff(&info.id, kctl, index_offset); snd_ctl_build_ioff(&info->id, kctl, index_offset);
info.access = vd->access; info->access = vd->access;
if (vd->owner) { if (vd->owner) {
info.access |= SNDRV_CTL_ELEM_ACCESS_LOCK; info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK;
if (vd->owner == ctl) if (vd->owner == ctl)
info.access |= SNDRV_CTL_ELEM_ACCESS_OWNER; info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER;
info.owner = vd->owner_pid; info->owner = vd->owner_pid;
} else { } else {
info.owner = -1; info->owner = -1;
} }
} }
up_read(&card->controls_rwsem); up_read(&card->controls_rwsem);
return result;
}
static int snd_ctl_elem_info_user(snd_ctl_file_t *ctl, snd_ctl_elem_info_t __user *_info)
{
snd_ctl_elem_info_t info;
int result;
if (copy_from_user(&info, _info, sizeof(info)))
return -EFAULT;
result = snd_ctl_elem_info(ctl, &info);
if (result >= 0) if (result >= 0)
if (copy_to_user(_info, &info, sizeof(info))) if (copy_to_user(_info, &info, sizeof(info)))
return -EFAULT; return -EFAULT;
...@@ -816,14 +827,6 @@ static int snd_ctl_elem_user_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t ...@@ -816,14 +827,6 @@ static int snd_ctl_elem_user_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t
struct user_element *ue = kcontrol->private_data; struct user_element *ue = kcontrol->private_data;
*uinfo = ue->info; *uinfo = ue->info;
if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
uinfo->value.enumerated.items = ue->info.value.enumerated.items;
if (uinfo->value.enumerated.item >= ue->info.value.enumerated.items)
uinfo->value.enumerated.item = 0;
strlcpy(uinfo->value.enumerated.name,
(char *)ue->priv_data + uinfo->value.enumerated.item * 64,
64);
}
return 0; return 0;
} }
...@@ -851,28 +854,25 @@ static void snd_ctl_elem_user_free(snd_kcontrol_t * kcontrol) ...@@ -851,28 +854,25 @@ static void snd_ctl_elem_user_free(snd_kcontrol_t * kcontrol)
kfree(kcontrol->private_data); kfree(kcontrol->private_data);
} }
static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_info, int replace) static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t *info, int replace)
{ {
snd_card_t *card = file->card; snd_card_t *card = file->card;
snd_ctl_elem_info_t info;
snd_kcontrol_t kctl, *_kctl; snd_kcontrol_t kctl, *_kctl;
unsigned int access; unsigned int access;
long private_size, extra_size; long private_size;
struct user_element *ue; struct user_element *ue;
int idx, err; int idx, err;
if (card->user_ctl_count >= MAX_USER_CONTROLS) if (card->user_ctl_count >= MAX_USER_CONTROLS)
return -ENOMEM; return -ENOMEM;
if (copy_from_user(&info, _info, sizeof(info))) if (info->count > 1024)
return -EFAULT;
if (info.count > 1024)
return -EINVAL; return -EINVAL;
access = info.access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(info.access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE)); (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|SNDRV_CTL_ELEM_ACCESS_INACTIVE));
info.id.numid = 0; info->id.numid = 0;
memset(&kctl, 0, sizeof(kctl)); memset(&kctl, 0, sizeof(kctl));
down_write(&card->controls_rwsem); down_write(&card->controls_rwsem);
_kctl = snd_ctl_find_id(card, &info.id); _kctl = snd_ctl_find_id(card, &info->id);
err = 0; err = 0;
if (_kctl) { if (_kctl) {
if (replace) if (replace)
...@@ -886,67 +886,50 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i ...@@ -886,67 +886,50 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i
up_write(&card->controls_rwsem); up_write(&card->controls_rwsem);
if (err < 0) if (err < 0)
return err; return err;
memcpy(&kctl.id, &info.id, sizeof(info.id)); memcpy(&kctl.id, &info->id, sizeof(info->id));
kctl.count = info.owner ? info.owner : 1; kctl.count = info->owner ? info->owner : 1;
access |= SNDRV_CTL_ELEM_ACCESS_USER; access |= SNDRV_CTL_ELEM_ACCESS_USER;
kctl.info = snd_ctl_elem_user_info; kctl.info = snd_ctl_elem_user_info;
if (access & SNDRV_CTL_ELEM_ACCESS_READ) if (access & SNDRV_CTL_ELEM_ACCESS_READ)
kctl.get = snd_ctl_elem_user_get; kctl.get = snd_ctl_elem_user_get;
if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
kctl.put = snd_ctl_elem_user_put; kctl.put = snd_ctl_elem_user_put;
extra_size = 0; switch (info->type) {
switch (info.type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN: case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
private_size = sizeof(char); private_size = sizeof(char);
if (info.count > 128) if (info->count > 128)
return -EINVAL; return -EINVAL;
break; break;
case SNDRV_CTL_ELEM_TYPE_INTEGER: case SNDRV_CTL_ELEM_TYPE_INTEGER:
private_size = sizeof(long); private_size = sizeof(long);
if (info.count > 128) if (info->count > 128)
return -EINVAL; return -EINVAL;
break; break;
case SNDRV_CTL_ELEM_TYPE_INTEGER64: case SNDRV_CTL_ELEM_TYPE_INTEGER64:
private_size = sizeof(long long); private_size = sizeof(long long);
if (info.count > 64) if (info->count > 64)
return -EINVAL;
break;
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
private_size = sizeof(unsigned int);
if (info.count > 128)
return -EINVAL; return -EINVAL;
if (info.value.enumerated.items > 128)
return -EINVAL;
extra_size = info.value.enumerated.items * 64;
break; break;
case SNDRV_CTL_ELEM_TYPE_BYTES: case SNDRV_CTL_ELEM_TYPE_BYTES:
private_size = sizeof(unsigned char); private_size = sizeof(unsigned char);
if (info.count > 512) if (info->count > 512)
return -EINVAL; return -EINVAL;
break; break;
case SNDRV_CTL_ELEM_TYPE_IEC958: case SNDRV_CTL_ELEM_TYPE_IEC958:
private_size = sizeof(struct sndrv_aes_iec958); private_size = sizeof(struct sndrv_aes_iec958);
if (info.count != 1) if (info->count != 1)
return -EINVAL; return -EINVAL;
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
private_size *= info.count; private_size *= info->count;
ue = kcalloc(1, sizeof(struct user_element) + private_size + extra_size, GFP_KERNEL); ue = kcalloc(1, sizeof(struct user_element) + private_size, GFP_KERNEL);
if (ue == NULL) if (ue == NULL)
return -ENOMEM; return -ENOMEM;
ue->info = info; ue->info = *info;
ue->elem_data = (char *)ue + sizeof(ue); ue->elem_data = (char *)ue + sizeof(ue);
ue->elem_data_size = private_size; ue->elem_data_size = private_size;
if (extra_size) {
ue->priv_data = (char *)ue + sizeof(ue) + private_size;
ue->priv_data_size = extra_size;
if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
if (copy_from_user(ue->priv_data, *(char __user **)info.value.enumerated.name, extra_size))
return -EFAULT;
}
}
kctl.private_free = snd_ctl_elem_user_free; kctl.private_free = snd_ctl_elem_user_free;
_kctl = snd_ctl_new(&kctl, access); _kctl = snd_ctl_new(&kctl, access);
if (_kctl == NULL) { if (_kctl == NULL) {
...@@ -969,6 +952,14 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i ...@@ -969,6 +952,14 @@ static int snd_ctl_elem_add(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_i
return 0; return 0;
} }
static int snd_ctl_elem_add_user(snd_ctl_file_t *file, snd_ctl_elem_info_t __user *_info, int replace)
{
snd_ctl_elem_info_t info;
if (copy_from_user(&info, _info, sizeof(info)))
return -EFAULT;
return snd_ctl_elem_add(file, &info, replace);
}
static int snd_ctl_elem_remove(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_id) static int snd_ctl_elem_remove(snd_ctl_file_t *file, snd_ctl_elem_id_t __user *_id)
{ {
snd_ctl_elem_id_t id; snd_ctl_elem_id_t id;
...@@ -1039,8 +1030,7 @@ static int snd_ctl_set_power_state(snd_card_t *card, unsigned int power_state) ...@@ -1039,8 +1030,7 @@ static int snd_ctl_set_power_state(snd_card_t *card, unsigned int power_state)
} }
#endif #endif
static inline int _snd_ctl_ioctl(struct inode *inode, struct file *file, static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
unsigned int cmd, unsigned long arg)
{ {
snd_ctl_file_t *ctl; snd_ctl_file_t *ctl;
snd_card_t *card; snd_card_t *card;
...@@ -1061,7 +1051,7 @@ static inline int _snd_ctl_ioctl(struct inode *inode, struct file *file, ...@@ -1061,7 +1051,7 @@ static inline int _snd_ctl_ioctl(struct inode *inode, struct file *file,
case SNDRV_CTL_IOCTL_ELEM_LIST: case SNDRV_CTL_IOCTL_ELEM_LIST:
return snd_ctl_elem_list(ctl->card, argp); return snd_ctl_elem_list(ctl->card, argp);
case SNDRV_CTL_IOCTL_ELEM_INFO: case SNDRV_CTL_IOCTL_ELEM_INFO:
return snd_ctl_elem_info(ctl, argp); return snd_ctl_elem_info_user(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_READ: case SNDRV_CTL_IOCTL_ELEM_READ:
return snd_ctl_elem_read_user(ctl->card, argp); return snd_ctl_elem_read_user(ctl->card, argp);
case SNDRV_CTL_IOCTL_ELEM_WRITE: case SNDRV_CTL_IOCTL_ELEM_WRITE:
...@@ -1071,7 +1061,7 @@ static inline int _snd_ctl_ioctl(struct inode *inode, struct file *file, ...@@ -1071,7 +1061,7 @@ static inline int _snd_ctl_ioctl(struct inode *inode, struct file *file,
case SNDRV_CTL_IOCTL_ELEM_UNLOCK: case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
return snd_ctl_elem_unlock(ctl, argp); return snd_ctl_elem_unlock(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_ADD: case SNDRV_CTL_IOCTL_ELEM_ADD:
return snd_ctl_elem_add(ctl, argp, 0); return snd_ctl_elem_add_user(ctl, argp, 0);
case SNDRV_CTL_IOCTL_ELEM_REPLACE: case SNDRV_CTL_IOCTL_ELEM_REPLACE:
return snd_ctl_elem_add(ctl, argp, 1); return snd_ctl_elem_add(ctl, argp, 1);
case SNDRV_CTL_IOCTL_ELEM_REMOVE: case SNDRV_CTL_IOCTL_ELEM_REMOVE:
...@@ -1113,17 +1103,6 @@ static inline int _snd_ctl_ioctl(struct inode *inode, struct file *file, ...@@ -1113,17 +1103,6 @@ static inline int _snd_ctl_ioctl(struct inode *inode, struct file *file,
return -ENOTTY; return -ENOTTY;
} }
/* FIXME: need to unlock BKL to allow preemption */
static int snd_ctl_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int err;
unlock_kernel();
err = _snd_ctl_ioctl(inode, file, cmd, arg);
lock_kernel();
return err;
}
static ssize_t snd_ctl_read(struct file *file, char __user *buffer, size_t count, loff_t * offset) static ssize_t snd_ctl_read(struct file *file, char __user *buffer, size_t count, loff_t * offset)
{ {
snd_ctl_file_t *ctl; snd_ctl_file_t *ctl;
...@@ -1199,7 +1178,7 @@ static unsigned int snd_ctl_poll(struct file *file, poll_table * wait) ...@@ -1199,7 +1178,7 @@ static unsigned int snd_ctl_poll(struct file *file, poll_table * wait)
* register the device-specific control-ioctls. * register the device-specific control-ioctls.
* called from each device manager like pcm.c, hwdep.c, etc. * called from each device manager like pcm.c, hwdep.c, etc.
*/ */
int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn) static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *lists)
{ {
snd_kctl_ioctl_t *pn; snd_kctl_ioctl_t *pn;
...@@ -1208,22 +1187,34 @@ int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn) ...@@ -1208,22 +1187,34 @@ int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn)
return -ENOMEM; return -ENOMEM;
pn->fioctl = fcn; pn->fioctl = fcn;
down_write(&snd_ioctl_rwsem); down_write(&snd_ioctl_rwsem);
list_add_tail(&pn->list, &snd_control_ioctls); list_add_tail(&pn->list, lists);
up_write(&snd_ioctl_rwsem); up_write(&snd_ioctl_rwsem);
return 0; return 0;
} }
int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls);
}
#ifdef CONFIG_COMPAT
int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls);
}
#endif
/* /*
* de-register the device-specific control-ioctls. * de-register the device-specific control-ioctls.
*/ */
int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn) static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *lists)
{ {
struct list_head *list; struct list_head *list;
snd_kctl_ioctl_t *p; snd_kctl_ioctl_t *p;
snd_runtime_check(fcn != NULL, return -EINVAL); snd_runtime_check(fcn != NULL, return -EINVAL);
down_write(&snd_ioctl_rwsem); down_write(&snd_ioctl_rwsem);
list_for_each(list, &snd_control_ioctls) { list_for_each(list, lists) {
p = list_entry(list, snd_kctl_ioctl_t, list); p = list_entry(list, snd_kctl_ioctl_t, list);
if (p->fioctl == fcn) { if (p->fioctl == fcn) {
list_del(&p->list); list_del(&p->list);
...@@ -1237,6 +1228,19 @@ int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn) ...@@ -1237,6 +1228,19 @@ int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn)
return -EINVAL; return -EINVAL;
} }
int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls);
}
#ifdef CONFIG_COMPAT
int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls);
}
#endif
static int snd_ctl_fasync(int fd, struct file * file, int on) static int snd_ctl_fasync(int fd, struct file * file, int on)
{ {
snd_ctl_file_t *ctl; snd_ctl_file_t *ctl;
...@@ -1248,6 +1252,15 @@ static int snd_ctl_fasync(int fd, struct file * file, int on) ...@@ -1248,6 +1252,15 @@ static int snd_ctl_fasync(int fd, struct file * file, int on)
return 0; return 0;
} }
/*
* ioctl32 compat
*/
#ifdef CONFIG_COMPAT
#include "control_compat.c"
#else
#define snd_ctl_ioctl_compat NULL
#endif
/* /*
* INIT PART * INIT PART
*/ */
...@@ -1259,7 +1272,8 @@ static struct file_operations snd_ctl_f_ops = ...@@ -1259,7 +1272,8 @@ static struct file_operations snd_ctl_f_ops =
.open = snd_ctl_open, .open = snd_ctl_open,
.release = snd_ctl_release, .release = snd_ctl_release,
.poll = snd_ctl_poll, .poll = snd_ctl_poll,
.ioctl = snd_ctl_ioctl, .unlocked_ioctl = snd_ctl_ioctl,
.compat_ioctl = snd_ctl_ioctl_compat,
.fasync = snd_ctl_fasync, .fasync = snd_ctl_fasync,
}; };
......
/*
* compat ioctls 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
*/
/* this file included from control.c */
#include <linux/compat.h>
struct sndrv_ctl_elem_list32 {
u32 offset;
u32 space;
u32 used;
u32 count;
u32 pids;
unsigned char reserved[50];
} /* don't set packed attribute here */;
static int snd_ctl_elem_list_compat(snd_card_t *card, struct sndrv_ctl_elem_list32 __user *data32)
{
struct sndrv_ctl_elem_list __user *data;
compat_caddr_t ptr;
int err;
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 = snd_ctl_elem_list(card, data);
if (err < 0)
return err;
/* copy the result */
if (copy_in_user(data32, data, 4 * sizeof(u32)))
return -EFAULT;
return 0;
}
/*
* control element info
* it uses union, so the things are not easy..
*/
struct sndrv_ctl_elem_info32 {
struct sndrv_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 int snd_ctl_elem_info_compat(snd_ctl_file_t *ctl, struct sndrv_ctl_elem_info32 __user *data32)
{
struct sndrv_ctl_elem_info *data;
int err;
data = kcalloc(1, sizeof(*data), GFP_KERNEL);
if (! data)
return -ENOMEM;
err = -EFAULT;
/* copy id */
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
goto error;
/* we need to copy the item index.
* hope this doesn't break anything..
*/
if (get_user(data->value.enumerated.item, &data32->value.enumerated.item))
goto error;
err = snd_ctl_elem_info(ctl, data);
if (err < 0)
goto error;
/* restore info to 32bit */
err = -EFAULT;
/* id, type, access, count */
if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) ||
copy_to_user(&data32->type, &data->type, 3 * sizeof(u32)))
goto error;
if (put_user(data->owner, &data32->owner))
goto error;
switch (data->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
case SNDRV_CTL_ELEM_TYPE_INTEGER:
if (put_user(data->value.integer.min, &data32->value.integer.min) ||
put_user(data->value.integer.max, &data32->value.integer.max) ||
put_user(data->value.integer.step, &data32->value.integer.step))
goto error;
break;
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
if (copy_to_user(&data32->value.integer64,
&data->value.integer64,
sizeof(data->value.integer64)))
goto error;
break;
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
if (copy_to_user(&data32->value.enumerated,
&data->value.enumerated,
sizeof(data->value.enumerated)))
goto error;
break;
default:
break;
}
err = 0;
error:
kfree(data);
return err;
}
/* read / write */
struct sndrv_ctl_elem_value32 {
struct sndrv_ctl_elem_id id;
unsigned int indirect; /* bit-field causes misalignment */
union {
s32 integer[128];
unsigned char data[512];
#ifndef CONFIG_X86_64
s64 integer64[64];
#endif
} value;
unsigned char reserved[128];
};
/* get the value type and count of the control */
static int get_ctl_type(snd_card_t *card, snd_ctl_elem_id_t *id, int *countp)
{
snd_kcontrol_t *kctl;
snd_ctl_elem_info_t 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;
err = kctl->info(kctl, &info);
up_read(&card->controls_rwsem);
if (err >= 0) {
err = info.type;
*countp = info.count;
}
return err;
}
static int get_elem_size(int type, int count)
{
switch (type) {
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
return sizeof(s64) * count;
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
return sizeof(int) * count;
case SNDRV_CTL_ELEM_TYPE_BYTES:
return 512;
case SNDRV_CTL_ELEM_TYPE_IEC958:
return sizeof(struct sndrv_aes_iec958);
default:
return -1;
}
}
static int copy_ctl_value_from_user(snd_card_t *card,
struct sndrv_ctl_elem_value *data,
struct sndrv_ctl_elem_value32 __user *data32,
int *typep, int *countp)
{
int i, type, count, size;
unsigned int indirect;
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
return -EFAULT;
if (get_user(indirect, &data32->indirect))
return -EFAULT;
if (indirect)
return -EINVAL;
type = get_ctl_type(card, &data->id, &count);
if (type < 0)
return type;
if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
for (i = 0; i < count; i++) {
int val;
if (get_user(val, &data32->value.integer[i]))
return -EFAULT;
data->value.integer.value[i] = val;
}
} else {
size = get_elem_size(type, count);
if (size < 0) {
printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
return -EINVAL;
}
if (copy_from_user(data->value.bytes.data,
data32->value.data, size))
return -EFAULT;
}
*typep = type;
*countp = count;
return 0;
}
/* restore the value to 32bit */
static int copy_ctl_value_to_user(struct sndrv_ctl_elem_value32 __user *data32,
struct sndrv_ctl_elem_value *data,
int type, int count)
{
int i, size;
if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
for (i = 0; i < count; i++) {
int val;
val = data->value.integer.value[i];
if (put_user(val, &data32->value.integer[i]))
return -EFAULT;
}
} else {
size = get_elem_size(type, count);
if (copy_to_user(data32->value.data,
data->value.bytes.data, size))
return -EFAULT;
}
return 0;
}
static int snd_ctl_elem_read_user_compat(snd_card_t *card,
struct sndrv_ctl_elem_value32 __user *data32)
{
struct sndrv_ctl_elem_value *data;
int err, type, count;
data = kcalloc(1, sizeof(*data), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
goto error;
if ((err = snd_ctl_elem_read(card, data)) < 0)
goto error;
err = copy_ctl_value_to_user(data32, data, type, count);
error:
kfree(data);
return err;
}
static int snd_ctl_elem_write_user_compat(snd_ctl_file_t *file,
struct sndrv_ctl_elem_value32 __user *data32)
{
struct sndrv_ctl_elem_value *data;
int err, type, count;
data = kcalloc(1, sizeof(*data), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
if ((err = copy_ctl_value_from_user(file->card, data, data32, &type, &count)) < 0)
goto error;
if ((err = snd_ctl_elem_write(file->card, file, data)) < 0)
goto error;
err = copy_ctl_value_to_user(data32, data, type, count);
error:
kfree(data);
return err;
}
/* add or replace a user control */
static int snd_ctl_elem_add_compat(snd_ctl_file_t *file,
struct sndrv_ctl_elem_info32 __user *data32,
int replace)
{
struct sndrv_ctl_elem_info *data;
int err;
data = kcalloc(1, sizeof(*data), GFP_KERNEL);
if (! data)
return -ENOMEM;
err = -EFAULT;
/* id, type, access, count */ \
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
goto error;
if (get_user(data->owner, &data32->owner) ||
get_user(data->type, &data32->type))
goto error;
switch (data->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
case SNDRV_CTL_ELEM_TYPE_INTEGER:
if (get_user(data->value.integer.min, &data32->value.integer.min) ||
get_user(data->value.integer.max, &data32->value.integer.max) ||
get_user(data->value.integer.step, &data32->value.integer.step))
goto error;
break;
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
if (copy_from_user(&data->value.integer64,
&data32->value.integer64,
sizeof(data->value.integer64)))
goto error;
break;
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
if (copy_from_user(&data->value.enumerated,
&data32->value.enumerated,
sizeof(data->value.enumerated)))
goto error;
break;
default:
break;
}
err = snd_ctl_elem_add(file, data, replace);
error:
kfree(data);
return err;
}
enum {
SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct sndrv_ctl_elem_list32),
SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct sndrv_ctl_elem_info32),
SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct sndrv_ctl_elem_value32),
SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct sndrv_ctl_elem_value32),
SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct sndrv_ctl_elem_info32),
SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct sndrv_ctl_elem_info32),
};
static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
{
snd_ctl_file_t *ctl;
struct list_head *list;
void __user *argp = compat_ptr(arg);
int err;
ctl = file->private_data;
snd_assert(ctl && ctl->card, return -ENXIO);
switch (cmd) {
case SNDRV_CTL_IOCTL_PVERSION:
case SNDRV_CTL_IOCTL_CARD_INFO:
case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
case SNDRV_CTL_IOCTL_POWER:
case SNDRV_CTL_IOCTL_POWER_STATE:
case SNDRV_CTL_IOCTL_ELEM_LOCK:
case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
return snd_ctl_ioctl(file, cmd, (unsigned long)argp);
case SNDRV_CTL_IOCTL_ELEM_LIST32:
return snd_ctl_elem_list_compat(ctl->card, argp);
case SNDRV_CTL_IOCTL_ELEM_INFO32:
return snd_ctl_elem_info_compat(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_READ32:
return snd_ctl_elem_read_user_compat(ctl->card, argp);
case SNDRV_CTL_IOCTL_ELEM_WRITE32:
return snd_ctl_elem_write_user_compat(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_ADD32:
return snd_ctl_elem_add_compat(ctl, argp, 0);
case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
return snd_ctl_elem_add_compat(ctl, argp, 1);
}
down_read(&snd_ioctl_rwsem);
list_for_each(list, &snd_control_compat_ioctls) {
snd_kctl_ioctl_t *p = list_entry(list, snd_kctl_ioctl_t, list);
if (p->fioctl) {
err = p->fioctl(ctl->card, ctl, cmd, arg);
if (err != -ENOIOCTLCMD) {
up_read(&snd_ioctl_rwsem);
return err;
}
}
}
up_read(&snd_ioctl_rwsem);
return -ENOIOCTLCMD;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment