xref: /linux/drivers/misc/issei/cdev.c (revision 93e4b3076b5f2d853462b9777d083c77fc0b7b23)
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