xref: /openbmc/linux/sound/pci/hda/hda_hwdep.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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