162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * configfs.c - Implementation of configfs interface to the driver stack
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/configfs.h>
1362306a36Sopenharmony_ci#include <linux/most.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define MAX_STRING_SIZE 80
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct mdev_link {
1862306a36Sopenharmony_ci	struct config_item item;
1962306a36Sopenharmony_ci	struct list_head list;
2062306a36Sopenharmony_ci	bool create_link;
2162306a36Sopenharmony_ci	bool destroy_link;
2262306a36Sopenharmony_ci	u16 num_buffers;
2362306a36Sopenharmony_ci	u16 buffer_size;
2462306a36Sopenharmony_ci	u16 subbuffer_size;
2562306a36Sopenharmony_ci	u16 packets_per_xact;
2662306a36Sopenharmony_ci	u16 dbr_size;
2762306a36Sopenharmony_ci	char datatype[MAX_STRING_SIZE];
2862306a36Sopenharmony_ci	char direction[MAX_STRING_SIZE];
2962306a36Sopenharmony_ci	char name[MAX_STRING_SIZE];
3062306a36Sopenharmony_ci	char device[MAX_STRING_SIZE];
3162306a36Sopenharmony_ci	char channel[MAX_STRING_SIZE];
3262306a36Sopenharmony_ci	char comp[MAX_STRING_SIZE];
3362306a36Sopenharmony_ci	char comp_params[MAX_STRING_SIZE];
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic struct list_head mdev_link_list;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic int set_cfg_buffer_size(struct mdev_link *link)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	return most_set_cfg_buffer_size(link->device, link->channel,
4162306a36Sopenharmony_ci					link->buffer_size);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int set_cfg_subbuffer_size(struct mdev_link *link)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	return most_set_cfg_subbuffer_size(link->device, link->channel,
4762306a36Sopenharmony_ci					   link->subbuffer_size);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int set_cfg_dbr_size(struct mdev_link *link)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	return most_set_cfg_dbr_size(link->device, link->channel,
5362306a36Sopenharmony_ci				     link->dbr_size);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int set_cfg_num_buffers(struct mdev_link *link)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	return most_set_cfg_num_buffers(link->device, link->channel,
5962306a36Sopenharmony_ci					link->num_buffers);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic int set_cfg_packets_xact(struct mdev_link *link)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	return most_set_cfg_packets_xact(link->device, link->channel,
6562306a36Sopenharmony_ci					 link->packets_per_xact);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int set_cfg_direction(struct mdev_link *link)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	return most_set_cfg_direction(link->device, link->channel,
7162306a36Sopenharmony_ci				      link->direction);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int set_cfg_datatype(struct mdev_link *link)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	return most_set_cfg_datatype(link->device, link->channel,
7762306a36Sopenharmony_ci				     link->datatype);
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic int (*set_config_val[])(struct mdev_link *link) = {
8162306a36Sopenharmony_ci	set_cfg_buffer_size,
8262306a36Sopenharmony_ci	set_cfg_subbuffer_size,
8362306a36Sopenharmony_ci	set_cfg_dbr_size,
8462306a36Sopenharmony_ci	set_cfg_num_buffers,
8562306a36Sopenharmony_ci	set_cfg_packets_xact,
8662306a36Sopenharmony_ci	set_cfg_direction,
8762306a36Sopenharmony_ci	set_cfg_datatype,
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic struct mdev_link *to_mdev_link(struct config_item *item)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	return container_of(item, struct mdev_link, item);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int set_config_and_add_link(struct mdev_link *mdev_link)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	int i;
9862306a36Sopenharmony_ci	int ret;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(set_config_val); i++) {
10162306a36Sopenharmony_ci		ret = set_config_val[i](mdev_link);
10262306a36Sopenharmony_ci		if (ret < 0 && ret != -ENODEV) {
10362306a36Sopenharmony_ci			pr_err("Config failed\n");
10462306a36Sopenharmony_ci			return ret;
10562306a36Sopenharmony_ci		}
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return most_add_link(mdev_link->device, mdev_link->channel,
10962306a36Sopenharmony_ci			     mdev_link->comp, mdev_link->name,
11062306a36Sopenharmony_ci			     mdev_link->comp_params);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic ssize_t mdev_link_create_link_store(struct config_item *item,
11462306a36Sopenharmony_ci					   const char *page, size_t count)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct mdev_link *mdev_link = to_mdev_link(item);
11762306a36Sopenharmony_ci	bool tmp;
11862306a36Sopenharmony_ci	int ret;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	ret = kstrtobool(page, &tmp);
12162306a36Sopenharmony_ci	if (ret)
12262306a36Sopenharmony_ci		return ret;
12362306a36Sopenharmony_ci	if (!tmp)
12462306a36Sopenharmony_ci		return count;
12562306a36Sopenharmony_ci	ret = set_config_and_add_link(mdev_link);
12662306a36Sopenharmony_ci	if (ret && ret != -ENODEV)
12762306a36Sopenharmony_ci		return ret;
12862306a36Sopenharmony_ci	list_add_tail(&mdev_link->list, &mdev_link_list);
12962306a36Sopenharmony_ci	mdev_link->create_link = tmp;
13062306a36Sopenharmony_ci	mdev_link->destroy_link = false;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return count;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic ssize_t mdev_link_destroy_link_store(struct config_item *item,
13662306a36Sopenharmony_ci					    const char *page, size_t count)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct mdev_link *mdev_link = to_mdev_link(item);
13962306a36Sopenharmony_ci	bool tmp;
14062306a36Sopenharmony_ci	int ret;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	ret = kstrtobool(page, &tmp);
14362306a36Sopenharmony_ci	if (ret)
14462306a36Sopenharmony_ci		return ret;
14562306a36Sopenharmony_ci	if (!tmp)
14662306a36Sopenharmony_ci		return count;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	ret = most_remove_link(mdev_link->device, mdev_link->channel,
14962306a36Sopenharmony_ci			       mdev_link->comp);
15062306a36Sopenharmony_ci	if (ret)
15162306a36Sopenharmony_ci		return ret;
15262306a36Sopenharmony_ci	if (!list_empty(&mdev_link_list))
15362306a36Sopenharmony_ci		list_del(&mdev_link->list);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	mdev_link->destroy_link = tmp;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	return count;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic ssize_t mdev_link_direction_show(struct config_item *item, char *page)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	return snprintf(page, PAGE_SIZE, "%s\n", to_mdev_link(item)->direction);
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic ssize_t mdev_link_direction_store(struct config_item *item,
16662306a36Sopenharmony_ci					 const char *page, size_t count)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct mdev_link *mdev_link = to_mdev_link(item);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (!sysfs_streq(page, "dir_rx") && !sysfs_streq(page, "rx") &&
17162306a36Sopenharmony_ci	    !sysfs_streq(page, "dir_tx") && !sysfs_streq(page, "tx"))
17262306a36Sopenharmony_ci		return -EINVAL;
17362306a36Sopenharmony_ci	strcpy(mdev_link->direction, page);
17462306a36Sopenharmony_ci	strim(mdev_link->direction);
17562306a36Sopenharmony_ci	return count;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic ssize_t mdev_link_datatype_show(struct config_item *item, char *page)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	return snprintf(page, PAGE_SIZE, "%s\n", to_mdev_link(item)->datatype);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic ssize_t mdev_link_datatype_store(struct config_item *item,
18462306a36Sopenharmony_ci					const char *page, size_t count)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct mdev_link *mdev_link = to_mdev_link(item);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (!sysfs_streq(page, "control") && !sysfs_streq(page, "async") &&
18962306a36Sopenharmony_ci	    !sysfs_streq(page, "sync") && !sysfs_streq(page, "isoc") &&
19062306a36Sopenharmony_ci	    !sysfs_streq(page, "isoc_avp"))
19162306a36Sopenharmony_ci		return -EINVAL;
19262306a36Sopenharmony_ci	strcpy(mdev_link->datatype, page);
19362306a36Sopenharmony_ci	strim(mdev_link->datatype);
19462306a36Sopenharmony_ci	return count;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic ssize_t mdev_link_device_show(struct config_item *item, char *page)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	return snprintf(page, PAGE_SIZE, "%s\n", to_mdev_link(item)->device);
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic ssize_t mdev_link_device_store(struct config_item *item,
20362306a36Sopenharmony_ci				      const char *page, size_t count)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct mdev_link *mdev_link = to_mdev_link(item);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	strscpy(mdev_link->device, page, sizeof(mdev_link->device));
20862306a36Sopenharmony_ci	strim(mdev_link->device);
20962306a36Sopenharmony_ci	return count;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic ssize_t mdev_link_channel_show(struct config_item *item, char *page)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	return snprintf(page, PAGE_SIZE, "%s\n", to_mdev_link(item)->channel);
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic ssize_t mdev_link_channel_store(struct config_item *item,
21862306a36Sopenharmony_ci				       const char *page, size_t count)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct mdev_link *mdev_link = to_mdev_link(item);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	strscpy(mdev_link->channel, page, sizeof(mdev_link->channel));
22362306a36Sopenharmony_ci	strim(mdev_link->channel);
22462306a36Sopenharmony_ci	return count;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic ssize_t mdev_link_comp_show(struct config_item *item, char *page)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	return snprintf(page, PAGE_SIZE, "%s\n", to_mdev_link(item)->comp);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic ssize_t mdev_link_comp_store(struct config_item *item,
23362306a36Sopenharmony_ci				    const char *page, size_t count)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct mdev_link *mdev_link = to_mdev_link(item);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	strscpy(mdev_link->comp, page, sizeof(mdev_link->comp));
23862306a36Sopenharmony_ci	strim(mdev_link->comp);
23962306a36Sopenharmony_ci	return count;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic ssize_t mdev_link_comp_params_show(struct config_item *item, char *page)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	return snprintf(page, PAGE_SIZE, "%s\n",
24562306a36Sopenharmony_ci			to_mdev_link(item)->comp_params);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic ssize_t mdev_link_comp_params_store(struct config_item *item,
24962306a36Sopenharmony_ci					   const char *page, size_t count)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	struct mdev_link *mdev_link = to_mdev_link(item);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	strscpy(mdev_link->comp_params, page, sizeof(mdev_link->comp_params));
25462306a36Sopenharmony_ci	strim(mdev_link->comp_params);
25562306a36Sopenharmony_ci	return count;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic ssize_t mdev_link_num_buffers_show(struct config_item *item, char *page)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	return snprintf(page, PAGE_SIZE, "%d\n",
26162306a36Sopenharmony_ci			to_mdev_link(item)->num_buffers);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic ssize_t mdev_link_num_buffers_store(struct config_item *item,
26562306a36Sopenharmony_ci					   const char *page, size_t count)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct mdev_link *mdev_link = to_mdev_link(item);
26862306a36Sopenharmony_ci	int ret;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	ret = kstrtou16(page, 0, &mdev_link->num_buffers);
27162306a36Sopenharmony_ci	if (ret)
27262306a36Sopenharmony_ci		return ret;
27362306a36Sopenharmony_ci	return count;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic ssize_t mdev_link_buffer_size_show(struct config_item *item, char *page)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	return snprintf(page, PAGE_SIZE, "%d\n",
27962306a36Sopenharmony_ci			to_mdev_link(item)->buffer_size);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic ssize_t mdev_link_buffer_size_store(struct config_item *item,
28362306a36Sopenharmony_ci					   const char *page, size_t count)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	struct mdev_link *mdev_link = to_mdev_link(item);
28662306a36Sopenharmony_ci	int ret;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	ret = kstrtou16(page, 0, &mdev_link->buffer_size);
28962306a36Sopenharmony_ci	if (ret)
29062306a36Sopenharmony_ci		return ret;
29162306a36Sopenharmony_ci	return count;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic ssize_t mdev_link_subbuffer_size_show(struct config_item *item,
29562306a36Sopenharmony_ci					     char *page)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	return snprintf(page, PAGE_SIZE, "%d\n",
29862306a36Sopenharmony_ci			to_mdev_link(item)->subbuffer_size);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic ssize_t mdev_link_subbuffer_size_store(struct config_item *item,
30262306a36Sopenharmony_ci					      const char *page, size_t count)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct mdev_link *mdev_link = to_mdev_link(item);
30562306a36Sopenharmony_ci	int ret;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	ret = kstrtou16(page, 0, &mdev_link->subbuffer_size);
30862306a36Sopenharmony_ci	if (ret)
30962306a36Sopenharmony_ci		return ret;
31062306a36Sopenharmony_ci	return count;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic ssize_t mdev_link_packets_per_xact_show(struct config_item *item,
31462306a36Sopenharmony_ci					       char *page)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	return snprintf(page, PAGE_SIZE, "%d\n",
31762306a36Sopenharmony_ci			to_mdev_link(item)->packets_per_xact);
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic ssize_t mdev_link_packets_per_xact_store(struct config_item *item,
32162306a36Sopenharmony_ci						const char *page, size_t count)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct mdev_link *mdev_link = to_mdev_link(item);
32462306a36Sopenharmony_ci	int ret;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	ret = kstrtou16(page, 0, &mdev_link->packets_per_xact);
32762306a36Sopenharmony_ci	if (ret)
32862306a36Sopenharmony_ci		return ret;
32962306a36Sopenharmony_ci	return count;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic ssize_t mdev_link_dbr_size_show(struct config_item *item, char *page)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	return snprintf(page, PAGE_SIZE, "%d\n", to_mdev_link(item)->dbr_size);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic ssize_t mdev_link_dbr_size_store(struct config_item *item,
33862306a36Sopenharmony_ci					const char *page, size_t count)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	struct mdev_link *mdev_link = to_mdev_link(item);
34162306a36Sopenharmony_ci	int ret;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	ret = kstrtou16(page, 0, &mdev_link->dbr_size);
34462306a36Sopenharmony_ci	if (ret)
34562306a36Sopenharmony_ci		return ret;
34662306a36Sopenharmony_ci	return count;
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ciCONFIGFS_ATTR_WO(mdev_link_, create_link);
35062306a36Sopenharmony_ciCONFIGFS_ATTR_WO(mdev_link_, destroy_link);
35162306a36Sopenharmony_ciCONFIGFS_ATTR(mdev_link_, device);
35262306a36Sopenharmony_ciCONFIGFS_ATTR(mdev_link_, channel);
35362306a36Sopenharmony_ciCONFIGFS_ATTR(mdev_link_, comp);
35462306a36Sopenharmony_ciCONFIGFS_ATTR(mdev_link_, comp_params);
35562306a36Sopenharmony_ciCONFIGFS_ATTR(mdev_link_, num_buffers);
35662306a36Sopenharmony_ciCONFIGFS_ATTR(mdev_link_, buffer_size);
35762306a36Sopenharmony_ciCONFIGFS_ATTR(mdev_link_, subbuffer_size);
35862306a36Sopenharmony_ciCONFIGFS_ATTR(mdev_link_, packets_per_xact);
35962306a36Sopenharmony_ciCONFIGFS_ATTR(mdev_link_, datatype);
36062306a36Sopenharmony_ciCONFIGFS_ATTR(mdev_link_, direction);
36162306a36Sopenharmony_ciCONFIGFS_ATTR(mdev_link_, dbr_size);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic struct configfs_attribute *mdev_link_attrs[] = {
36462306a36Sopenharmony_ci	&mdev_link_attr_create_link,
36562306a36Sopenharmony_ci	&mdev_link_attr_destroy_link,
36662306a36Sopenharmony_ci	&mdev_link_attr_device,
36762306a36Sopenharmony_ci	&mdev_link_attr_channel,
36862306a36Sopenharmony_ci	&mdev_link_attr_comp,
36962306a36Sopenharmony_ci	&mdev_link_attr_comp_params,
37062306a36Sopenharmony_ci	&mdev_link_attr_num_buffers,
37162306a36Sopenharmony_ci	&mdev_link_attr_buffer_size,
37262306a36Sopenharmony_ci	&mdev_link_attr_subbuffer_size,
37362306a36Sopenharmony_ci	&mdev_link_attr_packets_per_xact,
37462306a36Sopenharmony_ci	&mdev_link_attr_datatype,
37562306a36Sopenharmony_ci	&mdev_link_attr_direction,
37662306a36Sopenharmony_ci	&mdev_link_attr_dbr_size,
37762306a36Sopenharmony_ci	NULL,
37862306a36Sopenharmony_ci};
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic void mdev_link_release(struct config_item *item)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct mdev_link *mdev_link = to_mdev_link(item);
38362306a36Sopenharmony_ci	int ret;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	if (mdev_link->destroy_link)
38662306a36Sopenharmony_ci		goto free_item;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	ret = most_remove_link(mdev_link->device, mdev_link->channel,
38962306a36Sopenharmony_ci			       mdev_link->comp);
39062306a36Sopenharmony_ci	if (ret) {
39162306a36Sopenharmony_ci		pr_err("Removing link failed.\n");
39262306a36Sopenharmony_ci		goto free_item;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (!list_empty(&mdev_link_list))
39662306a36Sopenharmony_ci		list_del(&mdev_link->list);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cifree_item:
39962306a36Sopenharmony_ci	kfree(to_mdev_link(item));
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic struct configfs_item_operations mdev_link_item_ops = {
40362306a36Sopenharmony_ci	.release		= mdev_link_release,
40462306a36Sopenharmony_ci};
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic const struct config_item_type mdev_link_type = {
40762306a36Sopenharmony_ci	.ct_item_ops	= &mdev_link_item_ops,
40862306a36Sopenharmony_ci	.ct_attrs	= mdev_link_attrs,
40962306a36Sopenharmony_ci	.ct_owner	= THIS_MODULE,
41062306a36Sopenharmony_ci};
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistruct most_common {
41362306a36Sopenharmony_ci	struct config_group group;
41462306a36Sopenharmony_ci	struct module *mod;
41562306a36Sopenharmony_ci	struct configfs_subsystem subsys;
41662306a36Sopenharmony_ci};
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic struct most_common *to_most_common(struct configfs_subsystem *subsys)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	return container_of(subsys, struct most_common, subsys);
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic struct config_item *most_common_make_item(struct config_group *group,
42462306a36Sopenharmony_ci						 const char *name)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct mdev_link *mdev_link;
42762306a36Sopenharmony_ci	struct most_common *mc = to_most_common(group->cg_subsys);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	mdev_link = kzalloc(sizeof(*mdev_link), GFP_KERNEL);
43062306a36Sopenharmony_ci	if (!mdev_link)
43162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (!try_module_get(mc->mod)) {
43462306a36Sopenharmony_ci		kfree(mdev_link);
43562306a36Sopenharmony_ci		return ERR_PTR(-ENOLCK);
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci	config_item_init_type_name(&mdev_link->item, name,
43862306a36Sopenharmony_ci				   &mdev_link_type);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (!strcmp(group->cg_item.ci_namebuf, "most_cdev"))
44162306a36Sopenharmony_ci		strcpy(mdev_link->comp, "cdev");
44262306a36Sopenharmony_ci	else if (!strcmp(group->cg_item.ci_namebuf, "most_net"))
44362306a36Sopenharmony_ci		strcpy(mdev_link->comp, "net");
44462306a36Sopenharmony_ci	else if (!strcmp(group->cg_item.ci_namebuf, "most_video"))
44562306a36Sopenharmony_ci		strcpy(mdev_link->comp, "video");
44662306a36Sopenharmony_ci	strcpy(mdev_link->name, name);
44762306a36Sopenharmony_ci	return &mdev_link->item;
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic void most_common_release(struct config_item *item)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct config_group *group = to_config_group(item);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	kfree(to_most_common(group->cg_subsys));
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic struct configfs_item_operations most_common_item_ops = {
45862306a36Sopenharmony_ci	.release	= most_common_release,
45962306a36Sopenharmony_ci};
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic void most_common_disconnect(struct config_group *group,
46262306a36Sopenharmony_ci				   struct config_item *item)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	struct most_common *mc = to_most_common(group->cg_subsys);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	module_put(mc->mod);
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic struct configfs_group_operations most_common_group_ops = {
47062306a36Sopenharmony_ci	.make_item	= most_common_make_item,
47162306a36Sopenharmony_ci	.disconnect_notify = most_common_disconnect,
47262306a36Sopenharmony_ci};
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic const struct config_item_type most_common_type = {
47562306a36Sopenharmony_ci	.ct_item_ops	= &most_common_item_ops,
47662306a36Sopenharmony_ci	.ct_group_ops	= &most_common_group_ops,
47762306a36Sopenharmony_ci	.ct_owner	= THIS_MODULE,
47862306a36Sopenharmony_ci};
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic struct most_common most_cdev = {
48162306a36Sopenharmony_ci	.subsys = {
48262306a36Sopenharmony_ci		.su_group = {
48362306a36Sopenharmony_ci			.cg_item = {
48462306a36Sopenharmony_ci				.ci_namebuf = "most_cdev",
48562306a36Sopenharmony_ci				.ci_type = &most_common_type,
48662306a36Sopenharmony_ci			},
48762306a36Sopenharmony_ci		},
48862306a36Sopenharmony_ci	},
48962306a36Sopenharmony_ci};
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic struct most_common most_net = {
49262306a36Sopenharmony_ci	.subsys = {
49362306a36Sopenharmony_ci		.su_group = {
49462306a36Sopenharmony_ci			.cg_item = {
49562306a36Sopenharmony_ci				.ci_namebuf = "most_net",
49662306a36Sopenharmony_ci				.ci_type = &most_common_type,
49762306a36Sopenharmony_ci			},
49862306a36Sopenharmony_ci		},
49962306a36Sopenharmony_ci	},
50062306a36Sopenharmony_ci};
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic struct most_common most_video = {
50362306a36Sopenharmony_ci	.subsys = {
50462306a36Sopenharmony_ci		.su_group = {
50562306a36Sopenharmony_ci			.cg_item = {
50662306a36Sopenharmony_ci				.ci_namebuf = "most_video",
50762306a36Sopenharmony_ci				.ci_type = &most_common_type,
50862306a36Sopenharmony_ci			},
50962306a36Sopenharmony_ci		},
51062306a36Sopenharmony_ci	},
51162306a36Sopenharmony_ci};
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistruct most_snd_grp {
51462306a36Sopenharmony_ci	struct config_group group;
51562306a36Sopenharmony_ci	bool create_card;
51662306a36Sopenharmony_ci	struct list_head list;
51762306a36Sopenharmony_ci};
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic struct most_snd_grp *to_most_snd_grp(struct config_item *item)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	return container_of(to_config_group(item), struct most_snd_grp, group);
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic struct config_item *most_snd_grp_make_item(struct config_group *group,
52562306a36Sopenharmony_ci						  const char *name)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct mdev_link *mdev_link;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	mdev_link = kzalloc(sizeof(*mdev_link), GFP_KERNEL);
53062306a36Sopenharmony_ci	if (!mdev_link)
53162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	config_item_init_type_name(&mdev_link->item, name, &mdev_link_type);
53462306a36Sopenharmony_ci	mdev_link->create_link = false;
53562306a36Sopenharmony_ci	strcpy(mdev_link->name, name);
53662306a36Sopenharmony_ci	strcpy(mdev_link->comp, "sound");
53762306a36Sopenharmony_ci	return &mdev_link->item;
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic ssize_t most_snd_grp_create_card_store(struct config_item *item,
54162306a36Sopenharmony_ci					      const char *page, size_t count)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	struct most_snd_grp *snd_grp = to_most_snd_grp(item);
54462306a36Sopenharmony_ci	int ret;
54562306a36Sopenharmony_ci	bool tmp;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	ret = kstrtobool(page, &tmp);
54862306a36Sopenharmony_ci	if (ret)
54962306a36Sopenharmony_ci		return ret;
55062306a36Sopenharmony_ci	if (tmp) {
55162306a36Sopenharmony_ci		ret = most_cfg_complete("sound");
55262306a36Sopenharmony_ci		if (ret)
55362306a36Sopenharmony_ci			return ret;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci	snd_grp->create_card = tmp;
55662306a36Sopenharmony_ci	return count;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ciCONFIGFS_ATTR_WO(most_snd_grp_, create_card);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic struct configfs_attribute *most_snd_grp_attrs[] = {
56262306a36Sopenharmony_ci	&most_snd_grp_attr_create_card,
56362306a36Sopenharmony_ci	NULL,
56462306a36Sopenharmony_ci};
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic void most_snd_grp_release(struct config_item *item)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct most_snd_grp *group = to_most_snd_grp(item);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	list_del(&group->list);
57162306a36Sopenharmony_ci	kfree(group);
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic struct configfs_item_operations most_snd_grp_item_ops = {
57562306a36Sopenharmony_ci	.release	= most_snd_grp_release,
57662306a36Sopenharmony_ci};
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic struct configfs_group_operations most_snd_grp_group_ops = {
57962306a36Sopenharmony_ci	.make_item	= most_snd_grp_make_item,
58062306a36Sopenharmony_ci};
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic const struct config_item_type most_snd_grp_type = {
58362306a36Sopenharmony_ci	.ct_item_ops	= &most_snd_grp_item_ops,
58462306a36Sopenharmony_ci	.ct_group_ops	= &most_snd_grp_group_ops,
58562306a36Sopenharmony_ci	.ct_attrs	= most_snd_grp_attrs,
58662306a36Sopenharmony_ci	.ct_owner	= THIS_MODULE,
58762306a36Sopenharmony_ci};
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistruct most_sound {
59062306a36Sopenharmony_ci	struct configfs_subsystem subsys;
59162306a36Sopenharmony_ci	struct list_head soundcard_list;
59262306a36Sopenharmony_ci	struct module *mod;
59362306a36Sopenharmony_ci};
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic struct config_group *most_sound_make_group(struct config_group *group,
59662306a36Sopenharmony_ci						  const char *name)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	struct most_snd_grp *most;
59962306a36Sopenharmony_ci	struct most_sound *ms = container_of(group->cg_subsys,
60062306a36Sopenharmony_ci					     struct most_sound, subsys);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	list_for_each_entry(most, &ms->soundcard_list, list) {
60362306a36Sopenharmony_ci		if (!most->create_card) {
60462306a36Sopenharmony_ci			pr_info("adapter configuration still in progress.\n");
60562306a36Sopenharmony_ci			return ERR_PTR(-EPROTO);
60662306a36Sopenharmony_ci		}
60762306a36Sopenharmony_ci	}
60862306a36Sopenharmony_ci	if (!try_module_get(ms->mod))
60962306a36Sopenharmony_ci		return ERR_PTR(-ENOLCK);
61062306a36Sopenharmony_ci	most = kzalloc(sizeof(*most), GFP_KERNEL);
61162306a36Sopenharmony_ci	if (!most) {
61262306a36Sopenharmony_ci		module_put(ms->mod);
61362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci	config_group_init_type_name(&most->group, name, &most_snd_grp_type);
61662306a36Sopenharmony_ci	list_add_tail(&most->list, &ms->soundcard_list);
61762306a36Sopenharmony_ci	return &most->group;
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic void most_sound_disconnect(struct config_group *group,
62162306a36Sopenharmony_ci				  struct config_item *item)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	struct most_sound *ms = container_of(group->cg_subsys,
62462306a36Sopenharmony_ci					     struct most_sound, subsys);
62562306a36Sopenharmony_ci	module_put(ms->mod);
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_cistatic struct configfs_group_operations most_sound_group_ops = {
62962306a36Sopenharmony_ci	.make_group	= most_sound_make_group,
63062306a36Sopenharmony_ci	.disconnect_notify = most_sound_disconnect,
63162306a36Sopenharmony_ci};
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_cistatic const struct config_item_type most_sound_type = {
63462306a36Sopenharmony_ci	.ct_group_ops	= &most_sound_group_ops,
63562306a36Sopenharmony_ci	.ct_owner	= THIS_MODULE,
63662306a36Sopenharmony_ci};
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic struct most_sound most_sound_subsys = {
63962306a36Sopenharmony_ci	.subsys = {
64062306a36Sopenharmony_ci		.su_group = {
64162306a36Sopenharmony_ci			.cg_item = {
64262306a36Sopenharmony_ci				.ci_namebuf = "most_sound",
64362306a36Sopenharmony_ci				.ci_type = &most_sound_type,
64462306a36Sopenharmony_ci			},
64562306a36Sopenharmony_ci		},
64662306a36Sopenharmony_ci	},
64762306a36Sopenharmony_ci};
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ciint most_register_configfs_subsys(struct most_component *c)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	int ret;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (!strcmp(c->name, "cdev")) {
65462306a36Sopenharmony_ci		most_cdev.mod = c->mod;
65562306a36Sopenharmony_ci		ret = configfs_register_subsystem(&most_cdev.subsys);
65662306a36Sopenharmony_ci	} else if (!strcmp(c->name, "net")) {
65762306a36Sopenharmony_ci		most_net.mod = c->mod;
65862306a36Sopenharmony_ci		ret = configfs_register_subsystem(&most_net.subsys);
65962306a36Sopenharmony_ci	} else if (!strcmp(c->name, "video")) {
66062306a36Sopenharmony_ci		most_video.mod = c->mod;
66162306a36Sopenharmony_ci		ret = configfs_register_subsystem(&most_video.subsys);
66262306a36Sopenharmony_ci	} else if (!strcmp(c->name, "sound")) {
66362306a36Sopenharmony_ci		most_sound_subsys.mod = c->mod;
66462306a36Sopenharmony_ci		ret = configfs_register_subsystem(&most_sound_subsys.subsys);
66562306a36Sopenharmony_ci	} else {
66662306a36Sopenharmony_ci		return -ENODEV;
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if (ret) {
67062306a36Sopenharmony_ci		pr_err("Error %d while registering subsystem %s\n",
67162306a36Sopenharmony_ci		       ret, c->name);
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci	return ret;
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(most_register_configfs_subsys);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_civoid most_interface_register_notify(const char *mdev)
67862306a36Sopenharmony_ci{
67962306a36Sopenharmony_ci	bool register_snd_card = false;
68062306a36Sopenharmony_ci	struct mdev_link *mdev_link;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	list_for_each_entry(mdev_link, &mdev_link_list, list) {
68362306a36Sopenharmony_ci		if (!strcmp(mdev_link->device, mdev)) {
68462306a36Sopenharmony_ci			set_config_and_add_link(mdev_link);
68562306a36Sopenharmony_ci			if (!strcmp(mdev_link->comp, "sound"))
68662306a36Sopenharmony_ci				register_snd_card = true;
68762306a36Sopenharmony_ci		}
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci	if (register_snd_card)
69062306a36Sopenharmony_ci		most_cfg_complete("sound");
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_civoid most_deregister_configfs_subsys(struct most_component *c)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	if (!strcmp(c->name, "cdev"))
69662306a36Sopenharmony_ci		configfs_unregister_subsystem(&most_cdev.subsys);
69762306a36Sopenharmony_ci	else if (!strcmp(c->name, "net"))
69862306a36Sopenharmony_ci		configfs_unregister_subsystem(&most_net.subsys);
69962306a36Sopenharmony_ci	else if (!strcmp(c->name, "video"))
70062306a36Sopenharmony_ci		configfs_unregister_subsystem(&most_video.subsys);
70162306a36Sopenharmony_ci	else if (!strcmp(c->name, "sound"))
70262306a36Sopenharmony_ci		configfs_unregister_subsystem(&most_sound_subsys.subsys);
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(most_deregister_configfs_subsys);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ciint __init configfs_init(void)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	config_group_init(&most_cdev.subsys.su_group);
70962306a36Sopenharmony_ci	mutex_init(&most_cdev.subsys.su_mutex);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	config_group_init(&most_net.subsys.su_group);
71262306a36Sopenharmony_ci	mutex_init(&most_net.subsys.su_mutex);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	config_group_init(&most_video.subsys.su_group);
71562306a36Sopenharmony_ci	mutex_init(&most_video.subsys.su_mutex);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	config_group_init(&most_sound_subsys.subsys.su_group);
71862306a36Sopenharmony_ci	mutex_init(&most_sound_subsys.subsys.su_mutex);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	INIT_LIST_HEAD(&most_sound_subsys.soundcard_list);
72162306a36Sopenharmony_ci	INIT_LIST_HEAD(&mdev_link_list);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	return 0;
72462306a36Sopenharmony_ci}
725