165f0ecb9SAlexander Usyskin // SPDX-License-Identifier: GPL-2.0 265f0ecb9SAlexander Usyskin /* Copyright (C) 2023-2026 Intel Corporation */ 365f0ecb9SAlexander Usyskin #include <linux/atomic.h> 465f0ecb9SAlexander Usyskin #include <linux/cdev.h> 5*2207e8d4SAlexander Usyskin #include <linux/cleanup.h> 665f0ecb9SAlexander Usyskin #include <linux/device.h> 765f0ecb9SAlexander Usyskin #include <linux/err.h> 865f0ecb9SAlexander Usyskin #include <linux/export.h> 965f0ecb9SAlexander Usyskin #include <linux/fs.h> 1065f0ecb9SAlexander Usyskin #include <linux/issei.h> 1165f0ecb9SAlexander Usyskin #include <linux/kobject.h> 1265f0ecb9SAlexander Usyskin #include <linux/list.h> 1365f0ecb9SAlexander Usyskin #include <linux/module.h> 1465f0ecb9SAlexander Usyskin #include <linux/mutex.h> 15*2207e8d4SAlexander Usyskin #include <linux/poll.h> 16*2207e8d4SAlexander Usyskin #include <linux/sched/signal.h> 17*2207e8d4SAlexander Usyskin #include <linux/slab.h> 1865f0ecb9SAlexander Usyskin #include <linux/types.h> 1965f0ecb9SAlexander Usyskin #include <linux/wait.h> 2065f0ecb9SAlexander Usyskin #include <linux/xarray.h> 2165f0ecb9SAlexander Usyskin 2265f0ecb9SAlexander Usyskin #include "issei_dev.h" 23*2207e8d4SAlexander Usyskin #include "host_client.h" 2465f0ecb9SAlexander Usyskin #include "cdev.h" 2565f0ecb9SAlexander Usyskin 2665f0ecb9SAlexander Usyskin struct class *issei_class; 2765f0ecb9SAlexander Usyskin static dev_t issei_devt; 2865f0ecb9SAlexander Usyskin 2965f0ecb9SAlexander Usyskin #define ISSEI_MAX_DEVS MINORMASK 3065f0ecb9SAlexander Usyskin 3165f0ecb9SAlexander Usyskin static DEFINE_XARRAY_ALLOC(issei_minor_xa); 3265f0ecb9SAlexander Usyskin 33*2207e8d4SAlexander Usyskin static int issei_open(struct inode *inode, struct file *fp) 34*2207e8d4SAlexander Usyskin { 35*2207e8d4SAlexander Usyskin struct issei_host_client *cl; 36*2207e8d4SAlexander Usyskin struct issei_device *idev; 37*2207e8d4SAlexander Usyskin 38*2207e8d4SAlexander Usyskin xa_lock(&issei_minor_xa); 39*2207e8d4SAlexander Usyskin idev = xa_load(&issei_minor_xa, iminor(inode)); 40*2207e8d4SAlexander Usyskin if (idev) 41*2207e8d4SAlexander Usyskin get_device(&idev->dev); 42*2207e8d4SAlexander Usyskin xa_unlock(&issei_minor_xa); 43*2207e8d4SAlexander Usyskin if (!idev) 44*2207e8d4SAlexander Usyskin return -ENODEV; 45*2207e8d4SAlexander Usyskin 46*2207e8d4SAlexander Usyskin cl = issei_cl_create(idev, fp); 47*2207e8d4SAlexander Usyskin if (IS_ERR(cl)) { 48*2207e8d4SAlexander Usyskin put_device(&idev->dev); 49*2207e8d4SAlexander Usyskin return PTR_ERR(cl); 50*2207e8d4SAlexander Usyskin } 51*2207e8d4SAlexander Usyskin fp->private_data = cl; 52*2207e8d4SAlexander Usyskin 53*2207e8d4SAlexander Usyskin return nonseekable_open(inode, fp); 54*2207e8d4SAlexander Usyskin } 55*2207e8d4SAlexander Usyskin 56*2207e8d4SAlexander Usyskin static int issei_release(struct inode *inode, struct file *fp) 57*2207e8d4SAlexander Usyskin { 58*2207e8d4SAlexander Usyskin struct issei_host_client *cl = fp->private_data; 59*2207e8d4SAlexander Usyskin struct issei_device *idev = cl->idev; 60*2207e8d4SAlexander Usyskin 61*2207e8d4SAlexander Usyskin issei_cl_remove(cl); 62*2207e8d4SAlexander Usyskin put_device(&idev->dev); 63*2207e8d4SAlexander Usyskin 64*2207e8d4SAlexander Usyskin return 0; 65*2207e8d4SAlexander Usyskin } 66*2207e8d4SAlexander Usyskin 67*2207e8d4SAlexander Usyskin static long issei_ioctl(struct file *file, unsigned int cmd, unsigned long data) 68*2207e8d4SAlexander Usyskin { 69*2207e8d4SAlexander Usyskin struct issei_host_client *cl = file->private_data; 70*2207e8d4SAlexander Usyskin struct issei_connect_client_data conn; 71*2207e8d4SAlexander Usyskin struct issei_device *idev = cl->idev; 72*2207e8d4SAlexander Usyskin int ret; 73*2207e8d4SAlexander Usyskin 74*2207e8d4SAlexander Usyskin switch (cmd) { 75*2207e8d4SAlexander Usyskin case IOCTL_ISSEI_CONNECT_CLIENT: 76*2207e8d4SAlexander Usyskin dev_dbg(&idev->dev, "IOCTL_ISSEI_CONNECT_CLIENT\n"); 77*2207e8d4SAlexander Usyskin 78*2207e8d4SAlexander Usyskin if (idev->rst_state != ISSEI_RST_STATE_DONE) { 79*2207e8d4SAlexander Usyskin dev_dbg(&idev->dev, "Device is in transition\n"); 80*2207e8d4SAlexander Usyskin return -ENODEV; 81*2207e8d4SAlexander Usyskin } 82*2207e8d4SAlexander Usyskin 83*2207e8d4SAlexander Usyskin if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) { 84*2207e8d4SAlexander Usyskin dev_dbg(&idev->dev, "failed to copy data from userland\n"); 85*2207e8d4SAlexander Usyskin return -EFAULT; 86*2207e8d4SAlexander Usyskin } 87*2207e8d4SAlexander Usyskin 88*2207e8d4SAlexander Usyskin ret = issei_cl_connect(cl, (uuid_t *)&conn.in_client_uuid, 89*2207e8d4SAlexander Usyskin &conn.out_client_properties.max_msg_length, 90*2207e8d4SAlexander Usyskin &conn.out_client_properties.protocol_version, 91*2207e8d4SAlexander Usyskin &conn.out_client_properties.flags); 92*2207e8d4SAlexander Usyskin if (ret) 93*2207e8d4SAlexander Usyskin return ret; 94*2207e8d4SAlexander Usyskin 95*2207e8d4SAlexander Usyskin if (copy_to_user((char __user *)data, &conn, sizeof(conn))) { 96*2207e8d4SAlexander Usyskin dev_dbg(&idev->dev, "failed to copy data to userland\n"); 97*2207e8d4SAlexander Usyskin issei_cl_disconnect(cl); 98*2207e8d4SAlexander Usyskin return -EFAULT; 99*2207e8d4SAlexander Usyskin } 100*2207e8d4SAlexander Usyskin return 0; 101*2207e8d4SAlexander Usyskin 102*2207e8d4SAlexander Usyskin case IOCTL_ISSEI_DISCONNECT_CLIENT: 103*2207e8d4SAlexander Usyskin dev_dbg(&idev->dev, "IOCTL_ISSEI_DISCONNECT_CLIENT\n"); 104*2207e8d4SAlexander Usyskin 105*2207e8d4SAlexander Usyskin if (idev->rst_state != ISSEI_RST_STATE_DONE) { 106*2207e8d4SAlexander Usyskin dev_dbg(&idev->dev, "Device is in transition\n"); 107*2207e8d4SAlexander Usyskin return -ENODEV; 108*2207e8d4SAlexander Usyskin } 109*2207e8d4SAlexander Usyskin 110*2207e8d4SAlexander Usyskin return issei_cl_disconnect(cl); 111*2207e8d4SAlexander Usyskin 112*2207e8d4SAlexander Usyskin default: 113*2207e8d4SAlexander Usyskin return -ENOIOCTLCMD; 114*2207e8d4SAlexander Usyskin } 115*2207e8d4SAlexander Usyskin } 116*2207e8d4SAlexander Usyskin 117*2207e8d4SAlexander Usyskin static ssize_t issei_write(struct file *file, const char __user *ubuf, 118*2207e8d4SAlexander Usyskin size_t length, loff_t *offset) 119*2207e8d4SAlexander Usyskin { 120*2207e8d4SAlexander Usyskin struct issei_host_client *cl = file->private_data; 121*2207e8d4SAlexander Usyskin struct issei_device *idev = cl->idev; 122*2207e8d4SAlexander Usyskin ssize_t ret; 123*2207e8d4SAlexander Usyskin 124*2207e8d4SAlexander Usyskin if (!length) 125*2207e8d4SAlexander Usyskin return 0; 126*2207e8d4SAlexander Usyskin 127*2207e8d4SAlexander Usyskin if (idev->rst_state != ISSEI_RST_STATE_DONE) { 128*2207e8d4SAlexander Usyskin dev_dbg(&idev->dev, "Device is in transition\n"); 129*2207e8d4SAlexander Usyskin return -EBUSY; 130*2207e8d4SAlexander Usyskin } 131*2207e8d4SAlexander Usyskin 132*2207e8d4SAlexander Usyskin /* sanity check */ 133*2207e8d4SAlexander Usyskin if (length > idev->dma.length.h2f) { 134*2207e8d4SAlexander Usyskin dev_dbg(&idev->dev, "Write is too big %zu > %zu\n", 135*2207e8d4SAlexander Usyskin length, idev->dma.length.h2f); 136*2207e8d4SAlexander Usyskin return -EFBIG; 137*2207e8d4SAlexander Usyskin } 138*2207e8d4SAlexander Usyskin 139*2207e8d4SAlexander Usyskin u8 *buf __free(kfree) = memdup_user(ubuf, length); 140*2207e8d4SAlexander Usyskin if (IS_ERR(buf)) { 141*2207e8d4SAlexander Usyskin dev_dbg(&idev->dev, "failed to copy data from userland\n"); 142*2207e8d4SAlexander Usyskin return PTR_ERR(buf); 143*2207e8d4SAlexander Usyskin } 144*2207e8d4SAlexander Usyskin 145*2207e8d4SAlexander Usyskin do { 146*2207e8d4SAlexander Usyskin ret = issei_cl_write(cl, buf, length); 147*2207e8d4SAlexander Usyskin if (ret < 0 && ret != -EAGAIN) 148*2207e8d4SAlexander Usyskin return ret; 149*2207e8d4SAlexander Usyskin /* buf is consumed by issei_cl_write on success */ 150*2207e8d4SAlexander Usyskin if (ret >= 0) 151*2207e8d4SAlexander Usyskin retain_and_null_ptr(buf); 152*2207e8d4SAlexander Usyskin if (wait_event_interruptible(cl->write_wait, issei_cl_check_write(cl) != 1)) { 153*2207e8d4SAlexander Usyskin issei_cl_clean_all_wbuf(cl); 154*2207e8d4SAlexander Usyskin if (signal_pending(current)) 155*2207e8d4SAlexander Usyskin return -EINTR; 156*2207e8d4SAlexander Usyskin return -ERESTARTSYS; 157*2207e8d4SAlexander Usyskin } 158*2207e8d4SAlexander Usyskin } while (ret == -EAGAIN); 159*2207e8d4SAlexander Usyskin 160*2207e8d4SAlexander Usyskin return ret; 161*2207e8d4SAlexander Usyskin } 162*2207e8d4SAlexander Usyskin 163*2207e8d4SAlexander Usyskin static ssize_t issei_read(struct file *file, char __user *ubuf, 164*2207e8d4SAlexander Usyskin size_t length, loff_t *offset) 165*2207e8d4SAlexander Usyskin { 166*2207e8d4SAlexander Usyskin struct issei_host_client *cl = file->private_data; 167*2207e8d4SAlexander Usyskin struct issei_device *idev = cl->idev; 168*2207e8d4SAlexander Usyskin u8 *data = NULL; 169*2207e8d4SAlexander Usyskin ssize_t ret; 170*2207e8d4SAlexander Usyskin 171*2207e8d4SAlexander Usyskin if (!length) 172*2207e8d4SAlexander Usyskin return 0; 173*2207e8d4SAlexander Usyskin 174*2207e8d4SAlexander Usyskin if (idev->rst_state != ISSEI_RST_STATE_DONE) { 175*2207e8d4SAlexander Usyskin dev_dbg(&idev->dev, "Device is in transition\n"); 176*2207e8d4SAlexander Usyskin return -EBUSY; 177*2207e8d4SAlexander Usyskin } 178*2207e8d4SAlexander Usyskin 179*2207e8d4SAlexander Usyskin /* sanity check */ 180*2207e8d4SAlexander Usyskin if (length > idev->dma.length.f2h) { 181*2207e8d4SAlexander Usyskin dev_dbg(&idev->dev, "Read is too big %zu > %zu\n", 182*2207e8d4SAlexander Usyskin length, idev->dma.length.f2h); 183*2207e8d4SAlexander Usyskin return -EFBIG; 184*2207e8d4SAlexander Usyskin } 185*2207e8d4SAlexander Usyskin 186*2207e8d4SAlexander Usyskin ret = issei_cl_read(cl, &data, length); 187*2207e8d4SAlexander Usyskin if (ret < 0) { 188*2207e8d4SAlexander Usyskin if (ret != -ENOENT) 189*2207e8d4SAlexander Usyskin return ret; 190*2207e8d4SAlexander Usyskin 191*2207e8d4SAlexander Usyskin if (wait_event_interruptible(cl->read_wait, issei_cl_check_read(cl) != 0)) { 192*2207e8d4SAlexander Usyskin if (signal_pending(current)) 193*2207e8d4SAlexander Usyskin return -EINTR; 194*2207e8d4SAlexander Usyskin return -ERESTARTSYS; 195*2207e8d4SAlexander Usyskin } 196*2207e8d4SAlexander Usyskin 197*2207e8d4SAlexander Usyskin ret = issei_cl_read(cl, &data, length); 198*2207e8d4SAlexander Usyskin if (ret < 0) 199*2207e8d4SAlexander Usyskin return ret; 200*2207e8d4SAlexander Usyskin } 201*2207e8d4SAlexander Usyskin 202*2207e8d4SAlexander Usyskin if (copy_to_user(ubuf, data, ret)) { 203*2207e8d4SAlexander Usyskin dev_dbg(&idev->dev, "failed to copy data to userland\n"); 204*2207e8d4SAlexander Usyskin ret = -EFAULT; 205*2207e8d4SAlexander Usyskin } else { 206*2207e8d4SAlexander Usyskin *offset = 0; 207*2207e8d4SAlexander Usyskin } 208*2207e8d4SAlexander Usyskin 209*2207e8d4SAlexander Usyskin kfree(data); 210*2207e8d4SAlexander Usyskin 211*2207e8d4SAlexander Usyskin return ret; 212*2207e8d4SAlexander Usyskin } 213*2207e8d4SAlexander Usyskin 214*2207e8d4SAlexander Usyskin static __poll_t issei_poll(struct file *file, poll_table *wait) 215*2207e8d4SAlexander Usyskin { 216*2207e8d4SAlexander Usyskin __poll_t req_events = poll_requested_events(wait); 217*2207e8d4SAlexander Usyskin struct issei_host_client *cl = file->private_data; 218*2207e8d4SAlexander Usyskin struct issei_device *idev = cl->idev; 219*2207e8d4SAlexander Usyskin __poll_t mask = 0; 220*2207e8d4SAlexander Usyskin int ret; 221*2207e8d4SAlexander Usyskin 222*2207e8d4SAlexander Usyskin if (idev->rst_state != ISSEI_RST_STATE_DONE) { 223*2207e8d4SAlexander Usyskin dev_dbg(&idev->dev, "Device is in transition\n"); 224*2207e8d4SAlexander Usyskin return EPOLLERR; 225*2207e8d4SAlexander Usyskin } 226*2207e8d4SAlexander Usyskin 227*2207e8d4SAlexander Usyskin if (req_events & (EPOLLIN | EPOLLRDNORM)) { 228*2207e8d4SAlexander Usyskin poll_wait(file, &cl->read_wait, wait); 229*2207e8d4SAlexander Usyskin ret = issei_cl_check_read(cl); 230*2207e8d4SAlexander Usyskin if (ret == 1) 231*2207e8d4SAlexander Usyskin mask |= EPOLLIN | EPOLLRDNORM; 232*2207e8d4SAlexander Usyskin else if (ret < 0) 233*2207e8d4SAlexander Usyskin mask |= EPOLLERR; 234*2207e8d4SAlexander Usyskin } 235*2207e8d4SAlexander Usyskin 236*2207e8d4SAlexander Usyskin if (req_events & (EPOLLOUT | EPOLLWRNORM)) { 237*2207e8d4SAlexander Usyskin poll_wait(file, &cl->write_wait, wait); 238*2207e8d4SAlexander Usyskin ret = issei_cl_check_write(cl); 239*2207e8d4SAlexander Usyskin if (ret == 0) 240*2207e8d4SAlexander Usyskin mask |= EPOLLOUT | EPOLLWRNORM; 241*2207e8d4SAlexander Usyskin else if (ret < 0) 242*2207e8d4SAlexander Usyskin mask |= EPOLLERR; 243*2207e8d4SAlexander Usyskin } 244*2207e8d4SAlexander Usyskin 245*2207e8d4SAlexander Usyskin return mask; 246*2207e8d4SAlexander Usyskin } 247*2207e8d4SAlexander Usyskin 24865f0ecb9SAlexander Usyskin static ssize_t fw_ver_show(struct device *device, 24965f0ecb9SAlexander Usyskin struct device_attribute *attr, char *buf) 25065f0ecb9SAlexander Usyskin { 25165f0ecb9SAlexander Usyskin struct issei_device *idev = dev_get_drvdata(device); 25265f0ecb9SAlexander Usyskin 25365f0ecb9SAlexander Usyskin return sysfs_emit(buf, "%u.%u.%u.%u\n", idev->fw_version[0], idev->fw_version[1], 25465f0ecb9SAlexander Usyskin idev->fw_version[2], idev->fw_version[3]); 25565f0ecb9SAlexander Usyskin } 25665f0ecb9SAlexander Usyskin static DEVICE_ATTR_RO(fw_ver); 25765f0ecb9SAlexander Usyskin 25865f0ecb9SAlexander Usyskin static struct attribute *issei_attrs[] = { 25965f0ecb9SAlexander Usyskin &dev_attr_fw_ver.attr, 26065f0ecb9SAlexander Usyskin NULL 26165f0ecb9SAlexander Usyskin }; 26265f0ecb9SAlexander Usyskin ATTRIBUTE_GROUPS(issei); 26365f0ecb9SAlexander Usyskin 26465f0ecb9SAlexander Usyskin static const struct file_operations issei_fops = { 26565f0ecb9SAlexander Usyskin .owner = THIS_MODULE, 266*2207e8d4SAlexander Usyskin .open = issei_open, 267*2207e8d4SAlexander Usyskin .unlocked_ioctl = issei_ioctl, 268*2207e8d4SAlexander Usyskin .compat_ioctl = compat_ptr_ioctl, 269*2207e8d4SAlexander Usyskin .write = issei_write, 270*2207e8d4SAlexander Usyskin .read = issei_read, 271*2207e8d4SAlexander Usyskin .release = issei_release, 272*2207e8d4SAlexander Usyskin .poll = issei_poll, 27365f0ecb9SAlexander Usyskin }; 27465f0ecb9SAlexander Usyskin 27565f0ecb9SAlexander Usyskin static void issei_device_release(struct device *dev) 27665f0ecb9SAlexander Usyskin { 27765f0ecb9SAlexander Usyskin kfree(dev_get_drvdata(dev)); 27865f0ecb9SAlexander Usyskin } 27965f0ecb9SAlexander Usyskin 28065f0ecb9SAlexander Usyskin static void issei_device_init(struct issei_device *idev, struct device *parent, 28165f0ecb9SAlexander Usyskin const struct issei_dma_length *dma_length, 28265f0ecb9SAlexander Usyskin const struct issei_hw_ops *ops) 28365f0ecb9SAlexander Usyskin { 28465f0ecb9SAlexander Usyskin idev->parent = parent; 28565f0ecb9SAlexander Usyskin idev->power_down = false; 28665f0ecb9SAlexander Usyskin init_waitqueue_head(&idev->wait_has_data); 28765f0ecb9SAlexander Usyskin idev->has_data = false; 28865f0ecb9SAlexander Usyskin init_waitqueue_head(&idev->wait_rst_state); 28965f0ecb9SAlexander Usyskin idev->rst_state = ISSEI_RST_STATE_INIT; 29065f0ecb9SAlexander Usyskin 29165f0ecb9SAlexander Usyskin mutex_init(&idev->client_lock); 29265f0ecb9SAlexander Usyskin INIT_LIST_HEAD(&idev->host_client_list); 29365f0ecb9SAlexander Usyskin idev->host_client_last_id = 0; 29465f0ecb9SAlexander Usyskin idev->host_client_count = 0; 29565f0ecb9SAlexander Usyskin INIT_LIST_HEAD(&idev->fw_client_list); 29665f0ecb9SAlexander Usyskin INIT_LIST_HEAD(&idev->write_queue); 29765f0ecb9SAlexander Usyskin idev->last_write_ts = 0; 29865f0ecb9SAlexander Usyskin 29965f0ecb9SAlexander Usyskin idev->dma.length = *dma_length; 30065f0ecb9SAlexander Usyskin 30165f0ecb9SAlexander Usyskin idev->ops = ops; 30265f0ecb9SAlexander Usyskin } 30365f0ecb9SAlexander Usyskin 30465f0ecb9SAlexander Usyskin /** 30565f0ecb9SAlexander Usyskin * issei_register: register issei character device 30665f0ecb9SAlexander Usyskin * @hw_size: size of the hardware structure to allocate 30765f0ecb9SAlexander Usyskin * @parent: parent device 30865f0ecb9SAlexander Usyskin * @dma_length: structure with DMA sizes 30965f0ecb9SAlexander Usyskin * @ops: hardware-related operations 31065f0ecb9SAlexander Usyskin * 31165f0ecb9SAlexander Usyskin * Return: pointer allocated to issei_device structure, error on failure 31265f0ecb9SAlexander Usyskin */ 31365f0ecb9SAlexander Usyskin struct issei_device *issei_register(size_t hw_size, struct device *parent, 31465f0ecb9SAlexander Usyskin const struct issei_dma_length *dma_length, 31565f0ecb9SAlexander Usyskin const struct issei_hw_ops *ops) 31665f0ecb9SAlexander Usyskin { 31765f0ecb9SAlexander Usyskin struct issei_device *idev; 31865f0ecb9SAlexander Usyskin u32 minor; 31965f0ecb9SAlexander Usyskin int ret, devno; 32065f0ecb9SAlexander Usyskin 32165f0ecb9SAlexander Usyskin idev = kzalloc(sizeof(*idev) + hw_size, GFP_KERNEL); 32265f0ecb9SAlexander Usyskin if (!idev) 32365f0ecb9SAlexander Usyskin return ERR_PTR(-ENOMEM); 32465f0ecb9SAlexander Usyskin 32565f0ecb9SAlexander Usyskin issei_device_init(idev, parent, dma_length, ops); 32665f0ecb9SAlexander Usyskin 32765f0ecb9SAlexander Usyskin ret = xa_alloc(&issei_minor_xa, &minor, idev, XA_LIMIT(0, ISSEI_MAX_DEVS), GFP_KERNEL); 32865f0ecb9SAlexander Usyskin if (ret < 0) { 32965f0ecb9SAlexander Usyskin dev_err(&idev->dev, "Failed to allocate minor. ret = %d\n", ret); 33065f0ecb9SAlexander Usyskin kfree(idev); 33165f0ecb9SAlexander Usyskin return ERR_PTR(ret); 33265f0ecb9SAlexander Usyskin } 33365f0ecb9SAlexander Usyskin 33465f0ecb9SAlexander Usyskin idev->minor = minor; 33565f0ecb9SAlexander Usyskin devno = MKDEV(MAJOR(issei_devt), idev->minor); 33665f0ecb9SAlexander Usyskin 33765f0ecb9SAlexander Usyskin device_initialize(&idev->dev); 33865f0ecb9SAlexander Usyskin idev->dev.devt = devno; 33965f0ecb9SAlexander Usyskin idev->dev.class = issei_class; 34065f0ecb9SAlexander Usyskin idev->dev.parent = parent; 34165f0ecb9SAlexander Usyskin idev->dev.groups = issei_groups; 34265f0ecb9SAlexander Usyskin idev->dev.release = issei_device_release; 34365f0ecb9SAlexander Usyskin dev_set_drvdata(&idev->dev, idev); 34465f0ecb9SAlexander Usyskin 34565f0ecb9SAlexander Usyskin idev->cdev = cdev_alloc(); 34665f0ecb9SAlexander Usyskin if (!idev->cdev) { 34765f0ecb9SAlexander Usyskin ret = -ENOMEM; 34865f0ecb9SAlexander Usyskin goto err; 34965f0ecb9SAlexander Usyskin } 35065f0ecb9SAlexander Usyskin idev->cdev->ops = &issei_fops; 35165f0ecb9SAlexander Usyskin if (parent->driver) 35265f0ecb9SAlexander Usyskin idev->cdev->owner = parent->driver->owner; 35365f0ecb9SAlexander Usyskin cdev_set_parent(idev->cdev, &idev->dev.kobj); 35465f0ecb9SAlexander Usyskin 35565f0ecb9SAlexander Usyskin ret = cdev_add(idev->cdev, devno, 1); 35665f0ecb9SAlexander Usyskin if (ret) { 35765f0ecb9SAlexander Usyskin dev_err(parent, "unable to add device %d:%u ret = %d\n", 35865f0ecb9SAlexander Usyskin MAJOR(issei_devt), idev->minor, ret); 35965f0ecb9SAlexander Usyskin goto err_del_cdev; 36065f0ecb9SAlexander Usyskin } 36165f0ecb9SAlexander Usyskin 36265f0ecb9SAlexander Usyskin ret = dev_set_name(&idev->dev, "issei%u", idev->minor); 36365f0ecb9SAlexander Usyskin if (ret) { 36465f0ecb9SAlexander Usyskin dev_err(parent, "unable to set name to device %d:%u ret = %d\n", 36565f0ecb9SAlexander Usyskin MAJOR(issei_devt), idev->minor, ret); 36665f0ecb9SAlexander Usyskin goto err_del_cdev; 36765f0ecb9SAlexander Usyskin } 36865f0ecb9SAlexander Usyskin 36965f0ecb9SAlexander Usyskin ret = device_add(&idev->dev); 37065f0ecb9SAlexander Usyskin if (ret) { 37165f0ecb9SAlexander Usyskin dev_err(parent, "unable to add device %d:%u ret = %d\n", 37265f0ecb9SAlexander Usyskin MAJOR(issei_devt), idev->minor, ret); 37365f0ecb9SAlexander Usyskin goto err_del_cdev; 37465f0ecb9SAlexander Usyskin } 37565f0ecb9SAlexander Usyskin 37665f0ecb9SAlexander Usyskin idev->fw_clients = kset_create_and_add("fw_clients", NULL, &idev->dev.kobj); 37765f0ecb9SAlexander Usyskin if (!idev->fw_clients) { 37865f0ecb9SAlexander Usyskin ret = -ENOMEM; 37965f0ecb9SAlexander Usyskin goto err_del_dev; 38065f0ecb9SAlexander Usyskin } 38165f0ecb9SAlexander Usyskin 38265f0ecb9SAlexander Usyskin return idev; 38365f0ecb9SAlexander Usyskin 38465f0ecb9SAlexander Usyskin err_del_dev: 38565f0ecb9SAlexander Usyskin device_del(&idev->dev); 38665f0ecb9SAlexander Usyskin err_del_cdev: 38765f0ecb9SAlexander Usyskin cdev_del(idev->cdev); 38865f0ecb9SAlexander Usyskin err: 38965f0ecb9SAlexander Usyskin put_device(&idev->dev); 39065f0ecb9SAlexander Usyskin xa_erase(&issei_minor_xa, minor); 39165f0ecb9SAlexander Usyskin 39265f0ecb9SAlexander Usyskin return ERR_PTR(ret); 39365f0ecb9SAlexander Usyskin } 39465f0ecb9SAlexander Usyskin EXPORT_SYMBOL_GPL(issei_register); 39565f0ecb9SAlexander Usyskin 39665f0ecb9SAlexander Usyskin /** 39765f0ecb9SAlexander Usyskin * issei_deregister: remove issei character device 39865f0ecb9SAlexander Usyskin * @idev: the device structure 39965f0ecb9SAlexander Usyskin */ 40065f0ecb9SAlexander Usyskin void issei_deregister(struct issei_device *idev) 40165f0ecb9SAlexander Usyskin { 40265f0ecb9SAlexander Usyskin u32 minor = idev->minor; 40365f0ecb9SAlexander Usyskin 40465f0ecb9SAlexander Usyskin cdev_del(idev->cdev); 40565f0ecb9SAlexander Usyskin 40665f0ecb9SAlexander Usyskin kset_unregister(idev->fw_clients); 40765f0ecb9SAlexander Usyskin 40865f0ecb9SAlexander Usyskin device_del(&idev->dev); 40965f0ecb9SAlexander Usyskin 41065f0ecb9SAlexander Usyskin put_device(&idev->dev); 41165f0ecb9SAlexander Usyskin 41265f0ecb9SAlexander Usyskin xa_erase(&issei_minor_xa, minor); 41365f0ecb9SAlexander Usyskin } 41465f0ecb9SAlexander Usyskin EXPORT_SYMBOL_GPL(issei_deregister); 41565f0ecb9SAlexander Usyskin 41665f0ecb9SAlexander Usyskin static int __init issei_cdev_init(void) 41765f0ecb9SAlexander Usyskin { 41865f0ecb9SAlexander Usyskin int ret; 41965f0ecb9SAlexander Usyskin 42065f0ecb9SAlexander Usyskin issei_class = class_create("issei"); 42165f0ecb9SAlexander Usyskin if (IS_ERR(issei_class)) { 42265f0ecb9SAlexander Usyskin pr_err("couldn't create class\n"); 42365f0ecb9SAlexander Usyskin return PTR_ERR(issei_class); 42465f0ecb9SAlexander Usyskin } 42565f0ecb9SAlexander Usyskin 42665f0ecb9SAlexander Usyskin ret = alloc_chrdev_region(&issei_devt, 0, ISSEI_MAX_DEVS, "issei"); 42765f0ecb9SAlexander Usyskin if (ret < 0) { 42865f0ecb9SAlexander Usyskin pr_err("unable to allocate char dev region\n"); 42965f0ecb9SAlexander Usyskin class_destroy(issei_class); 43065f0ecb9SAlexander Usyskin return ret; 43165f0ecb9SAlexander Usyskin } 43265f0ecb9SAlexander Usyskin 43365f0ecb9SAlexander Usyskin return 0; 43465f0ecb9SAlexander Usyskin } 43565f0ecb9SAlexander Usyskin 43665f0ecb9SAlexander Usyskin static void __exit issei_cdev_exit(void) 43765f0ecb9SAlexander Usyskin { 43865f0ecb9SAlexander Usyskin unregister_chrdev_region(issei_devt, ISSEI_MAX_DEVS); 43965f0ecb9SAlexander Usyskin class_destroy(issei_class); 44065f0ecb9SAlexander Usyskin } 44165f0ecb9SAlexander Usyskin 44265f0ecb9SAlexander Usyskin module_init(issei_cdev_init); 44365f0ecb9SAlexander Usyskin module_exit(issei_cdev_exit); 44465f0ecb9SAlexander Usyskin 44565f0ecb9SAlexander Usyskin MODULE_DESCRIPTION("Intel(R) Silicon Security Engine Interface"); 44665f0ecb9SAlexander Usyskin MODULE_LICENSE("GPL"); 447