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