linux/sound/pci/hda/hda_hwdep.c
<<
>>
Prefs
   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}
 122