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