18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * HWDEP Interface for HD-audio codec 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/compat.h> 118c2ecf20Sopenharmony_ci#include <linux/nospec.h> 128c2ecf20Sopenharmony_ci#include <sound/core.h> 138c2ecf20Sopenharmony_ci#include <sound/hda_codec.h> 148c2ecf20Sopenharmony_ci#include "hda_local.h" 158c2ecf20Sopenharmony_ci#include <sound/hda_hwdep.h> 168c2ecf20Sopenharmony_ci#include <sound/minors.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* 198c2ecf20Sopenharmony_ci * write/read an out-of-bound verb 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_cistatic int verb_write_ioctl(struct hda_codec *codec, 228c2ecf20Sopenharmony_ci struct hda_verb_ioctl __user *arg) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci u32 verb, res; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (get_user(verb, &arg->verb)) 278c2ecf20Sopenharmony_ci return -EFAULT; 288c2ecf20Sopenharmony_ci res = snd_hda_codec_read(codec, verb >> 24, 0, 298c2ecf20Sopenharmony_ci (verb >> 8) & 0xffff, verb & 0xff); 308c2ecf20Sopenharmony_ci if (put_user(res, &arg->res)) 318c2ecf20Sopenharmony_ci return -EFAULT; 328c2ecf20Sopenharmony_ci return 0; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic int get_wcap_ioctl(struct hda_codec *codec, 368c2ecf20Sopenharmony_ci struct hda_verb_ioctl __user *arg) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci u32 verb, res; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci if (get_user(verb, &arg->verb)) 418c2ecf20Sopenharmony_ci return -EFAULT; 428c2ecf20Sopenharmony_ci /* open-code get_wcaps(verb>>24) with nospec */ 438c2ecf20Sopenharmony_ci verb >>= 24; 448c2ecf20Sopenharmony_ci if (verb < codec->core.start_nid || 458c2ecf20Sopenharmony_ci verb >= codec->core.start_nid + codec->core.num_nodes) { 468c2ecf20Sopenharmony_ci res = 0; 478c2ecf20Sopenharmony_ci } else { 488c2ecf20Sopenharmony_ci verb -= codec->core.start_nid; 498c2ecf20Sopenharmony_ci verb = array_index_nospec(verb, codec->core.num_nodes); 508c2ecf20Sopenharmony_ci res = codec->wcaps[verb]; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci if (put_user(res, &arg->res)) 538c2ecf20Sopenharmony_ci return -EFAULT; 548c2ecf20Sopenharmony_ci return 0; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_cistatic int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, 618c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct hda_codec *codec = hw->private_data; 648c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci switch (cmd) { 678c2ecf20Sopenharmony_ci case HDA_IOCTL_PVERSION: 688c2ecf20Sopenharmony_ci return put_user(HDA_HWDEP_VERSION, (int __user *)argp); 698c2ecf20Sopenharmony_ci case HDA_IOCTL_VERB_WRITE: 708c2ecf20Sopenharmony_ci return verb_write_ioctl(codec, argp); 718c2ecf20Sopenharmony_ci case HDA_IOCTL_GET_WCAP: 728c2ecf20Sopenharmony_ci return get_wcap_ioctl(codec, argp); 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 788c2ecf20Sopenharmony_cistatic int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file, 798c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg)); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci#endif 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int hda_hwdep_open(struct snd_hwdep *hw, struct file *file) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci#ifndef CONFIG_SND_DEBUG_VERBOSE 888c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 898c2ecf20Sopenharmony_ci return -EACCES; 908c2ecf20Sopenharmony_ci#endif 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ciint snd_hda_create_hwdep(struct hda_codec *codec) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci char hwname[16]; 978c2ecf20Sopenharmony_ci struct snd_hwdep *hwdep; 988c2ecf20Sopenharmony_ci int err; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci sprintf(hwname, "HDA Codec %d", codec->addr); 1018c2ecf20Sopenharmony_ci err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep); 1028c2ecf20Sopenharmony_ci if (err < 0) 1038c2ecf20Sopenharmony_ci return err; 1048c2ecf20Sopenharmony_ci codec->hwdep = hwdep; 1058c2ecf20Sopenharmony_ci sprintf(hwdep->name, "HDA Codec %d", codec->addr); 1068c2ecf20Sopenharmony_ci hwdep->iface = SNDRV_HWDEP_IFACE_HDA; 1078c2ecf20Sopenharmony_ci hwdep->private_data = codec; 1088c2ecf20Sopenharmony_ci hwdep->exclusive = 1; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci hwdep->ops.open = hda_hwdep_open; 1118c2ecf20Sopenharmony_ci hwdep->ops.ioctl = hda_hwdep_ioctl; 1128c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 1138c2ecf20Sopenharmony_ci hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat; 1148c2ecf20Sopenharmony_ci#endif 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* for sysfs */ 1178c2ecf20Sopenharmony_ci hwdep->dev.groups = snd_hda_dev_attr_groups; 1188c2ecf20Sopenharmony_ci dev_set_drvdata(&hwdep->dev, codec); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci} 122