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