blob: 3722f9abd7b9e179e79a463cfff83421e3980ef3 [file] [log] [blame]
Greg Kroah-Hartman1a79f222017-11-07 14:58:50 +01001// SPDX-License-Identifier: GPL-2.0
Christian Gromm9bc79bb2015-07-24 16:11:49 +02002/*
Christian Grommb7937dc2017-11-21 15:05:12 +01003 * cdev.c - Character device component for Mostcore
Christian Gromm9bc79bb2015-07-24 16:11:49 +02004 *
5 * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG
Christian Gromm9bc79bb2015-07-24 16:11:49 +02006 */
7
Christian Gromm9bc79bb2015-07-24 16:11:49 +02008#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>
Christian Grommaac997d2015-09-28 17:18:57 +020014#include <linux/poll.h>
Christian Gromm9bc79bb2015-07-24 16:11:49 +020015#include <linux/kfifo.h>
16#include <linux/uaccess.h>
17#include <linux/idr.h>
Christian Grommb2765272020-03-10 14:02:40 +010018#include <linux/most.h>
Christian Gromm9bc79bb2015-07-24 16:11:49 +020019
Christian Grommaba258b2018-05-08 11:45:06 +020020#define CHRDEV_REGION_SIZE 50
21
Christian Grommc73d9152017-11-21 15:05:20 +010022static struct cdev_component {
23 dev_t devno;
24 struct ida minor_id;
25 unsigned int major;
26 struct class *class;
Christian Gromm45917e72019-12-13 13:04:15 +010027 struct most_component cc;
Christian Grommc73d9152017-11-21 15:05:20 +010028} comp;
Christian Gromm9bc79bb2015-07-24 16:11:49 +020029
Christian Grommef0fbbb2017-11-21 15:05:08 +010030struct comp_channel {
Christian Gromm9bc79bb2015-07-24 16:11:49 +020031 wait_queue_head_t wq;
Christian Grommfa96b8e2015-12-22 10:53:04 +010032 spinlock_t unlink; /* synchronization lock to unlink channels */
Christian Gromm9bc79bb2015-07-24 16:11:49 +020033 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;
Christian Gromm06e7ecf2015-12-22 10:52:45 +010040 size_t mbo_offs;
Christian Gromm9bc79bb2015-07-24 16:11:49 +020041 DECLARE_KFIFO_PTR(fifo, typeof(struct mbo *));
Christian Grommb3c9f3c2015-12-22 10:53:05 +010042 int access_ref;
Christian Gromm9bc79bb2015-07-24 16:11:49 +020043 struct list_head list;
44};
Christian Gromm9cbe5aa2015-10-21 17:50:45 +020045
Christian Grommef0fbbb2017-11-21 15:05:08 +010046#define to_channel(d) container_of(d, struct comp_channel, cdev)
Shixin Liu57515642021-03-29 17:40:15 +080047static LIST_HEAD(channel_list);
Shixin Liu2c4134e2021-03-29 17:40:14 +080048static DEFINE_SPINLOCK(ch_list_lock);
Christian Gromm9bc79bb2015-07-24 16:11:49 +020049
Christian Grommef0fbbb2017-11-21 15:05:08 +010050static inline bool ch_has_mbo(struct comp_channel *c)
Christian Grommcdc293d2016-01-12 14:00:03 +010051{
Christian Grommc73d9152017-11-21 15:05:20 +010052 return channel_has_mbo(c->iface, c->channel_id, &comp.cc) > 0;
Christian Grommcdc293d2016-01-12 14:00:03 +010053}
54
Christian Gromm7d56f622018-05-08 11:45:08 +020055static inline struct mbo *ch_get_mbo(struct comp_channel *c, struct mbo **mbo)
Christian Grommfa96b8e2015-12-22 10:53:04 +010056{
Christian Grommda2c0872016-09-23 15:20:02 +020057 if (!kfifo_peek(&c->fifo, mbo)) {
Christian Grommc73d9152017-11-21 15:05:20 +010058 *mbo = most_get_mbo(c->iface, c->channel_id, &comp.cc);
Christian Grommda2c0872016-09-23 15:20:02 +020059 if (*mbo)
60 kfifo_in(&c->fifo, mbo, 1);
61 }
Christian Grommfa96b8e2015-12-22 10:53:04 +010062 return *mbo;
63}
64
Christian Grommef0fbbb2017-11-21 15:05:08 +010065static struct comp_channel *get_channel(struct most_interface *iface, int id)
Christian Gromm9bc79bb2015-07-24 16:11:49 +020066{
Christian Grommef0fbbb2017-11-21 15:05:08 +010067 struct comp_channel *c, *tmp;
Christian Gromm9bc79bb2015-07-24 16:11:49 +020068 unsigned long flags;
Christian Gromm9bc79bb2015-07-24 16:11:49 +020069
70 spin_lock_irqsave(&ch_list_lock, flags);
Christian Grommd8b082e2015-12-22 10:53:03 +010071 list_for_each_entry_safe(c, tmp, &channel_list, list) {
72 if ((c->iface == iface) && (c->channel_id == id)) {
Christian Gromm0ae9e4f2020-06-22 10:48:15 +020073 spin_unlock_irqrestore(&ch_list_lock, flags);
74 return c;
Christian Gromm9bc79bb2015-07-24 16:11:49 +020075 }
76 }
77 spin_unlock_irqrestore(&ch_list_lock, flags);
Christian Gromm0ae9e4f2020-06-22 10:48:15 +020078 return NULL;
Christian Gromm9bc79bb2015-07-24 16:11:49 +020079}
80
Christian Grommef0fbbb2017-11-21 15:05:08 +010081static void stop_channel(struct comp_channel *c)
Christian Gromm5f858a62015-12-22 10:53:01 +010082{
83 struct mbo *mbo;
84
85 while (kfifo_out((struct kfifo *)&c->fifo, &mbo, 1))
86 most_put_mbo(mbo);
Christian Grommc73d9152017-11-21 15:05:20 +010087 most_stop_channel(c->iface, c->channel_id, &comp.cc);
Christian Gromm5f858a62015-12-22 10:53:01 +010088}
89
Christian Grommef0fbbb2017-11-21 15:05:08 +010090static void destroy_cdev(struct comp_channel *c)
Christian Gromm5f858a62015-12-22 10:53:01 +010091{
92 unsigned long flags;
93
Christian Grommc73d9152017-11-21 15:05:20 +010094 device_destroy(comp.class, c->devno);
Christian Gromm5f858a62015-12-22 10:53:01 +010095 cdev_del(&c->cdev);
Christian Gromm5f858a62015-12-22 10:53:01 +010096 spin_lock_irqsave(&ch_list_lock, flags);
97 list_del(&c->list);
98 spin_unlock_irqrestore(&ch_list_lock, flags);
Andrey Shvetsov2c4aaa12017-04-07 15:38:37 +020099}
100
Christian Grommef0fbbb2017-11-21 15:05:08 +0100101static void destroy_channel(struct comp_channel *c)
Andrey Shvetsov2c4aaa12017-04-07 15:38:37 +0200102{
Christian Grommc73d9152017-11-21 15:05:20 +0100103 ida_simple_remove(&comp.minor_id, MINOR(c->devno));
Andrey Shvetsov2c4aaa12017-04-07 15:38:37 +0200104 kfifo_free(&c->fifo);
105 kfree(c);
Christian Gromm5f858a62015-12-22 10:53:01 +0100106}
107
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200108/**
Christian Gromm1fd923f2017-11-21 15:05:19 +0100109 * comp_open - implements the syscall to open the device
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200110 * @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 */
Christian Gromm1fd923f2017-11-21 15:05:19 +0100116static int comp_open(struct inode *inode, struct file *filp)
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200117{
Christian Grommef0fbbb2017-11-21 15:05:08 +0100118 struct comp_channel *c;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200119 int ret;
120
Christian Grommd8b082e2015-12-22 10:53:03 +0100121 c = to_channel(inode->i_cdev);
122 filp->private_data = c;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200123
Christian Grommd8b082e2015-12-22 10:53:03 +0100124 if (((c->cfg->direction == MOST_CH_RX) &&
Christian Gromm623d8002015-10-21 17:50:48 +0200125 ((filp->f_flags & O_ACCMODE) != O_RDONLY)) ||
Christian Grommd8b082e2015-12-22 10:53:03 +0100126 ((c->cfg->direction == MOST_CH_TX) &&
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200127 ((filp->f_flags & O_ACCMODE) != O_WRONLY))) {
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200128 return -EACCES;
129 }
Christian Grommfa96b8e2015-12-22 10:53:04 +0100130
131 mutex_lock(&c->io_mutex);
132 if (!c->dev) {
Christian Grommfa96b8e2015-12-22 10:53:04 +0100133 mutex_unlock(&c->io_mutex);
Christian Gromm9b28e142016-08-19 13:09:33 +0200134 return -ENODEV;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200135 }
136
Christian Grommb3c9f3c2015-12-22 10:53:05 +0100137 if (c->access_ref) {
Christian Grommfa96b8e2015-12-22 10:53:04 +0100138 mutex_unlock(&c->io_mutex);
139 return -EBUSY;
140 }
141
Christian Grommf45b0fb2015-12-22 10:53:06 +0100142 c->mbo_offs = 0;
Christian Grommc73d9152017-11-21 15:05:20 +0100143 ret = most_start_channel(c->iface, c->channel_id, &comp.cc);
Christian Grommb3c9f3c2015-12-22 10:53:05 +0100144 if (!ret)
145 c->access_ref = 1;
Christian Grommfa96b8e2015-12-22 10:53:04 +0100146 mutex_unlock(&c->io_mutex);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200147 return ret;
148}
149
150/**
Christian Gromm1fd923f2017-11-21 15:05:19 +0100151 * comp_close - implements the syscall to close the device
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200152 * @inode: inode pointer
153 * @filp: file pointer
154 *
155 * This stops the channel within the core.
156 */
Christian Gromm1fd923f2017-11-21 15:05:19 +0100157static int comp_close(struct inode *inode, struct file *filp)
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200158{
Christian Grommef0fbbb2017-11-21 15:05:08 +0100159 struct comp_channel *c = to_channel(inode->i_cdev);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200160
Christian Grommd8b082e2015-12-22 10:53:03 +0100161 mutex_lock(&c->io_mutex);
Christian Grommfa96b8e2015-12-22 10:53:04 +0100162 spin_lock(&c->unlink);
Christian Grommb3c9f3c2015-12-22 10:53:05 +0100163 c->access_ref = 0;
Christian Grommfa96b8e2015-12-22 10:53:04 +0100164 spin_unlock(&c->unlink);
165 if (c->dev) {
166 stop_channel(c);
167 mutex_unlock(&c->io_mutex);
168 } else {
Christian Grommfa96b8e2015-12-22 10:53:04 +0100169 mutex_unlock(&c->io_mutex);
Andrey Shvetsov2c4aaa12017-04-07 15:38:37 +0200170 destroy_channel(c);
Christian Grommfa96b8e2015-12-22 10:53:04 +0100171 }
Christian Gromm5f858a62015-12-22 10:53:01 +0100172 return 0;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200173}
174
175/**
Christian Gromm1fd923f2017-11-21 15:05:19 +0100176 * comp_write - implements the syscall to write to the device
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200177 * @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 */
Christian Gromm1fd923f2017-11-21 15:05:19 +0100182static ssize_t comp_write(struct file *filp, const char __user *buf,
183 size_t count, loff_t *offset)
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200184{
Christian Gromm5adf5dc2015-12-22 10:53:07 +0100185 int ret;
Christian Grommda2c0872016-09-23 15:20:02 +0200186 size_t to_copy, left;
Christian Grommfa96b8e2015-12-22 10:53:04 +0100187 struct mbo *mbo = NULL;
Christian Grommef0fbbb2017-11-21 15:05:08 +0100188 struct comp_channel *c = filp->private_data;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200189
Christian Grommd8b082e2015-12-22 10:53:03 +0100190 mutex_lock(&c->io_mutex);
Christian Grommfa96b8e2015-12-22 10:53:04 +0100191 while (c->dev && !ch_get_mbo(c, &mbo)) {
Christian Grommd8b082e2015-12-22 10:53:03 +0100192 mutex_unlock(&c->io_mutex);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200193
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200194 if ((filp->f_flags & O_NONBLOCK))
195 return -EAGAIN;
Christian Grommfa96b8e2015-12-22 10:53:04 +0100196 if (wait_event_interruptible(c->wq, ch_has_mbo(c) || !c->dev))
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200197 return -ERESTARTSYS;
Christian Grommfa96b8e2015-12-22 10:53:04 +0100198 mutex_lock(&c->io_mutex);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200199 }
200
Christian Grommd8b082e2015-12-22 10:53:03 +0100201 if (unlikely(!c->dev)) {
Christian Gromm9b28e142016-08-19 13:09:33 +0200202 ret = -ENODEV;
Christian Gromm5adf5dc2015-12-22 10:53:07 +0100203 goto unlock;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200204 }
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200205
Christian Grommda2c0872016-09-23 15:20:02 +0200206 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) {
Christian Gromm5adf5dc2015-12-22 10:53:07 +0100209 ret = -EFAULT;
Christian Grommda2c0872016-09-23 15:20:02 +0200210 goto unlock;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200211 }
212
Christian Grommda2c0872016-09-23 15:20:02 +0200213 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;
Christian Gromm5adf5dc2015-12-22 10:53:07 +0100224unlock:
Christian Grommfa96b8e2015-12-22 10:53:04 +0100225 mutex_unlock(&c->io_mutex);
Christian Gromm5adf5dc2015-12-22 10:53:07 +0100226 return ret;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200227}
228
229/**
Christian Gromm1fd923f2017-11-21 15:05:19 +0100230 * comp_read - implements the syscall to read from the device
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200231 * @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 */
236static ssize_t
Christian Gromm1fd923f2017-11-21 15:05:19 +0100237comp_read(struct file *filp, char __user *buf, size_t count, loff_t *offset)
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200238{
Christian Gromm06e7ecf2015-12-22 10:52:45 +0100239 size_t to_copy, not_copied, copied;
Christian Gromm8463d9f2018-05-08 11:45:05 +0200240 struct mbo *mbo = NULL;
Christian Grommef0fbbb2017-11-21 15:05:08 +0100241 struct comp_channel *c = filp->private_data;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200242
Christian Grommfa96b8e2015-12-22 10:53:04 +0100243 mutex_lock(&c->io_mutex);
Christian Grommf45b0fb2015-12-22 10:53:06 +0100244 while (c->dev && !kfifo_peek(&c->fifo, &mbo)) {
Christian Grommfa96b8e2015-12-22 10:53:04 +0100245 mutex_unlock(&c->io_mutex);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200246 if (filp->f_flags & O_NONBLOCK)
247 return -EAGAIN;
Christian Grommd8b082e2015-12-22 10:53:03 +0100248 if (wait_event_interruptible(c->wq,
249 (!kfifo_is_empty(&c->fifo) ||
250 (!c->dev))))
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200251 return -ERESTARTSYS;
Christian Grommfa96b8e2015-12-22 10:53:04 +0100252 mutex_lock(&c->io_mutex);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200253 }
254
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200255 /* make sure we don't submit to gone devices */
Christian Grommd8b082e2015-12-22 10:53:03 +0100256 if (unlikely(!c->dev)) {
257 mutex_unlock(&c->io_mutex);
Christian Gromm9b28e142016-08-19 13:09:33 +0200258 return -ENODEV;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200259 }
260
Christian Gromme6d6cbe2015-12-22 10:52:46 +0100261 to_copy = min_t(size_t,
262 count,
Christian Grommd8b082e2015-12-22 10:53:03 +0100263 mbo->processed_length - c->mbo_offs);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200264
265 not_copied = copy_to_user(buf,
Christian Grommd8b082e2015-12-22 10:53:03 +0100266 mbo->virt_address + c->mbo_offs,
Christian Grommf9f24872015-12-22 10:52:43 +0100267 to_copy);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200268
Christian Gromm4aa575a2015-12-22 10:52:44 +0100269 copied = to_copy - not_copied;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200270
Christian Grommd8b082e2015-12-22 10:53:03 +0100271 c->mbo_offs += copied;
272 if (c->mbo_offs >= mbo->processed_length) {
Christian Grommf45b0fb2015-12-22 10:53:06 +0100273 kfifo_skip(&c->fifo);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200274 most_put_mbo(mbo);
Christian Grommd8b082e2015-12-22 10:53:03 +0100275 c->mbo_offs = 0;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200276 }
Christian Grommd8b082e2015-12-22 10:53:03 +0100277 mutex_unlock(&c->io_mutex);
Christian Grommf9f24872015-12-22 10:52:43 +0100278 return copied;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200279}
280
Linus Torvalds5d8515b2018-02-01 09:51:57 -0800281static __poll_t comp_poll(struct file *filp, poll_table *wait)
Christian Grommaac997d2015-09-28 17:18:57 +0200282{
Christian Grommef0fbbb2017-11-21 15:05:08 +0100283 struct comp_channel *c = filp->private_data;
Al Viroafc9a422017-07-03 06:39:46 -0400284 __poll_t mask = 0;
Christian Grommaac997d2015-09-28 17:18:57 +0200285
Christian Grommc27fc352015-10-28 15:14:35 +0100286 poll_wait(filp, &c->wq, wait);
Christian Grommaac997d2015-09-28 17:18:57 +0200287
Christian Gromm993c1632018-05-08 11:45:10 +0200288 mutex_lock(&c->io_mutex);
Christian Grommaac997d2015-09-28 17:18:57 +0200289 if (c->cfg->direction == MOST_CH_RX) {
Christian Gromm993c1632018-05-08 11:45:10 +0200290 if (!c->dev || !kfifo_is_empty(&c->fifo))
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800291 mask |= EPOLLIN | EPOLLRDNORM;
Christian Grommaac997d2015-09-28 17:18:57 +0200292 } else {
Christian Gromm993c1632018-05-08 11:45:10 +0200293 if (!c->dev || !kfifo_is_empty(&c->fifo) || ch_has_mbo(c))
Linus Torvaldsa9a08842018-02-11 14:34:03 -0800294 mask |= EPOLLOUT | EPOLLWRNORM;
Christian Grommaac997d2015-09-28 17:18:57 +0200295 }
Christian Gromm993c1632018-05-08 11:45:10 +0200296 mutex_unlock(&c->io_mutex);
Christian Grommaac997d2015-09-28 17:18:57 +0200297 return mask;
298}
299
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200300/**
301 * Initialization of struct file_operations
302 */
303static const struct file_operations channel_fops = {
304 .owner = THIS_MODULE,
Christian Gromm1fd923f2017-11-21 15:05:19 +0100305 .read = comp_read,
306 .write = comp_write,
307 .open = comp_open,
308 .release = comp_close,
309 .poll = comp_poll,
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200310};
311
312/**
Christian Gromm1fd923f2017-11-21 15:05:19 +0100313 * comp_disconnect_channel - disconnect a channel
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200314 * @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 */
Christian Gromm1fd923f2017-11-21 15:05:19 +0100320static int comp_disconnect_channel(struct most_interface *iface, int channel_id)
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200321{
Christian Grommef0fbbb2017-11-21 15:05:08 +0100322 struct comp_channel *c;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200323
Christian Grommd8b082e2015-12-22 10:53:03 +0100324 c = get_channel(iface, channel_id);
325 if (!c)
Christian Gromm78aee652020-06-22 10:48:14 +0200326 return -EINVAL;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200327
Christian Grommd8b082e2015-12-22 10:53:03 +0100328 mutex_lock(&c->io_mutex);
Christian Grommfa96b8e2015-12-22 10:53:04 +0100329 spin_lock(&c->unlink);
Christian Grommd8b082e2015-12-22 10:53:03 +0100330 c->dev = NULL;
Christian Grommfa96b8e2015-12-22 10:53:04 +0100331 spin_unlock(&c->unlink);
Andrey Shvetsov2c4aaa12017-04-07 15:38:37 +0200332 destroy_cdev(c);
Christian Grommb3c9f3c2015-12-22 10:53:05 +0100333 if (c->access_ref) {
Christian Grommfa96b8e2015-12-22 10:53:04 +0100334 stop_channel(c);
Christian Grommd8b082e2015-12-22 10:53:03 +0100335 wake_up_interruptible(&c->wq);
Christian Grommfa96b8e2015-12-22 10:53:04 +0100336 mutex_unlock(&c->io_mutex);
337 } else {
Christian Grommfa96b8e2015-12-22 10:53:04 +0100338 mutex_unlock(&c->io_mutex);
Andrey Shvetsov2c4aaa12017-04-07 15:38:37 +0200339 destroy_channel(c);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200340 }
341 return 0;
342}
343
344/**
Christian Gromm1fd923f2017-11-21 15:05:19 +0100345 * comp_rx_completion - completion handler for rx channels
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200346 * @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 */
Christian Gromm1fd923f2017-11-21 15:05:19 +0100351static int comp_rx_completion(struct mbo *mbo)
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200352{
Christian Grommef0fbbb2017-11-21 15:05:08 +0100353 struct comp_channel *c;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200354
355 if (!mbo)
356 return -EINVAL;
357
Christian Grommd8b082e2015-12-22 10:53:03 +0100358 c = get_channel(mbo->ifp, mbo->hdm_channel_id);
359 if (!c)
Christian Gromm78aee652020-06-22 10:48:14 +0200360 return -EINVAL;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200361
Christian Grommfa96b8e2015-12-22 10:53:04 +0100362 spin_lock(&c->unlink);
Christian Grommb3c9f3c2015-12-22 10:53:05 +0100363 if (!c->access_ref || !c->dev) {
Christian Grommfa96b8e2015-12-22 10:53:04 +0100364 spin_unlock(&c->unlink);
Christian Gromm9b28e142016-08-19 13:09:33 +0200365 return -ENODEV;
Christian Grommfa96b8e2015-12-22 10:53:04 +0100366 }
Christian Grommd8b082e2015-12-22 10:53:03 +0100367 kfifo_in(&c->fifo, &mbo, 1);
Christian Grommfa96b8e2015-12-22 10:53:04 +0100368 spin_unlock(&c->unlink);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200369#ifdef DEBUG_MESG
Christian Grommd8b082e2015-12-22 10:53:03 +0100370 if (kfifo_is_full(&c->fifo))
Christian Gromm08839382020-06-22 10:48:11 +0200371 dev_warn(c->dev, "Fifo is full\n");
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200372#endif
Christian Grommd8b082e2015-12-22 10:53:03 +0100373 wake_up_interruptible(&c->wq);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200374 return 0;
375}
376
377/**
Christian Gromm1fd923f2017-11-21 15:05:19 +0100378 * comp_tx_completion - completion handler for tx channels
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200379 * @iface: pointer to interface instance
380 * @channel_id: channel index/ID
381 *
382 * This wakes sleeping processes in the wait-queue.
383 */
Christian Gromm1fd923f2017-11-21 15:05:19 +0100384static int comp_tx_completion(struct most_interface *iface, int channel_id)
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200385{
Christian Grommef0fbbb2017-11-21 15:05:08 +0100386 struct comp_channel *c;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200387
Christian Grommd8b082e2015-12-22 10:53:03 +0100388 c = get_channel(iface, channel_id);
389 if (!c)
Christian Gromm78aee652020-06-22 10:48:14 +0200390 return -EINVAL;
Christian Gromm08839382020-06-22 10:48:11 +0200391
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
Christian Grommd8b082e2015-12-22 10:53:03 +0100397 wake_up_interruptible(&c->wq);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200398 return 0;
399}
400
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200401/**
Christian Gromm1fd923f2017-11-21 15:05:19 +0100402 * comp_probe - probe function of the driver module
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200403 * @iface: pointer to interface instance
404 * @channel_id: channel index/ID
405 * @cfg: pointer to actual channel configuration
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200406 * @name: name of the device to be created
407 *
408 * This allocates achannel object and creates the device node in /dev
409 *
410 * Returns 0 on success or error code otherwise.
411 */
Christian Gromm1fd923f2017-11-21 15:05:19 +0100412static int comp_probe(struct most_interface *iface, int channel_id,
Christian Grommdfee92d2019-04-03 15:19:45 +0200413 struct most_channel_config *cfg, char *name, char *args)
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200414{
Christian Grommef0fbbb2017-11-21 15:05:08 +0100415 struct comp_channel *c;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200416 unsigned long cl_flags;
417 int retval;
418 int current_minor;
419
Christian Gromme8e0f7f2020-06-22 10:48:13 +0200420 if (!cfg || !name)
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200421 return -EINVAL;
Christian Gromm61fd9712020-06-22 10:48:10 +0200422
Christian Grommd8b082e2015-12-22 10:53:03 +0100423 c = get_channel(iface, channel_id);
424 if (c)
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200425 return -EEXIST;
426
Christian Grommc73d9152017-11-21 15:05:20 +0100427 current_minor = ida_simple_get(&comp.minor_id, 0, 0, GFP_KERNEL);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200428 if (current_minor < 0)
429 return current_minor;
430
Christian Grommd8b082e2015-12-22 10:53:03 +0100431 c = kzalloc(sizeof(*c), GFP_KERNEL);
432 if (!c) {
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200433 retval = -ENOMEM;
Christian Grommbddd3c22018-09-21 11:28:51 +0200434 goto err_remove_ida;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200435 }
436
Christian Grommc73d9152017-11-21 15:05:20 +0100437 c->devno = MKDEV(comp.major, current_minor);
Christian Grommd8b082e2015-12-22 10:53:03 +0100438 cdev_init(&c->cdev, &channel_fops);
439 c->cdev.owner = THIS_MODULE;
Colin Ian King5ae89072019-02-02 22:34:49 +0000440 retval = cdev_add(&c->cdev, c->devno, 1);
441 if (retval < 0)
442 goto err_free_c;
Christian Grommd8b082e2015-12-22 10:53:03 +0100443 c->iface = iface;
444 c->cfg = cfg;
445 c->channel_id = channel_id;
Christian Grommb3c9f3c2015-12-22 10:53:05 +0100446 c->access_ref = 0;
Christian Grommfa96b8e2015-12-22 10:53:04 +0100447 spin_lock_init(&c->unlink);
Christian Grommd8b082e2015-12-22 10:53:03 +0100448 INIT_KFIFO(c->fifo);
449 retval = kfifo_alloc(&c->fifo, cfg->num_buffers, GFP_KERNEL);
Keyur Patelebf256e2019-07-14 13:27:06 -0400450 if (retval)
Christian Grommbddd3c22018-09-21 11:28:51 +0200451 goto err_del_cdev_and_free_channel;
Christian Grommd8b082e2015-12-22 10:53:03 +0100452 init_waitqueue_head(&c->wq);
453 mutex_init(&c->io_mutex);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200454 spin_lock_irqsave(&ch_list_lock, cl_flags);
Christian Grommd8b082e2015-12-22 10:53:03 +0100455 list_add_tail(&c->list, &channel_list);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200456 spin_unlock_irqrestore(&ch_list_lock, cl_flags);
Christian Grommc73d9152017-11-21 15:05:20 +0100457 c->dev = device_create(comp.class, NULL, c->devno, NULL, "%s", name);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200458
Sudip Mukherjeeea398542016-02-08 22:55:34 +0530459 if (IS_ERR(c->dev)) {
460 retval = PTR_ERR(c->dev);
Christian Grommbddd3c22018-09-21 11:28:51 +0200461 goto err_free_kfifo_and_del_list;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200462 }
Christian Grommd8b082e2015-12-22 10:53:03 +0100463 kobject_uevent(&c->dev->kobj, KOBJ_ADD);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200464 return 0;
465
Christian Grommbddd3c22018-09-21 11:28:51 +0200466err_free_kfifo_and_del_list:
Christian Grommd8b082e2015-12-22 10:53:03 +0100467 kfifo_free(&c->fifo);
468 list_del(&c->list);
Christian Grommbddd3c22018-09-21 11:28:51 +0200469err_del_cdev_and_free_channel:
Christian Grommd8b082e2015-12-22 10:53:03 +0100470 cdev_del(&c->cdev);
Colin Ian King5ae89072019-02-02 22:34:49 +0000471err_free_c:
Christian Grommd8b082e2015-12-22 10:53:03 +0100472 kfree(c);
Christian Grommbddd3c22018-09-21 11:28:51 +0200473err_remove_ida:
Christian Grommc73d9152017-11-21 15:05:20 +0100474 ida_simple_remove(&comp.minor_id, current_minor);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200475 return retval;
476}
477
Christian Grommc73d9152017-11-21 15:05:20 +0100478static struct cdev_component comp = {
479 .cc = {
Christian Gromm08283d32019-11-08 17:21:08 +0100480 .mod = THIS_MODULE,
Christian Grommc73d9152017-11-21 15:05:20 +0100481 .name = "cdev",
482 .probe_channel = comp_probe,
483 .disconnect_channel = comp_disconnect_channel,
484 .rx_completion = comp_rx_completion,
485 .tx_completion = comp_tx_completion,
486 },
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200487};
488
Randy Dunlapddb13812021-07-11 15:31:46 -0700489static int __init most_cdev_init(void)
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200490{
Christian Gromm9b28e142016-08-19 13:09:33 +0200491 int err;
492
Christian Grommc73d9152017-11-21 15:05:20 +0100493 comp.class = class_create(THIS_MODULE, "most_cdev");
Christian Gromm61fd9712020-06-22 10:48:10 +0200494 if (IS_ERR(comp.class))
Christian Grommc73d9152017-11-21 15:05:20 +0100495 return PTR_ERR(comp.class);
Christian Grommc73d9152017-11-21 15:05:20 +0100496
Christian Grommc73d9152017-11-21 15:05:20 +0100497 ida_init(&comp.minor_id);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200498
Christian Grommaba258b2018-05-08 11:45:06 +0200499 err = alloc_chrdev_region(&comp.devno, 0, CHRDEV_REGION_SIZE, "cdev");
Christian Gromm324e87b2016-08-19 13:09:34 +0200500 if (err < 0)
Christian Gromm1d9e3a02016-08-22 16:46:24 +0200501 goto dest_ida;
Christian Grommc73d9152017-11-21 15:05:20 +0100502 comp.major = MAJOR(comp.devno);
503 err = most_register_component(&comp.cc);
Christian Gromm9b28e142016-08-19 13:09:33 +0200504 if (err)
Christian Grommc73d9152017-11-21 15:05:20 +0100505 goto free_cdev;
Christian Gromm919c03a2019-04-03 15:19:48 +0200506 err = most_register_configfs_subsys(&comp.cc);
507 if (err)
508 goto deregister_comp;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200509 return 0;
510
Christian Gromm919c03a2019-04-03 15:19:48 +0200511deregister_comp:
512 most_deregister_component(&comp.cc);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200513free_cdev:
Christian Grommaba258b2018-05-08 11:45:06 +0200514 unregister_chrdev_region(comp.devno, CHRDEV_REGION_SIZE);
Christian Gromm1d9e3a02016-08-22 16:46:24 +0200515dest_ida:
Christian Grommc73d9152017-11-21 15:05:20 +0100516 ida_destroy(&comp.minor_id);
517 class_destroy(comp.class);
Christian Gromm9b28e142016-08-19 13:09:33 +0200518 return err;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200519}
520
Randy Dunlapddb13812021-07-11 15:31:46 -0700521static void __exit most_cdev_exit(void)
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200522{
Christian Grommef0fbbb2017-11-21 15:05:08 +0100523 struct comp_channel *c, *tmp;
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200524
Christian Gromm919c03a2019-04-03 15:19:48 +0200525 most_deregister_configfs_subsys(&comp.cc);
Christian Grommc73d9152017-11-21 15:05:20 +0100526 most_deregister_component(&comp.cc);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200527
Christian Grommd8b082e2015-12-22 10:53:03 +0100528 list_for_each_entry_safe(c, tmp, &channel_list, list) {
529 destroy_cdev(c);
Andrey Shvetsov2c4aaa12017-04-07 15:38:37 +0200530 destroy_channel(c);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200531 }
Suresh Udipiaf708902019-04-24 21:23:43 +0200532 unregister_chrdev_region(comp.devno, CHRDEV_REGION_SIZE);
Christian Grommc73d9152017-11-21 15:05:20 +0100533 ida_destroy(&comp.minor_id);
534 class_destroy(comp.class);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200535}
536
Randy Dunlapddb13812021-07-11 15:31:46 -0700537module_init(most_cdev_init);
538module_exit(most_cdev_exit);
Christian Gromm9bc79bb2015-07-24 16:11:49 +0200539MODULE_AUTHOR("Christian Gromm <christian.gromm@microchip.com>");
540MODULE_LICENSE("GPL");
Christian Grommb7937dc2017-11-21 15:05:12 +0100541MODULE_DESCRIPTION("character device component for mostcore");