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