cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hdac_sysfs.c (11441B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * sysfs support for HD-audio core device
      4 */
      5
      6#include <linux/slab.h>
      7#include <linux/sysfs.h>
      8#include <linux/device.h>
      9#include <sound/core.h>
     10#include <sound/hdaudio.h>
     11#include "local.h"
     12
     13struct hdac_widget_tree {
     14	struct kobject *root;
     15	struct kobject *afg;
     16	struct kobject **nodes;
     17};
     18
     19#define CODEC_ATTR(type)					\
     20static ssize_t type##_show(struct device *dev,			\
     21			   struct device_attribute *attr,	\
     22			   char *buf)				\
     23{								\
     24	struct hdac_device *codec = dev_to_hdac_dev(dev);	\
     25	return sprintf(buf, "0x%x\n", codec->type);		\
     26} \
     27static DEVICE_ATTR_RO(type)
     28
     29#define CODEC_ATTR_STR(type)					\
     30static ssize_t type##_show(struct device *dev,			\
     31			     struct device_attribute *attr,	\
     32					char *buf)		\
     33{								\
     34	struct hdac_device *codec = dev_to_hdac_dev(dev);	\
     35	return sprintf(buf, "%s\n",				\
     36		       codec->type ? codec->type : "");		\
     37} \
     38static DEVICE_ATTR_RO(type)
     39
     40CODEC_ATTR(type);
     41CODEC_ATTR(vendor_id);
     42CODEC_ATTR(subsystem_id);
     43CODEC_ATTR(revision_id);
     44CODEC_ATTR(afg);
     45CODEC_ATTR(mfg);
     46CODEC_ATTR_STR(vendor_name);
     47CODEC_ATTR_STR(chip_name);
     48
     49static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
     50			     char *buf)
     51{
     52	return snd_hdac_codec_modalias(dev_to_hdac_dev(dev), buf, 256);
     53}
     54static DEVICE_ATTR_RO(modalias);
     55
     56static struct attribute *hdac_dev_attrs[] = {
     57	&dev_attr_type.attr,
     58	&dev_attr_vendor_id.attr,
     59	&dev_attr_subsystem_id.attr,
     60	&dev_attr_revision_id.attr,
     61	&dev_attr_afg.attr,
     62	&dev_attr_mfg.attr,
     63	&dev_attr_vendor_name.attr,
     64	&dev_attr_chip_name.attr,
     65	&dev_attr_modalias.attr,
     66	NULL
     67};
     68
     69static const struct attribute_group hdac_dev_attr_group = {
     70	.attrs	= hdac_dev_attrs,
     71};
     72
     73const struct attribute_group *hdac_dev_attr_groups[] = {
     74	&hdac_dev_attr_group,
     75	NULL
     76};
     77
     78/*
     79 * Widget tree sysfs
     80 *
     81 * This is a tree showing the attributes of each widget.  It appears like
     82 * /sys/bus/hdaudioC0D0/widgets/04/caps
     83 */
     84
     85struct widget_attribute;
     86
     87struct widget_attribute {
     88	struct attribute	attr;
     89	ssize_t (*show)(struct hdac_device *codec, hda_nid_t nid,
     90			struct widget_attribute *attr, char *buf);
     91	ssize_t (*store)(struct hdac_device *codec, hda_nid_t nid,
     92			 struct widget_attribute *attr,
     93			 const char *buf, size_t count);
     94};
     95
     96static int get_codec_nid(struct kobject *kobj, struct hdac_device **codecp)
     97{
     98	struct device *dev = kobj_to_dev(kobj->parent->parent);
     99	int nid;
    100	ssize_t ret;
    101
    102	ret = kstrtoint(kobj->name, 16, &nid);
    103	if (ret < 0)
    104		return ret;
    105	*codecp = dev_to_hdac_dev(dev);
    106	return nid;
    107}
    108
    109static ssize_t widget_attr_show(struct kobject *kobj, struct attribute *attr,
    110				char *buf)
    111{
    112	struct widget_attribute *wid_attr =
    113		container_of(attr, struct widget_attribute, attr);
    114	struct hdac_device *codec;
    115	int nid;
    116
    117	if (!wid_attr->show)
    118		return -EIO;
    119	nid = get_codec_nid(kobj, &codec);
    120	if (nid < 0)
    121		return nid;
    122	return wid_attr->show(codec, nid, wid_attr, buf);
    123}
    124
    125static ssize_t widget_attr_store(struct kobject *kobj, struct attribute *attr,
    126				 const char *buf, size_t count)
    127{
    128	struct widget_attribute *wid_attr =
    129		container_of(attr, struct widget_attribute, attr);
    130	struct hdac_device *codec;
    131	int nid;
    132
    133	if (!wid_attr->store)
    134		return -EIO;
    135	nid = get_codec_nid(kobj, &codec);
    136	if (nid < 0)
    137		return nid;
    138	return wid_attr->store(codec, nid, wid_attr, buf, count);
    139}
    140
    141static const struct sysfs_ops widget_sysfs_ops = {
    142	.show	= widget_attr_show,
    143	.store	= widget_attr_store,
    144};
    145
    146static void widget_release(struct kobject *kobj)
    147{
    148	kfree(kobj);
    149}
    150
    151static struct kobj_type widget_ktype = {
    152	.release	= widget_release,
    153	.sysfs_ops	= &widget_sysfs_ops,
    154};
    155
    156#define WIDGET_ATTR_RO(_name) \
    157	struct widget_attribute wid_attr_##_name = __ATTR_RO(_name)
    158#define WIDGET_ATTR_RW(_name) \
    159	struct widget_attribute wid_attr_##_name = __ATTR_RW(_name)
    160
    161static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid,
    162			struct widget_attribute *attr, char *buf)
    163{
    164	return sprintf(buf, "0x%08x\n", get_wcaps(codec, nid));
    165}
    166
    167static ssize_t pin_caps_show(struct hdac_device *codec, hda_nid_t nid,
    168			     struct widget_attribute *attr, char *buf)
    169{
    170	if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
    171		return 0;
    172	return sprintf(buf, "0x%08x\n",
    173		       snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP));
    174}
    175
    176static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid,
    177			    struct widget_attribute *attr, char *buf)
    178{
    179	unsigned int val;
    180
    181	if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
    182		return 0;
    183	if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val))
    184		return 0;
    185	return sprintf(buf, "0x%08x\n", val);
    186}
    187
    188static bool has_pcm_cap(struct hdac_device *codec, hda_nid_t nid)
    189{
    190	if (nid == codec->afg || nid == codec->mfg)
    191		return true;
    192	switch (get_wcaps_type(get_wcaps(codec, nid))) {
    193	case AC_WID_AUD_OUT:
    194	case AC_WID_AUD_IN:
    195		return true;
    196	default:
    197		return false;
    198	}
    199}
    200
    201static ssize_t pcm_caps_show(struct hdac_device *codec, hda_nid_t nid,
    202			     struct widget_attribute *attr, char *buf)
    203{
    204	if (!has_pcm_cap(codec, nid))
    205		return 0;
    206	return sprintf(buf, "0x%08x\n",
    207		       snd_hdac_read_parm(codec, nid, AC_PAR_PCM));
    208}
    209
    210static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid,
    211				struct widget_attribute *attr, char *buf)
    212{
    213	if (!has_pcm_cap(codec, nid))
    214		return 0;
    215	return sprintf(buf, "0x%08x\n",
    216		       snd_hdac_read_parm(codec, nid, AC_PAR_STREAM));
    217}
    218
    219static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid,
    220				struct widget_attribute *attr, char *buf)
    221{
    222	if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
    223		return 0;
    224	return sprintf(buf, "0x%08x\n",
    225		       snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP));
    226}
    227
    228static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid,
    229				 struct widget_attribute *attr, char *buf)
    230{
    231	if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
    232		return 0;
    233	return sprintf(buf, "0x%08x\n",
    234		       snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP));
    235}
    236
    237static ssize_t power_caps_show(struct hdac_device *codec, hda_nid_t nid,
    238			       struct widget_attribute *attr, char *buf)
    239{
    240	if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_POWER))
    241		return 0;
    242	return sprintf(buf, "0x%08x\n",
    243		       snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE));
    244}
    245
    246static ssize_t gpio_caps_show(struct hdac_device *codec, hda_nid_t nid,
    247			      struct widget_attribute *attr, char *buf)
    248{
    249	return sprintf(buf, "0x%08x\n",
    250		       snd_hdac_read_parm(codec, nid, AC_PAR_GPIO_CAP));
    251}
    252
    253static ssize_t connections_show(struct hdac_device *codec, hda_nid_t nid,
    254				struct widget_attribute *attr, char *buf)
    255{
    256	hda_nid_t list[32];
    257	int i, nconns;
    258	ssize_t ret = 0;
    259
    260	nconns = snd_hdac_get_connections(codec, nid, list, ARRAY_SIZE(list));
    261	if (nconns <= 0)
    262		return nconns;
    263	for (i = 0; i < nconns; i++)
    264		ret += sprintf(buf + ret, "%s0x%02x", i ? " " : "", list[i]);
    265	ret += sprintf(buf + ret, "\n");
    266	return ret;
    267}
    268
    269static WIDGET_ATTR_RO(caps);
    270static WIDGET_ATTR_RO(pin_caps);
    271static WIDGET_ATTR_RO(pin_cfg);
    272static WIDGET_ATTR_RO(pcm_caps);
    273static WIDGET_ATTR_RO(pcm_formats);
    274static WIDGET_ATTR_RO(amp_in_caps);
    275static WIDGET_ATTR_RO(amp_out_caps);
    276static WIDGET_ATTR_RO(power_caps);
    277static WIDGET_ATTR_RO(gpio_caps);
    278static WIDGET_ATTR_RO(connections);
    279
    280static struct attribute *widget_node_attrs[] = {
    281	&wid_attr_caps.attr,
    282	&wid_attr_pin_caps.attr,
    283	&wid_attr_pin_cfg.attr,
    284	&wid_attr_pcm_caps.attr,
    285	&wid_attr_pcm_formats.attr,
    286	&wid_attr_amp_in_caps.attr,
    287	&wid_attr_amp_out_caps.attr,
    288	&wid_attr_power_caps.attr,
    289	&wid_attr_connections.attr,
    290	NULL,
    291};
    292
    293static struct attribute *widget_afg_attrs[] = {
    294	&wid_attr_pcm_caps.attr,
    295	&wid_attr_pcm_formats.attr,
    296	&wid_attr_amp_in_caps.attr,
    297	&wid_attr_amp_out_caps.attr,
    298	&wid_attr_power_caps.attr,
    299	&wid_attr_gpio_caps.attr,
    300	NULL,
    301};
    302
    303static const struct attribute_group widget_node_group = {
    304	.attrs = widget_node_attrs,
    305};
    306
    307static const struct attribute_group widget_afg_group = {
    308	.attrs = widget_afg_attrs,
    309};
    310
    311static void free_widget_node(struct kobject *kobj,
    312			     const struct attribute_group *group)
    313{
    314	if (kobj) {
    315		sysfs_remove_group(kobj, group);
    316		kobject_put(kobj);
    317	}
    318}
    319
    320static void widget_tree_free(struct hdac_device *codec)
    321{
    322	struct hdac_widget_tree *tree = codec->widgets;
    323	struct kobject **p;
    324
    325	if (!tree)
    326		return;
    327	free_widget_node(tree->afg, &widget_afg_group);
    328	if (tree->nodes) {
    329		for (p = tree->nodes; *p; p++)
    330			free_widget_node(*p, &widget_node_group);
    331		kfree(tree->nodes);
    332	}
    333	kobject_put(tree->root);
    334	kfree(tree);
    335	codec->widgets = NULL;
    336}
    337
    338static int add_widget_node(struct kobject *parent, hda_nid_t nid,
    339			   const struct attribute_group *group,
    340			   struct kobject **res)
    341{
    342	struct kobject *kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
    343	int err;
    344
    345	if (!kobj)
    346		return -ENOMEM;
    347	kobject_init(kobj, &widget_ktype);
    348	err = kobject_add(kobj, parent, "%02x", nid);
    349	if (err < 0)
    350		return err;
    351	err = sysfs_create_group(kobj, group);
    352	if (err < 0) {
    353		kobject_put(kobj);
    354		return err;
    355	}
    356
    357	*res = kobj;
    358	return 0;
    359}
    360
    361static int widget_tree_create(struct hdac_device *codec)
    362{
    363	struct hdac_widget_tree *tree;
    364	int i, err;
    365	hda_nid_t nid;
    366
    367	tree = codec->widgets = kzalloc(sizeof(*tree), GFP_KERNEL);
    368	if (!tree)
    369		return -ENOMEM;
    370
    371	tree->root = kobject_create_and_add("widgets", &codec->dev.kobj);
    372	if (!tree->root)
    373		return -ENOMEM;
    374
    375	tree->nodes = kcalloc(codec->num_nodes + 1, sizeof(*tree->nodes),
    376			      GFP_KERNEL);
    377	if (!tree->nodes)
    378		return -ENOMEM;
    379
    380	for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
    381		err = add_widget_node(tree->root, nid, &widget_node_group,
    382				      &tree->nodes[i]);
    383		if (err < 0)
    384			return err;
    385	}
    386
    387	if (codec->afg) {
    388		err = add_widget_node(tree->root, codec->afg,
    389				      &widget_afg_group, &tree->afg);
    390		if (err < 0)
    391			return err;
    392	}
    393
    394	kobject_uevent(tree->root, KOBJ_CHANGE);
    395	return 0;
    396}
    397
    398/* call with codec->widget_lock held */
    399int hda_widget_sysfs_init(struct hdac_device *codec)
    400{
    401	int err;
    402
    403	if (codec->widgets)
    404		return 0; /* already created */
    405
    406	err = widget_tree_create(codec);
    407	if (err < 0) {
    408		widget_tree_free(codec);
    409		return err;
    410	}
    411
    412	return 0;
    413}
    414
    415/* call with codec->widget_lock held */
    416void hda_widget_sysfs_exit(struct hdac_device *codec)
    417{
    418	widget_tree_free(codec);
    419}
    420
    421/* call with codec->widget_lock held */
    422int hda_widget_sysfs_reinit(struct hdac_device *codec,
    423			    hda_nid_t start_nid, int num_nodes)
    424{
    425	struct hdac_widget_tree *tree;
    426	hda_nid_t end_nid = start_nid + num_nodes;
    427	hda_nid_t nid;
    428	int i;
    429
    430	if (!codec->widgets)
    431		return 0;
    432
    433	tree = kmemdup(codec->widgets, sizeof(*tree), GFP_KERNEL);
    434	if (!tree)
    435		return -ENOMEM;
    436
    437	tree->nodes = kcalloc(num_nodes + 1, sizeof(*tree->nodes), GFP_KERNEL);
    438	if (!tree->nodes) {
    439		kfree(tree);
    440		return -ENOMEM;
    441	}
    442
    443	/* prune non-existing nodes */
    444	for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
    445		if (nid < start_nid || nid >= end_nid)
    446			free_widget_node(codec->widgets->nodes[i],
    447					 &widget_node_group);
    448	}
    449
    450	/* add new nodes */
    451	for (i = 0, nid = start_nid; i < num_nodes; i++, nid++) {
    452		if (nid < codec->start_nid || nid >= codec->end_nid)
    453			add_widget_node(tree->root, nid, &widget_node_group,
    454					&tree->nodes[i]);
    455		else
    456			tree->nodes[i] =
    457				codec->widgets->nodes[nid - codec->start_nid];
    458	}
    459
    460	/* replace with the new tree */
    461	kfree(codec->widgets->nodes);
    462	kfree(codec->widgets);
    463	codec->widgets = tree;
    464
    465	kobject_uevent(tree->root, KOBJ_CHANGE);
    466	return 0;
    467}