1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * HWDEP Interface for HD-audio codec 4 * 5 * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de> 6 */ 7 8 #include <linux/init.h> 9 #include <linux/slab.h> 10 #include <linux/compat.h> 11 #include <linux/nospec.h> 12 #include <sound/core.h> 13 #include <sound/hda_codec.h> 14 #include "hda_local.h" 15 #include <sound/hda_hwdep.h> 16 #include <sound/minors.h> 17 18 /* 19 * write/read an out-of-bound verb 20 */ 21 static int verb_write_ioctl(struct hda_codec *codec, 22 struct hda_verb_ioctl __user *arg) 23 { 24 u32 verb, res; 25 26 if (get_user(verb, &arg->verb)) 27 return -EFAULT; 28 res = snd_hda_codec_read(codec, verb >> 24, 0, 29 (verb >> 8) & 0xffff, verb & 0xff); 30 if (put_user(res, &arg->res)) 31 return -EFAULT; 32 return 0; 33 } 34 35 static int get_wcap_ioctl(struct hda_codec *codec, 36 struct hda_verb_ioctl __user *arg) 37 { 38 u32 verb, res; 39 40 if (get_user(verb, &arg->verb)) 41 return -EFAULT; 42 /* open-code get_wcaps(verb>>24) with nospec */ 43 verb >>= 24; 44 if (verb < codec->core.start_nid || 45 verb >= codec->core.start_nid + codec->core.num_nodes) { 46 res = 0; 47 } else { 48 verb -= codec->core.start_nid; 49 verb = array_index_nospec(verb, codec->core.num_nodes); 50 res = codec->wcaps[verb]; 51 } 52 if (put_user(res, &arg->res)) 53 return -EFAULT; 54 return 0; 55 } 56 57 58 /* 59 */ 60 static int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, 61 unsigned int cmd, unsigned long arg) 62 { 63 struct hda_codec *codec = hw->private_data; 64 void __user *argp = (void __user *)arg; 65 66 switch (cmd) { 67 case HDA_IOCTL_PVERSION: 68 return put_user(HDA_HWDEP_VERSION, (int __user *)argp); 69 case HDA_IOCTL_VERB_WRITE: 70 return verb_write_ioctl(codec, argp); 71 case HDA_IOCTL_GET_WCAP: 72 return get_wcap_ioctl(codec, argp); 73 } 74 return -ENOIOCTLCMD; 75 } 76 77 #ifdef CONFIG_COMPAT 78 static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file, 79 unsigned int cmd, unsigned long arg) 80 { 81 return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg)); 82 } 83 #endif 84 85 static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file) 86 { 87 #ifndef CONFIG_SND_DEBUG_VERBOSE 88 if (!capable(CAP_SYS_RAWIO)) 89 return -EACCES; 90 #endif 91 return 0; 92 } 93 94 int snd_hda_create_hwdep(struct hda_codec *codec) 95 { 96 char hwname[16]; 97 struct snd_hwdep *hwdep; 98 int err; 99 100 sprintf(hwname, "HDA Codec %d", codec->addr); 101 err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep); 102 if (err < 0) 103 return err; 104 codec->hwdep = hwdep; 105 sprintf(hwdep->name, "HDA Codec %d", codec->addr); 106 hwdep->iface = SNDRV_HWDEP_IFACE_HDA; 107 hwdep->private_data = codec; 108 hwdep->exclusive = 1; 109 110 hwdep->ops.open = hda_hwdep_open; 111 hwdep->ops.ioctl = hda_hwdep_ioctl; 112 #ifdef CONFIG_COMPAT 113 hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat; 114 #endif 115 116 /* for sysfs */ 117 hwdep->dev.groups = snd_hda_dev_attr_groups; 118 dev_set_drvdata(&hwdep->dev, codec); 119 120 return 0; 121 } 122