xref: /openbmc/linux/drivers/most/most_cdev.c (revision 1aaba11d)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * cdev.c - Character device component for Mostcore
4  *
5  * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG
6  */
7 
8 #include <linux/module.h>
9 #include <linux/sched.h>
10 #include <linux/fs.h>
11 #include <linux/slab.h>
12 #include <linux/device.h>
13 #include <linux/cdev.h>
14 #include <linux/poll.h>
15 #include <linux/kfifo.h>
16 #include <linux/uaccess.h>
17 #include <linux/idr.h>
18 #include <linux/most.h>
19 
20 #define CHRDEV_REGION_SIZE 50
21 
22 static struct cdev_component {
23 	dev_t devno;
24 	struct ida minor_id;
25 	unsigned int major;
26 	struct class *class;
27 	struct most_component cc;
28 } comp;
29 
30 struct comp_channel {
31 	wait_queue_head_t wq;
32 	spinlock_t unlink;	/* synchronization lock to unlink channels */
33 	struct cdev cdev;
34 	struct device *dev;
35 	struct mutex io_mutex;
36 	struct most_interface *iface;
37 	struct most_channel_config *cfg;
38 	unsigned int channel_id;
39 	dev_t devno;
40 	size_t mbo_offs;
41 	DECLARE_KFIFO_PTR(fifo, typeof(struct mbo *));
42 	int access_ref;
43 	struct list_head list;
44 };
45 
46 #define to_channel(d) container_of(d, struct comp_channel, cdev)
47 static LIST_HEAD(channel_list);
48 static DEFINE_SPINLOCK(ch_list_lock);
49 
ch_has_mbo(struct comp_channel * c)50 static inline bool ch_has_mbo(struct comp_channel *c)
51 {
52 	return channel_has_mbo(c->iface, c->channel_id, &comp.cc) > 0;
53 }
54 
ch_get_mbo(struct comp_channel * c,struct mbo ** mbo)55 static inline struct mbo *ch_get_mbo(struct comp_channel *c, struct mbo **mbo)
56 {
57 	if (!kfifo_peek(&c->fifo, mbo)) {
58 		*mbo = most_get_mbo(c->iface, c->channel_id, &comp.cc);
59 		if (*mbo)
60 			kfifo_in(&c->fifo, mbo, 1);
61 	}
62 	return *mbo;
63 }
64 
get_channel(struct most_interface * iface,int id)65 static struct comp_channel *get_channel(struct most_interface *iface, int id)
66 {
67 	struct comp_channel *c, *tmp;
68 	unsigned long flags;
69 
70 	spin_lock_irqsave(&ch_list_lock, flags);
71 	list_for_each_entry_safe(c, tmp, &channel_list, list) {
72 		if ((c->iface == iface) && (c->channel_id == id)) {
73 			spin_unlock_irqrestore(&ch_list_lock, flags);
74 			return c;
75 		}
76 	}
77 	spin_unlock_irqrestore(&ch_list_lock, flags);
78 	return NULL;
79 }
80 
stop_channel(struct comp_channel * c)81 static void stop_channel(struct comp_channel *c)
82 {
83 	struct mbo *mbo;
84 
85 	while (kfifo_out((struct kfifo *)&c->fifo, &mbo, 1))
86 		most_put_mbo(mbo);
87 	most_stop_channel(c->iface, c->channel_id, &comp.cc);
88 }
89 
destroy_cdev(struct comp_channel * c)90 static void destroy_cdev(struct comp_channel *c)
91 {
92 	unsigned long flags;
93 
94 	device_destroy(comp.class, c->devno);
95 	cdev_del(&c->cdev);
96 	spin_lock_irqsave(&ch_list_lock, flags);
97 	list_del(&c->list);
98 	spin_unlock_irqrestore(&ch_list_lock, flags);
99 }
100 
destroy_channel(struct comp_channel * c)101 static void destroy_channel(struct comp_channel *c)
102 {
103 	ida_simple_remove(&comp.minor_id, MINOR(c->devno));
104 	kfifo_free(&c->fifo);
105 	kfree(c);
106 }
107 
108 /**
109  * comp_open - implements the syscall to open the device
110  * @inode: inode pointer
111  * @filp: file pointer
112  *
113  * This stores the channel pointer in the private data field of
114  * the file structure and activates the channel within the core.
115  */
comp_open(struct inode * inode,struct file * filp)116 static int comp_open(struct inode *inode, struct file *filp)
117 {
118 	struct comp_channel *c;
119 	int ret;
120 
121 	c = to_channel(inode->i_cdev);
122 	filp->private_data = c;
123 
124 	if (((c->cfg->direction == MOST_CH_RX) &&
125 	     ((filp->f_flags & O_ACCMODE) != O_RDONLY)) ||
126 	     ((c->cfg->direction == MOST_CH_TX) &&
127 		((filp->f_flags & O_ACCMODE) != O_WRONLY))) {
128 		return -EACCES;
129 	}
130 
131 	mutex_lock(&c->io_mutex);
132 	if (!c->dev) {
133 		mutex_unlock(&c->io_mutex);
134 		return -ENODEV;
135 	}
136 
137 	if (c->access_ref) {
138 		mutex_unlock(&c->io_mutex);
139 		return -EBUSY;
140 	}
141 
142 	c->mbo_offs = 0;
143 	ret = most_start_channel(c->iface, c->channel_id, &comp.cc);
144 	if (!ret)
145 		c->access_ref = 1;
146 	mutex_unlock(&c->io_mutex);
147 	return ret;
148 }
149 
150 /**
151  * comp_close - implements the syscall to close the device
152  * @inode: inode pointer
153  * @filp: file pointer
154  *
155  * This stops the channel within the core.
156  */
comp_close(struct inode * inode,struct file * filp)157 static int comp_close(struct inode *inode, struct file *filp)
158 {
159 	struct comp_channel *c = to_channel(inode->i_cdev);
160 
161 	mutex_lock(&c->io_mutex);
162 	spin_lock(&c->unlink);
163 	c->access_ref = 0;
164 	spin_unlock(&c->unlink);
165 	if (c->dev) {
166 		stop_channel(c);
167 		mutex_unlock(&c->io_mutex);
168 	} else {
169 		mutex_unlock(&c->io_mutex);
170 		destroy_channel(c);
171 	}
172 	return 0;
173 }
174 
175 /**
176  * comp_write - implements the syscall to write to the device
177  * @filp: file pointer
178  * @buf: pointer to user buffer
179  * @count: number of bytes to write
180  * @offset: offset from where to start writing
181  */
comp_write(struct file * filp,const char __user * buf,size_t count,loff_t * offset)182 static ssize_t comp_write(struct file *filp, const char __user *buf,
183 			  size_t count, loff_t *offset)
184 {
185 	int ret;
186 	size_t to_copy, left;
187 	struct mbo *mbo = NULL;
188 	struct comp_channel *c = filp->private_data;
189 
190 	mutex_lock(&c->io_mutex);
191 	while (c->dev && !ch_get_mbo(c, &mbo)) {
192 		mutex_unlock(&c->io_mutex);
193 
194 		if ((filp->f_flags & O_NONBLOCK))
195 			return -EAGAIN;
196 		if (wait_event_interruptible(c->wq, ch_has_mbo(c) || !c->dev))
197 			return -ERESTARTSYS;
198 		mutex_lock(&c->io_mutex);
199 	}
200 
201 	if (unlikely(!c->dev)) {
202 		ret = -ENODEV;
203 		goto unlock;
204 	}
205 
206 	to_copy = min(count, c->cfg->buffer_size - c->mbo_offs);
207 	left = copy_from_user(mbo->virt_address + c->mbo_offs, buf, to_copy);
208 	if (left == to_copy) {
209 		ret = -EFAULT;
210 		goto unlock;
211 	}
212 
213 	c->mbo_offs += to_copy - left;
214 	if (c->mbo_offs >= c->cfg->buffer_size ||
215 	    c->cfg->data_type == MOST_CH_CONTROL ||
216 	    c->cfg->data_type == MOST_CH_ASYNC) {
217 		kfifo_skip(&c->fifo);
218 		mbo->buffer_length = c->mbo_offs;
219 		c->mbo_offs = 0;
220 		most_submit_mbo(mbo);
221 	}
222 
223 	ret = to_copy - left;
224 unlock:
225 	mutex_unlock(&c->io_mutex);
226 	return ret;
227 }
228 
229 /**
230  * comp_read - implements the syscall to read from the device
231  * @filp: file pointer
232  * @buf: pointer to user buffer
233  * @count: number of bytes to read
234  * @offset: offset from where to start reading
235  */
236 static ssize_t
comp_read(struct file * filp,char __user * buf,size_t count,loff_t * offset)237 comp_read(struct file *filp, char __user *buf, size_t count, loff_t *offset)
238 {
239 	size_t to_copy, not_copied, copied;
240 	struct mbo *mbo = NULL;
241 	struct comp_channel *c = filp->private_data;
242 
243 	mutex_lock(&c->io_mutex);
244 	while (c->dev && !kfifo_peek(&c->fifo, &mbo)) {
245 		mutex_unlock(&c->io_mutex);
246 		if (filp->f_flags & O_NONBLOCK)
247 			return -EAGAIN;
248 		if (wait_event_interruptible(c->wq,
249 					     (!kfifo_is_empty(&c->fifo) ||
250 					      (!c->dev))))
251 			return -ERESTARTSYS;
252 		mutex_lock(&c->io_mutex);
253 	}
254 
255 	/* make sure we don't submit to gone devices */
256 	if (unlikely(!c->dev)) {
257 		mutex_unlock(&c->io_mutex);
258 		return -ENODEV;
259 	}
260 
261 	to_copy = min_t(size_t,
262 			count,
263 			mbo->processed_length - c->mbo_offs);
264 
265 	not_copied = copy_to_user(buf,
266 				  mbo->virt_address + c->mbo_offs,
267 				  to_copy);
268 
269 	copied = to_copy - not_copied;
270 
271 	c->mbo_offs += copied;
272 	if (c->mbo_offs >= mbo->processed_length) {
273 		kfifo_skip(&c->fifo);
274 		most_put_mbo(mbo);
275 		c->mbo_offs = 0;
276 	}
277 	mutex_unlock(&c->io_mutex);
278 	return copied;
279 }
280 
comp_poll(struct file * filp,poll_table * wait)281 static __poll_t comp_poll(struct file *filp, poll_table *wait)
282 {
283 	struct comp_channel *c = filp->private_data;
284 	__poll_t mask = 0;
285 
286 	poll_wait(filp, &c->wq, wait);
287 
288 	mutex_lock(&c->io_mutex);
289 	if (c->cfg->direction == MOST_CH_RX) {
290 		if (!c->dev || !kfifo_is_empty(&c->fifo))
291 			mask |= EPOLLIN | EPOLLRDNORM;
292 	} else {
293 		if (!c->dev || !kfifo_is_empty(&c->fifo) || ch_has_mbo(c))
294 			mask |= EPOLLOUT | EPOLLWRNORM;
295 	}
296 	mutex_unlock(&c->io_mutex);
297 	return mask;
298 }
299 
300 /*
301  * Initialization of struct file_operations
302  */
303 static const struct file_operations channel_fops = {
304 	.owner = THIS_MODULE,
305 	.read = comp_read,
306 	.write = comp_write,
307 	.open = comp_open,
308 	.release = comp_close,
309 	.poll = comp_poll,
310 };
311 
312 /**
313  * comp_disconnect_channel - disconnect a channel
314  * @iface: pointer to interface instance
315  * @channel_id: channel index
316  *
317  * This frees allocated memory and removes the cdev that represents this
318  * channel in user space.
319  */
comp_disconnect_channel(struct most_interface * iface,int channel_id)320 static int comp_disconnect_channel(struct most_interface *iface, int channel_id)
321 {
322 	struct comp_channel *c;
323 
324 	c = get_channel(iface, channel_id);
325 	if (!c)
326 		return -EINVAL;
327 
328 	mutex_lock(&c->io_mutex);
329 	spin_lock(&c->unlink);
330 	c->dev = NULL;
331 	spin_unlock(&c->unlink);
332 	destroy_cdev(c);
333 	if (c->access_ref) {
334 		stop_channel(c);
335 		wake_up_interruptible(&c->wq);
336 		mutex_unlock(&c->io_mutex);
337 	} else {
338 		mutex_unlock(&c->io_mutex);
339 		destroy_channel(c);
340 	}
341 	return 0;
342 }
343 
344 /**
345  * comp_rx_completion - completion handler for rx channels
346  * @mbo: pointer to buffer object that has completed
347  *
348  * This searches for the channel linked to this MBO and stores it in the local
349  * fifo buffer.
350  */
comp_rx_completion(struct mbo * mbo)351 static int comp_rx_completion(struct mbo *mbo)
352 {
353 	struct comp_channel *c;
354 
355 	if (!mbo)
356 		return -EINVAL;
357 
358 	c = get_channel(mbo->ifp, mbo->hdm_channel_id);
359 	if (!c)
360 		return -EINVAL;
361 
362 	spin_lock(&c->unlink);
363 	if (!c->access_ref || !c->dev) {
364 		spin_unlock(&c->unlink);
365 		return -ENODEV;
366 	}
367 	kfifo_in(&c->fifo, &mbo, 1);
368 	spin_unlock(&c->unlink);
369 #ifdef DEBUG_MESG
370 	if (kfifo_is_full(&c->fifo))
371 		dev_warn(c->dev, "Fifo is full\n");
372 #endif
373 	wake_up_interruptible(&c->wq);
374 	return 0;
375 }
376 
377 /**
378  * comp_tx_completion - completion handler for tx channels
379  * @iface: pointer to interface instance
380  * @channel_id: channel index/ID
381  *
382  * This wakes sleeping processes in the wait-queue.
383  */
comp_tx_completion(struct most_interface * iface,int channel_id)384 static int comp_tx_completion(struct most_interface *iface, int channel_id)
385 {
386 	struct comp_channel *c;
387 
388 	c = get_channel(iface, channel_id);
389 	if (!c)
390 		return -EINVAL;
391 
392 	if ((channel_id < 0) || (channel_id >= iface->num_channels)) {
393 		dev_warn(c->dev, "Channel ID out of range\n");
394 		return -EINVAL;
395 	}
396 
397 	wake_up_interruptible(&c->wq);
398 	return 0;
399 }
400 
401 /**
402  * comp_probe - probe function of the driver module
403  * @iface: pointer to interface instance
404  * @channel_id: channel index/ID
405  * @cfg: pointer to actual channel configuration
406  * @name: name of the device to be created
407  * @args: pointer to array of component parameters (from configfs)
408  *
409  * This allocates a channel object and creates the device node in /dev
410  *
411  * Returns 0 on success or error code otherwise.
412  */
comp_probe(struct most_interface * iface,int channel_id,struct most_channel_config * cfg,char * name,char * args)413 static int comp_probe(struct most_interface *iface, int channel_id,
414 		      struct most_channel_config *cfg, char *name, char *args)
415 {
416 	struct comp_channel *c;
417 	unsigned long cl_flags;
418 	int retval;
419 	int current_minor;
420 
421 	if (!cfg || !name)
422 		return -EINVAL;
423 
424 	c = get_channel(iface, channel_id);
425 	if (c)
426 		return -EEXIST;
427 
428 	current_minor = ida_simple_get(&comp.minor_id, 0, 0, GFP_KERNEL);
429 	if (current_minor < 0)
430 		return current_minor;
431 
432 	c = kzalloc(sizeof(*c), GFP_KERNEL);
433 	if (!c) {
434 		retval = -ENOMEM;
435 		goto err_remove_ida;
436 	}
437 
438 	c->devno = MKDEV(comp.major, current_minor);
439 	cdev_init(&c->cdev, &channel_fops);
440 	c->cdev.owner = THIS_MODULE;
441 	retval = cdev_add(&c->cdev, c->devno, 1);
442 	if (retval < 0)
443 		goto err_free_c;
444 	c->iface = iface;
445 	c->cfg = cfg;
446 	c->channel_id = channel_id;
447 	c->access_ref = 0;
448 	spin_lock_init(&c->unlink);
449 	INIT_KFIFO(c->fifo);
450 	retval = kfifo_alloc(&c->fifo, cfg->num_buffers, GFP_KERNEL);
451 	if (retval)
452 		goto err_del_cdev_and_free_channel;
453 	init_waitqueue_head(&c->wq);
454 	mutex_init(&c->io_mutex);
455 	spin_lock_irqsave(&ch_list_lock, cl_flags);
456 	list_add_tail(&c->list, &channel_list);
457 	spin_unlock_irqrestore(&ch_list_lock, cl_flags);
458 	c->dev = device_create(comp.class, NULL, c->devno, NULL, "%s", name);
459 
460 	if (IS_ERR(c->dev)) {
461 		retval = PTR_ERR(c->dev);
462 		goto err_free_kfifo_and_del_list;
463 	}
464 	kobject_uevent(&c->dev->kobj, KOBJ_ADD);
465 	return 0;
466 
467 err_free_kfifo_and_del_list:
468 	kfifo_free(&c->fifo);
469 	list_del(&c->list);
470 err_del_cdev_and_free_channel:
471 	cdev_del(&c->cdev);
472 err_free_c:
473 	kfree(c);
474 err_remove_ida:
475 	ida_simple_remove(&comp.minor_id, current_minor);
476 	return retval;
477 }
478 
479 static struct cdev_component comp = {
480 	.cc = {
481 		.mod = THIS_MODULE,
482 		.name = "cdev",
483 		.probe_channel = comp_probe,
484 		.disconnect_channel = comp_disconnect_channel,
485 		.rx_completion = comp_rx_completion,
486 		.tx_completion = comp_tx_completion,
487 	},
488 };
489 
most_cdev_init(void)490 static int __init most_cdev_init(void)
491 {
492 	int err;
493 
494 	comp.class = class_create("most_cdev");
495 	if (IS_ERR(comp.class))
496 		return PTR_ERR(comp.class);
497 
498 	ida_init(&comp.minor_id);
499 
500 	err = alloc_chrdev_region(&comp.devno, 0, CHRDEV_REGION_SIZE, "cdev");
501 	if (err < 0)
502 		goto dest_ida;
503 	comp.major = MAJOR(comp.devno);
504 	err = most_register_component(&comp.cc);
505 	if (err)
506 		goto free_cdev;
507 	err = most_register_configfs_subsys(&comp.cc);
508 	if (err)
509 		goto deregister_comp;
510 	return 0;
511 
512 deregister_comp:
513 	most_deregister_component(&comp.cc);
514 free_cdev:
515 	unregister_chrdev_region(comp.devno, CHRDEV_REGION_SIZE);
516 dest_ida:
517 	ida_destroy(&comp.minor_id);
518 	class_destroy(comp.class);
519 	return err;
520 }
521 
most_cdev_exit(void)522 static void __exit most_cdev_exit(void)
523 {
524 	struct comp_channel *c, *tmp;
525 
526 	most_deregister_configfs_subsys(&comp.cc);
527 	most_deregister_component(&comp.cc);
528 
529 	list_for_each_entry_safe(c, tmp, &channel_list, list) {
530 		destroy_cdev(c);
531 		destroy_channel(c);
532 	}
533 	unregister_chrdev_region(comp.devno, CHRDEV_REGION_SIZE);
534 	ida_destroy(&comp.minor_id);
535 	class_destroy(comp.class);
536 }
537 
538 module_init(most_cdev_init);
539 module_exit(most_cdev_exit);
540 MODULE_AUTHOR("Christian Gromm <christian.gromm@microchip.com>");
541 MODULE_LICENSE("GPL");
542 MODULE_DESCRIPTION("character device component for mostcore");
543