hc
2024-12-19 9370bb92b2d16684ee45cf24e879c93c509162da
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * HWDEP Interface for HD-audio codec
 *
 * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de>
 */
 
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/nospec.h>
#include <sound/core.h>
#include <sound/hda_codec.h>
#include "hda_local.h"
#include <sound/hda_hwdep.h>
#include <sound/minors.h>
 
/*
 * write/read an out-of-bound verb
 */
static int verb_write_ioctl(struct hda_codec *codec,
               struct hda_verb_ioctl __user *arg)
{
   u32 verb, res;
 
   if (get_user(verb, &arg->verb))
       return -EFAULT;
   res = snd_hda_codec_read(codec, verb >> 24, 0,
                (verb >> 8) & 0xffff, verb & 0xff);
   if (put_user(res, &arg->res))
       return -EFAULT;
   return 0;
}
 
static int get_wcap_ioctl(struct hda_codec *codec,
             struct hda_verb_ioctl __user *arg)
{
   u32 verb, res;
   
   if (get_user(verb, &arg->verb))
       return -EFAULT;
   /* open-code get_wcaps(verb>>24) with nospec */
   verb >>= 24;
   if (verb < codec->core.start_nid ||
       verb >= codec->core.start_nid + codec->core.num_nodes) {
       res = 0;
   } else {
       verb -= codec->core.start_nid;
       verb = array_index_nospec(verb, codec->core.num_nodes);
       res = codec->wcaps[verb];
   }
   if (put_user(res, &arg->res))
       return -EFAULT;
   return 0;
}
 
 
/*
 */
static int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
              unsigned int cmd, unsigned long arg)
{
   struct hda_codec *codec = hw->private_data;
   void __user *argp = (void __user *)arg;
 
   switch (cmd) {
   case HDA_IOCTL_PVERSION:
       return put_user(HDA_HWDEP_VERSION, (int __user *)argp);
   case HDA_IOCTL_VERB_WRITE:
       return verb_write_ioctl(codec, argp);
   case HDA_IOCTL_GET_WCAP:
       return get_wcap_ioctl(codec, argp);
   }
   return -ENOIOCTLCMD;
}
 
#ifdef CONFIG_COMPAT
static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
                 unsigned int cmd, unsigned long arg)
{
   return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg));
}
#endif
 
static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
{
#ifndef CONFIG_SND_DEBUG_VERBOSE
   if (!capable(CAP_SYS_RAWIO))
       return -EACCES;
#endif
   return 0;
}
 
int snd_hda_create_hwdep(struct hda_codec *codec)
{
   char hwname[16];
   struct snd_hwdep *hwdep;
   int err;
 
   sprintf(hwname, "HDA Codec %d", codec->addr);
   err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep);
   if (err < 0)
       return err;
   codec->hwdep = hwdep;
   sprintf(hwdep->name, "HDA Codec %d", codec->addr);
   hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
   hwdep->private_data = codec;
   hwdep->exclusive = 1;
 
   hwdep->ops.open = hda_hwdep_open;
   hwdep->ops.ioctl = hda_hwdep_ioctl;
#ifdef CONFIG_COMPAT
   hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
#endif
 
   /* for sysfs */
   hwdep->dev.groups = snd_hda_dev_attr_groups;
   dev_set_drvdata(&hwdep->dev, codec);
 
   return 0;
}