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