mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
2.7 KiB
121 lines
2.7 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* HWDEP Interface for HD-audio codec |
|
* |
|
* Copyright (c) 2007 Takashi Iwai <[email protected]> |
|
*/ |
|
|
|
#include <linux/init.h> |
|
#include <linux/slab.h> |
|
#include <linux/compat.h> |
|
#include <linux/nospec.h> |
|
#include <sound/core.h> |
|
#include <sound/hda_codec.h> |
|
#include "hda_local.h" |
|
#include <sound/hda_hwdep.h> |
|
#include <sound/minors.h> |
|
|
|
/* |
|
* write/read an out-of-bound verb |
|
*/ |
|
static int verb_write_ioctl(struct hda_codec *codec, |
|
struct hda_verb_ioctl __user *arg) |
|
{ |
|
u32 verb, res; |
|
|
|
if (get_user(verb, &arg->verb)) |
|
return -EFAULT; |
|
res = snd_hda_codec_read(codec, verb >> 24, 0, |
|
(verb >> 8) & 0xffff, verb & 0xff); |
|
if (put_user(res, &arg->res)) |
|
return -EFAULT; |
|
return 0; |
|
} |
|
|
|
static int get_wcap_ioctl(struct hda_codec *codec, |
|
struct hda_verb_ioctl __user *arg) |
|
{ |
|
u32 verb, res; |
|
|
|
if (get_user(verb, &arg->verb)) |
|
return -EFAULT; |
|
/* open-code get_wcaps(verb>>24) with nospec */ |
|
verb >>= 24; |
|
if (verb < codec->core.start_nid || |
|
verb >= codec->core.start_nid + codec->core.num_nodes) { |
|
res = 0; |
|
} else { |
|
verb -= codec->core.start_nid; |
|
verb = array_index_nospec(verb, codec->core.num_nodes); |
|
res = codec->wcaps[verb]; |
|
} |
|
if (put_user(res, &arg->res)) |
|
return -EFAULT; |
|
return 0; |
|
} |
|
|
|
|
|
/* |
|
*/ |
|
static int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, |
|
unsigned int cmd, unsigned long arg) |
|
{ |
|
struct hda_codec *codec = hw->private_data; |
|
void __user *argp = (void __user *)arg; |
|
|
|
switch (cmd) { |
|
case HDA_IOCTL_PVERSION: |
|
return put_user(HDA_HWDEP_VERSION, (int __user *)argp); |
|
case HDA_IOCTL_VERB_WRITE: |
|
return verb_write_ioctl(codec, argp); |
|
case HDA_IOCTL_GET_WCAP: |
|
return get_wcap_ioctl(codec, argp); |
|
} |
|
return -ENOIOCTLCMD; |
|
} |
|
|
|
#ifdef CONFIG_COMPAT |
|
static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file, |
|
unsigned int cmd, unsigned long arg) |
|
{ |
|
return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg)); |
|
} |
|
#endif |
|
|
|
static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file) |
|
{ |
|
#ifndef CONFIG_SND_DEBUG_VERBOSE |
|
if (!capable(CAP_SYS_RAWIO)) |
|
return -EACCES; |
|
#endif |
|
return 0; |
|
} |
|
|
|
int snd_hda_create_hwdep(struct hda_codec *codec) |
|
{ |
|
char hwname[16]; |
|
struct snd_hwdep *hwdep; |
|
int err; |
|
|
|
sprintf(hwname, "HDA Codec %d", codec->addr); |
|
err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep); |
|
if (err < 0) |
|
return err; |
|
codec->hwdep = hwdep; |
|
sprintf(hwdep->name, "HDA Codec %d", codec->addr); |
|
hwdep->iface = SNDRV_HWDEP_IFACE_HDA; |
|
hwdep->private_data = codec; |
|
hwdep->exclusive = 1; |
|
|
|
hwdep->ops.open = hda_hwdep_open; |
|
hwdep->ops.ioctl = hda_hwdep_ioctl; |
|
#ifdef CONFIG_COMPAT |
|
hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat; |
|
#endif |
|
|
|
/* for sysfs */ |
|
hwdep->dev.groups = snd_hda_dev_attr_groups; |
|
dev_set_drvdata(&hwdep->dev, codec); |
|
|
|
return 0; |
|
}
|
|
|