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 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 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 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 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 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 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 */ 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 */ 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 */ 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 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 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 300ceea9344SChristian Gromm /** 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 */ 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 */ 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 */ 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 407ceea9344SChristian Gromm * 408ceea9344SChristian Gromm * This allocates achannel object and creates the device node in /dev 409ceea9344SChristian Gromm * 410ceea9344SChristian Gromm * Returns 0 on success or error code otherwise. 411ceea9344SChristian Gromm */ 412ceea9344SChristian Gromm static int comp_probe(struct most_interface *iface, int channel_id, 413ceea9344SChristian Gromm struct most_channel_config *cfg, char *name, char *args) 414ceea9344SChristian Gromm { 415ceea9344SChristian Gromm struct comp_channel *c; 416ceea9344SChristian Gromm unsigned long cl_flags; 417ceea9344SChristian Gromm int retval; 418ceea9344SChristian Gromm int current_minor; 419ceea9344SChristian Gromm 420ceea9344SChristian Gromm if (!cfg || !name) 421ceea9344SChristian Gromm return -EINVAL; 422ceea9344SChristian Gromm 423ceea9344SChristian Gromm c = get_channel(iface, channel_id); 424ceea9344SChristian Gromm if (c) 425ceea9344SChristian Gromm return -EEXIST; 426ceea9344SChristian Gromm 427ceea9344SChristian Gromm current_minor = ida_simple_get(&comp.minor_id, 0, 0, GFP_KERNEL); 428ceea9344SChristian Gromm if (current_minor < 0) 429ceea9344SChristian Gromm return current_minor; 430ceea9344SChristian Gromm 431ceea9344SChristian Gromm c = kzalloc(sizeof(*c), GFP_KERNEL); 432ceea9344SChristian Gromm if (!c) { 433ceea9344SChristian Gromm retval = -ENOMEM; 434ceea9344SChristian Gromm goto err_remove_ida; 435ceea9344SChristian Gromm } 436ceea9344SChristian Gromm 437ceea9344SChristian Gromm c->devno = MKDEV(comp.major, current_minor); 438ceea9344SChristian Gromm cdev_init(&c->cdev, &channel_fops); 439ceea9344SChristian Gromm c->cdev.owner = THIS_MODULE; 440ceea9344SChristian Gromm retval = cdev_add(&c->cdev, c->devno, 1); 441ceea9344SChristian Gromm if (retval < 0) 442ceea9344SChristian Gromm goto err_free_c; 443ceea9344SChristian Gromm c->iface = iface; 444ceea9344SChristian Gromm c->cfg = cfg; 445ceea9344SChristian Gromm c->channel_id = channel_id; 446ceea9344SChristian Gromm c->access_ref = 0; 447ceea9344SChristian Gromm spin_lock_init(&c->unlink); 448ceea9344SChristian Gromm INIT_KFIFO(c->fifo); 449ceea9344SChristian Gromm retval = kfifo_alloc(&c->fifo, cfg->num_buffers, GFP_KERNEL); 450ceea9344SChristian Gromm if (retval) 451ceea9344SChristian Gromm goto err_del_cdev_and_free_channel; 452ceea9344SChristian Gromm init_waitqueue_head(&c->wq); 453ceea9344SChristian Gromm mutex_init(&c->io_mutex); 454ceea9344SChristian Gromm spin_lock_irqsave(&ch_list_lock, cl_flags); 455ceea9344SChristian Gromm list_add_tail(&c->list, &channel_list); 456ceea9344SChristian Gromm spin_unlock_irqrestore(&ch_list_lock, cl_flags); 457ceea9344SChristian Gromm c->dev = device_create(comp.class, NULL, c->devno, NULL, "%s", name); 458ceea9344SChristian Gromm 459ceea9344SChristian Gromm if (IS_ERR(c->dev)) { 460ceea9344SChristian Gromm retval = PTR_ERR(c->dev); 461ceea9344SChristian Gromm goto err_free_kfifo_and_del_list; 462ceea9344SChristian Gromm } 463ceea9344SChristian Gromm kobject_uevent(&c->dev->kobj, KOBJ_ADD); 464ceea9344SChristian Gromm return 0; 465ceea9344SChristian Gromm 466ceea9344SChristian Gromm err_free_kfifo_and_del_list: 467ceea9344SChristian Gromm kfifo_free(&c->fifo); 468ceea9344SChristian Gromm list_del(&c->list); 469ceea9344SChristian Gromm err_del_cdev_and_free_channel: 470ceea9344SChristian Gromm cdev_del(&c->cdev); 471ceea9344SChristian Gromm err_free_c: 472ceea9344SChristian Gromm kfree(c); 473ceea9344SChristian Gromm err_remove_ida: 474ceea9344SChristian Gromm ida_simple_remove(&comp.minor_id, current_minor); 475ceea9344SChristian Gromm return retval; 476ceea9344SChristian Gromm } 477ceea9344SChristian Gromm 478ceea9344SChristian Gromm static struct cdev_component comp = { 479ceea9344SChristian Gromm .cc = { 480ceea9344SChristian Gromm .mod = THIS_MODULE, 481ceea9344SChristian Gromm .name = "cdev", 482ceea9344SChristian Gromm .probe_channel = comp_probe, 483ceea9344SChristian Gromm .disconnect_channel = comp_disconnect_channel, 484ceea9344SChristian Gromm .rx_completion = comp_rx_completion, 485ceea9344SChristian Gromm .tx_completion = comp_tx_completion, 486ceea9344SChristian Gromm }, 487ceea9344SChristian Gromm }; 488ceea9344SChristian Gromm 489*ddb13810SRandy Dunlap static int __init most_cdev_init(void) 490ceea9344SChristian Gromm { 491ceea9344SChristian Gromm int err; 492ceea9344SChristian Gromm 493ceea9344SChristian Gromm comp.class = class_create(THIS_MODULE, "most_cdev"); 494ceea9344SChristian Gromm if (IS_ERR(comp.class)) 495ceea9344SChristian Gromm return PTR_ERR(comp.class); 496ceea9344SChristian Gromm 497ceea9344SChristian Gromm ida_init(&comp.minor_id); 498ceea9344SChristian Gromm 499ceea9344SChristian Gromm err = alloc_chrdev_region(&comp.devno, 0, CHRDEV_REGION_SIZE, "cdev"); 500ceea9344SChristian Gromm if (err < 0) 501ceea9344SChristian Gromm goto dest_ida; 502ceea9344SChristian Gromm comp.major = MAJOR(comp.devno); 503ceea9344SChristian Gromm err = most_register_component(&comp.cc); 504ceea9344SChristian Gromm if (err) 505ceea9344SChristian Gromm goto free_cdev; 506ceea9344SChristian Gromm err = most_register_configfs_subsys(&comp.cc); 507ceea9344SChristian Gromm if (err) 508ceea9344SChristian Gromm goto deregister_comp; 509ceea9344SChristian Gromm return 0; 510ceea9344SChristian Gromm 511ceea9344SChristian Gromm deregister_comp: 512ceea9344SChristian Gromm most_deregister_component(&comp.cc); 513ceea9344SChristian Gromm free_cdev: 514ceea9344SChristian Gromm unregister_chrdev_region(comp.devno, CHRDEV_REGION_SIZE); 515ceea9344SChristian Gromm dest_ida: 516ceea9344SChristian Gromm ida_destroy(&comp.minor_id); 517ceea9344SChristian Gromm class_destroy(comp.class); 518ceea9344SChristian Gromm return err; 519ceea9344SChristian Gromm } 520ceea9344SChristian Gromm 521*ddb13810SRandy Dunlap static void __exit most_cdev_exit(void) 522ceea9344SChristian Gromm { 523ceea9344SChristian Gromm struct comp_channel *c, *tmp; 524ceea9344SChristian Gromm 525ceea9344SChristian Gromm most_deregister_configfs_subsys(&comp.cc); 526ceea9344SChristian Gromm most_deregister_component(&comp.cc); 527ceea9344SChristian Gromm 528ceea9344SChristian Gromm list_for_each_entry_safe(c, tmp, &channel_list, list) { 529ceea9344SChristian Gromm destroy_cdev(c); 530ceea9344SChristian Gromm destroy_channel(c); 531ceea9344SChristian Gromm } 532ceea9344SChristian Gromm unregister_chrdev_region(comp.devno, CHRDEV_REGION_SIZE); 533ceea9344SChristian Gromm ida_destroy(&comp.minor_id); 534ceea9344SChristian Gromm class_destroy(comp.class); 535ceea9344SChristian Gromm } 536ceea9344SChristian Gromm 537*ddb13810SRandy Dunlap module_init(most_cdev_init); 538*ddb13810SRandy Dunlap module_exit(most_cdev_exit); 539ceea9344SChristian Gromm MODULE_AUTHOR("Christian Gromm <christian.gromm@microchip.com>"); 540ceea9344SChristian Gromm MODULE_LICENSE("GPL"); 541ceea9344SChristian Gromm MODULE_DESCRIPTION("character device component for mostcore"); 542