122 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * HWDEP Interface for HD-audio codec4 *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 verb20 */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_COMPAT78static 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#endif84 85static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)86{87#ifndef CONFIG_SND_DEBUG_VERBOSE88 if (!capable(CAP_SYS_RAWIO))89 return -EACCES;90#endif91 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_COMPAT113 hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;114#endif115 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