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