1d0fa1179SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
22807314dSTakashi Iwai /*
32807314dSTakashi Iwai * HWDEP Interface for HD-audio codec
42807314dSTakashi Iwai *
52807314dSTakashi Iwai * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de>
62807314dSTakashi Iwai */
72807314dSTakashi Iwai
82807314dSTakashi Iwai #include <linux/init.h>
92807314dSTakashi Iwai #include <linux/slab.h>
102807314dSTakashi Iwai #include <linux/compat.h>
1169fa6f19STakashi Iwai #include <linux/nospec.h>
122807314dSTakashi Iwai #include <sound/core.h>
13be57bfffSPierre-Louis Bossart #include <sound/hda_codec.h>
142807314dSTakashi Iwai #include "hda_local.h"
152807314dSTakashi Iwai #include <sound/hda_hwdep.h>
16d7ffba19STakashi Iwai #include <sound/minors.h>
172807314dSTakashi Iwai
182807314dSTakashi Iwai /*
192807314dSTakashi Iwai * write/read an out-of-bound verb
202807314dSTakashi Iwai */
verb_write_ioctl(struct hda_codec * codec,struct hda_verb_ioctl __user * arg)212807314dSTakashi Iwai static int verb_write_ioctl(struct hda_codec *codec,
222807314dSTakashi Iwai struct hda_verb_ioctl __user *arg)
232807314dSTakashi Iwai {
242807314dSTakashi Iwai u32 verb, res;
252807314dSTakashi Iwai
262807314dSTakashi Iwai if (get_user(verb, &arg->verb))
272807314dSTakashi Iwai return -EFAULT;
282807314dSTakashi Iwai res = snd_hda_codec_read(codec, verb >> 24, 0,
292807314dSTakashi Iwai (verb >> 8) & 0xffff, verb & 0xff);
302807314dSTakashi Iwai if (put_user(res, &arg->res))
312807314dSTakashi Iwai return -EFAULT;
322807314dSTakashi Iwai return 0;
332807314dSTakashi Iwai }
342807314dSTakashi Iwai
get_wcap_ioctl(struct hda_codec * codec,struct hda_verb_ioctl __user * arg)352807314dSTakashi Iwai static int get_wcap_ioctl(struct hda_codec *codec,
362807314dSTakashi Iwai struct hda_verb_ioctl __user *arg)
372807314dSTakashi Iwai {
382807314dSTakashi Iwai u32 verb, res;
392807314dSTakashi Iwai
402807314dSTakashi Iwai if (get_user(verb, &arg->verb))
412807314dSTakashi Iwai return -EFAULT;
4269fa6f19STakashi Iwai /* open-code get_wcaps(verb>>24) with nospec */
4369fa6f19STakashi Iwai verb >>= 24;
4469fa6f19STakashi Iwai if (verb < codec->core.start_nid ||
4569fa6f19STakashi Iwai verb >= codec->core.start_nid + codec->core.num_nodes) {
4669fa6f19STakashi Iwai res = 0;
4769fa6f19STakashi Iwai } else {
4869fa6f19STakashi Iwai verb -= codec->core.start_nid;
4969fa6f19STakashi Iwai verb = array_index_nospec(verb, codec->core.num_nodes);
5069fa6f19STakashi Iwai res = codec->wcaps[verb];
5169fa6f19STakashi Iwai }
522807314dSTakashi Iwai if (put_user(res, &arg->res))
532807314dSTakashi Iwai return -EFAULT;
542807314dSTakashi Iwai return 0;
552807314dSTakashi Iwai }
562807314dSTakashi Iwai
572807314dSTakashi Iwai
582807314dSTakashi Iwai /*
592807314dSTakashi Iwai */
hda_hwdep_ioctl(struct snd_hwdep * hw,struct file * file,unsigned int cmd,unsigned long arg)602807314dSTakashi Iwai static int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
612807314dSTakashi Iwai unsigned int cmd, unsigned long arg)
622807314dSTakashi Iwai {
632807314dSTakashi Iwai struct hda_codec *codec = hw->private_data;
642807314dSTakashi Iwai void __user *argp = (void __user *)arg;
652807314dSTakashi Iwai
662807314dSTakashi Iwai switch (cmd) {
672807314dSTakashi Iwai case HDA_IOCTL_PVERSION:
682807314dSTakashi Iwai return put_user(HDA_HWDEP_VERSION, (int __user *)argp);
692807314dSTakashi Iwai case HDA_IOCTL_VERB_WRITE:
702807314dSTakashi Iwai return verb_write_ioctl(codec, argp);
712807314dSTakashi Iwai case HDA_IOCTL_GET_WCAP:
722807314dSTakashi Iwai return get_wcap_ioctl(codec, argp);
732807314dSTakashi Iwai }
742807314dSTakashi Iwai return -ENOIOCTLCMD;
752807314dSTakashi Iwai }
762807314dSTakashi Iwai
772807314dSTakashi Iwai #ifdef CONFIG_COMPAT
hda_hwdep_ioctl_compat(struct snd_hwdep * hw,struct file * file,unsigned int cmd,unsigned long arg)782807314dSTakashi Iwai static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
792807314dSTakashi Iwai unsigned int cmd, unsigned long arg)
802807314dSTakashi Iwai {
81312d045cSTakashi Iwai return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg));
822807314dSTakashi Iwai }
832807314dSTakashi Iwai #endif
842807314dSTakashi Iwai
hda_hwdep_open(struct snd_hwdep * hw,struct file * file)852807314dSTakashi Iwai static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
862807314dSTakashi Iwai {
8762cf872aSTakashi Iwai #ifndef CONFIG_SND_DEBUG_VERBOSE
882807314dSTakashi Iwai if (!capable(CAP_SYS_RAWIO))
892807314dSTakashi Iwai return -EACCES;
902807314dSTakashi Iwai #endif
912807314dSTakashi Iwai return 0;
922807314dSTakashi Iwai }
932807314dSTakashi Iwai
snd_hda_create_hwdep(struct hda_codec * codec)946a0f56a7STakashi Iwai int snd_hda_create_hwdep(struct hda_codec *codec)
952807314dSTakashi Iwai {
962807314dSTakashi Iwai char hwname[16];
972807314dSTakashi Iwai struct snd_hwdep *hwdep;
982807314dSTakashi Iwai int err;
992807314dSTakashi Iwai
1002807314dSTakashi Iwai sprintf(hwname, "HDA Codec %d", codec->addr);
1016efdd851STakashi Iwai err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep);
1022807314dSTakashi Iwai if (err < 0)
1032807314dSTakashi Iwai return err;
1042807314dSTakashi Iwai codec->hwdep = hwdep;
1052807314dSTakashi Iwai sprintf(hwdep->name, "HDA Codec %d", codec->addr);
1062807314dSTakashi Iwai hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
1072807314dSTakashi Iwai hwdep->private_data = codec;
1082807314dSTakashi Iwai hwdep->exclusive = 1;
1092807314dSTakashi Iwai
1102807314dSTakashi Iwai hwdep->ops.open = hda_hwdep_open;
1112807314dSTakashi Iwai hwdep->ops.ioctl = hda_hwdep_ioctl;
1122807314dSTakashi Iwai #ifdef CONFIG_COMPAT
1132807314dSTakashi Iwai hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
1142807314dSTakashi Iwai #endif
1152807314dSTakashi Iwai
1167b461600STakashi Iwai /* for sysfs */
117*897c8882STakashi Iwai hwdep->dev->groups = snd_hda_dev_attr_groups;
118*897c8882STakashi Iwai dev_set_drvdata(hwdep->dev, codec);
11913aeaf68STakashi Iwai
1202807314dSTakashi Iwai return 0;
1212807314dSTakashi Iwai }
122