Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * HWDEP Interface for HD-audio codec
0004  *
0005  * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de>
0006  */
0007 
0008 #include <linux/init.h>
0009 #include <linux/slab.h>
0010 #include <linux/compat.h>
0011 #include <linux/nospec.h>
0012 #include <sound/core.h>
0013 #include <sound/hda_codec.h>
0014 #include "hda_local.h"
0015 #include <sound/hda_hwdep.h>
0016 #include <sound/minors.h>
0017 
0018 /*
0019  * write/read an out-of-bound verb
0020  */
0021 static int verb_write_ioctl(struct hda_codec *codec,
0022                 struct hda_verb_ioctl __user *arg)
0023 {
0024     u32 verb, res;
0025 
0026     if (get_user(verb, &arg->verb))
0027         return -EFAULT;
0028     res = snd_hda_codec_read(codec, verb >> 24, 0,
0029                  (verb >> 8) & 0xffff, verb & 0xff);
0030     if (put_user(res, &arg->res))
0031         return -EFAULT;
0032     return 0;
0033 }
0034 
0035 static int get_wcap_ioctl(struct hda_codec *codec,
0036               struct hda_verb_ioctl __user *arg)
0037 {
0038     u32 verb, res;
0039     
0040     if (get_user(verb, &arg->verb))
0041         return -EFAULT;
0042     /* open-code get_wcaps(verb>>24) with nospec */
0043     verb >>= 24;
0044     if (verb < codec->core.start_nid ||
0045         verb >= codec->core.start_nid + codec->core.num_nodes) {
0046         res = 0;
0047     } else {
0048         verb -= codec->core.start_nid;
0049         verb = array_index_nospec(verb, codec->core.num_nodes);
0050         res = codec->wcaps[verb];
0051     }
0052     if (put_user(res, &arg->res))
0053         return -EFAULT;
0054     return 0;
0055 }
0056 
0057 
0058 /*
0059  */
0060 static int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
0061                unsigned int cmd, unsigned long arg)
0062 {
0063     struct hda_codec *codec = hw->private_data;
0064     void __user *argp = (void __user *)arg;
0065 
0066     switch (cmd) {
0067     case HDA_IOCTL_PVERSION:
0068         return put_user(HDA_HWDEP_VERSION, (int __user *)argp);
0069     case HDA_IOCTL_VERB_WRITE:
0070         return verb_write_ioctl(codec, argp);
0071     case HDA_IOCTL_GET_WCAP:
0072         return get_wcap_ioctl(codec, argp);
0073     }
0074     return -ENOIOCTLCMD;
0075 }
0076 
0077 #ifdef CONFIG_COMPAT
0078 static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
0079                   unsigned int cmd, unsigned long arg)
0080 {
0081     return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg));
0082 }
0083 #endif
0084 
0085 static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
0086 {
0087 #ifndef CONFIG_SND_DEBUG_VERBOSE
0088     if (!capable(CAP_SYS_RAWIO))
0089         return -EACCES;
0090 #endif
0091     return 0;
0092 }
0093 
0094 int snd_hda_create_hwdep(struct hda_codec *codec)
0095 {
0096     char hwname[16];
0097     struct snd_hwdep *hwdep;
0098     int err;
0099 
0100     sprintf(hwname, "HDA Codec %d", codec->addr);
0101     err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep);
0102     if (err < 0)
0103         return err;
0104     codec->hwdep = hwdep;
0105     sprintf(hwdep->name, "HDA Codec %d", codec->addr);
0106     hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
0107     hwdep->private_data = codec;
0108     hwdep->exclusive = 1;
0109 
0110     hwdep->ops.open = hda_hwdep_open;
0111     hwdep->ops.ioctl = hda_hwdep_ioctl;
0112 #ifdef CONFIG_COMPAT
0113     hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
0114 #endif
0115 
0116     /* for sysfs */
0117     hwdep->dev.groups = snd_hda_dev_attr_groups;
0118     dev_set_drvdata(&hwdep->dev, codec);
0119 
0120     return 0;
0121 }