hda_hwdep.c (2809B)
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 */ 21static 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 35static 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 */ 60static 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 78static 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 85static 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 94int 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}