xref: /linux/drivers/misc/issei/fw_client.c (revision 93e4b3076b5f2d853462b9777d083c77fc0b7b23)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) 2023-2026 Intel Corporation */
3 #include <linux/bug.h>
4 #include <linux/cleanup.h>
5 #include <linux/container_of.h>
6 #include <linux/device.h>
7 #include <linux/dev_printk.h>
8 #include <linux/kobject.h>
9 #include <linux/list.h>
10 #include <linux/slab.h>
11 #include <linux/sprintf.h>
12 #include <linux/string.h>
13 #include <linux/types.h>
14 #include <linux/uuid.h>
15 
16 #include "issei_dev.h"
17 #include "fw_client.h"
18 
19 /*
20  * Specific attribute handlers for fw_clients kset.
21  * Provide only show function as all fw_client attributes are read-only.
22  */
23 
24 struct issei_fw_cl_attr {
25 	struct attribute attr;
26 	ssize_t (*show)(struct issei_fw_client *fw_cl, const struct issei_fw_cl_attr *attr,
27 			char *buf);
28 };
29 #define to_issei_fw_cl_attr(x) container_of_const(x, struct issei_fw_cl_attr, attr)
30 
31 static ssize_t fw_cl_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
32 {
33 	const struct issei_fw_cl_attr *issei_attr;
34 	struct issei_fw_client *fw_cl;
35 
36 	issei_attr = to_issei_fw_cl_attr(attr);
37 	fw_cl = to_issei_fw_client(kobj);
38 
39 	if (!issei_attr->show)
40 		return -EIO;
41 
42 	return issei_attr->show(fw_cl, issei_attr, buf);
43 }
44 
45 static const struct sysfs_ops fw_cl_sysfs_ops = {
46 	.show = fw_cl_attr_show,
47 };
48 
49 #define FW_CL_ATTR_RO(_name) \
50 	struct issei_fw_cl_attr fw_cl_attr_##_name = __ATTR_RO(_name)
51 
52 /* fw_client attributes */
53 
54 static ssize_t id_show(struct issei_fw_client *fw_cl,
55 		       const struct issei_fw_cl_attr *attr, char *buf)
56 {
57 	return sysfs_emit(buf, "%u\n", fw_cl->id);
58 }
59 static FW_CL_ATTR_RO(id);
60 
61 static ssize_t ver_show(struct issei_fw_client *fw_cl,
62 			const struct issei_fw_cl_attr *attr, char *buf)
63 {
64 	return sysfs_emit(buf, "%u\n", fw_cl->ver);
65 }
66 static FW_CL_ATTR_RO(ver);
67 
68 static ssize_t uuid_show(struct issei_fw_client *fw_cl,
69 			 const struct issei_fw_cl_attr *attr, char *buf)
70 {
71 	return sysfs_emit(buf, "%pUb\n", &fw_cl->uuid);
72 }
73 static FW_CL_ATTR_RO(uuid);
74 
75 static ssize_t mtu_show(struct issei_fw_client *fw_cl,
76 			const struct issei_fw_cl_attr *attr, char *buf)
77 {
78 	return sysfs_emit(buf, "%u\n", fw_cl->mtu);
79 }
80 static FW_CL_ATTR_RO(mtu);
81 
82 static const struct attribute *const fw_cl_attrs[] = {
83 	&fw_cl_attr_id.attr,
84 	&fw_cl_attr_ver.attr,
85 	&fw_cl_attr_uuid.attr,
86 	&fw_cl_attr_mtu.attr,
87 	NULL,
88 };
89 
90 static const struct attribute_group fw_cl_group = {
91 	.attrs_const = fw_cl_attrs,
92 };
93 __ATTRIBUTE_GROUPS(fw_cl);
94 
95 static void issei_fw_cl_init(struct issei_fw_client *fw_cl, u16 id, u8 ver, const uuid_t *uuid,
96 			     u32 mtu, u32 flags)
97 {
98 	INIT_LIST_HEAD(&fw_cl->list);
99 	fw_cl->id = id;
100 	fw_cl->ver = ver;
101 	fw_cl->uuid = *uuid;
102 	fw_cl->mtu = mtu;
103 	fw_cl->flags = flags;
104 }
105 
106 static void fw_cl_release(struct kobject *kobj)
107 {
108 	struct issei_fw_client *fw_cl = to_issei_fw_client(kobj);
109 
110 	kfree(fw_cl);
111 }
112 
113 static const struct kobj_type fw_client_ktype = {
114 	.sysfs_ops = &fw_cl_sysfs_ops,
115 	.release = fw_cl_release,
116 	.default_groups = fw_cl_groups,
117 };
118 
119 /**
120  * issei_fw_cl_create - create firmware client object and add to list
121  * @idev: issei device object
122  * @id: firmware client id
123  * @ver: firmware client version
124  * @uuid: firmware client unique id
125  * @mtu: firmware client maximum message size
126  * @flags: firmware client flags
127  *
128  * Should be called under idev->client_lock
129  *
130  * Return: pointer to newly created object on success, ERR_PTR on failure
131  */
132 struct issei_fw_client *issei_fw_cl_create(struct issei_device *idev, u16 id, u8 ver,
133 					   const uuid_t *uuid, u32 mtu, u32 flags)
134 {
135 	int ret;
136 	struct issei_fw_client *fw_cl = kzalloc_obj(*fw_cl);
137 
138 	if (!fw_cl)
139 		return ERR_PTR(-ENOMEM);
140 
141 	WARN_ON(!mutex_is_locked(&idev->client_lock));
142 
143 	issei_fw_cl_init(fw_cl, id, ver, uuid, mtu, flags);
144 	fw_cl->kobj.kset = idev->fw_clients;
145 
146 	ret = kobject_init_and_add(&fw_cl->kobj, &fw_client_ktype, NULL, "%u", id);
147 	if (ret) {
148 		kobject_put(&fw_cl->kobj);
149 		return ERR_PTR(ret);
150 	}
151 
152 	list_add_tail(&fw_cl->list, &idev->fw_client_list);
153 
154 	dev_dbg(&idev->dev, "FW client %pUb created\n", uuid);
155 
156 	kobject_uevent(&fw_cl->kobj, KOBJ_ADD);
157 
158 	return fw_cl;
159 }
160 
161 static void __issei_fw_cl_remove(struct issei_device *idev, struct issei_fw_client *fw_cl)
162 {
163 	WARN(fw_cl->cl, "Removing connected client!\n");
164 
165 	dev_dbg(&idev->dev, "FW client %pUb will be removed\n", &fw_cl->uuid);
166 
167 	list_del(&fw_cl->list);
168 	kobject_put(&fw_cl->kobj);
169 }
170 
171 /**
172  * issei_fw_cl_remove_all - remove all firmware client objects
173  * @idev: issei device object
174  */
175 void issei_fw_cl_remove_all(struct issei_device *idev)
176 {
177 	struct issei_fw_client *fw_cl, *next;
178 
179 	guard(mutex)(&idev->client_lock);
180 
181 	list_for_each_entry_safe(fw_cl, next, &idev->fw_client_list, list)
182 		__issei_fw_cl_remove(idev, fw_cl);
183 }
184 
185 /**
186  * issei_fw_cl_find_by_uuid - find firmware client by uuid
187  * @idev: issei device object
188  * @uuid: uuid to search by it
189  *
190  * Should be called under idev->client_lock
191  *
192  * Return: pointer to firmware client object if found, NULL on failure
193  */
194 struct issei_fw_client *issei_fw_cl_find_by_uuid(struct issei_device *idev, const uuid_t *uuid)
195 {
196 	struct issei_fw_client *fw_cl;
197 
198 	WARN_ON(!mutex_is_locked(&idev->client_lock));
199 
200 	list_for_each_entry(fw_cl, &idev->fw_client_list, list) {
201 		if (uuid_equal(&fw_cl->uuid, uuid)) {
202 			kobject_get(&fw_cl->kobj);
203 			return fw_cl;
204 		}
205 	}
206 	return NULL;
207 }
208 
209 /**
210  * issei_fw_cl_connect - connect firmware and host client
211  * @fw_cl: firmware client
212  * @cl: host client
213  *
214  * Should be called under idev->client_lock
215  *
216  * Return: 0 on success, -EBUSY if already connected
217  */
218 int issei_fw_cl_connect(struct issei_fw_client *fw_cl, struct issei_host_client *cl)
219 {
220 	if (fw_cl->cl)
221 		return -EBUSY;
222 
223 	kobject_get(&fw_cl->kobj);
224 	fw_cl->cl = cl;
225 	return 0;
226 }
227 
228 /**
229  * issei_fw_cl_disconnect - disconnect firmware and host client
230  * @fw_cl: firmware client
231  *
232  * Should be called under idev->client_lock
233  */
234 void issei_fw_cl_disconnect(struct issei_fw_client *fw_cl)
235 {
236 	WARN_ON(!fw_cl->cl);
237 
238 	fw_cl->cl = NULL;
239 	kobject_put(&fw_cl->kobj);
240 }
241