123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- /**
- * TSIF driver client
- *
- * Character device that, being read
- * returns stream of TSIF packets.
- *
- * Copyright (c) 2009-2011, The Linux Foundation. All rights
- * reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/module.h> /* Needed by all modules */
- #include <linux/kernel.h> /* Needed for KERN_INFO */
- #include <linux/cdev.h>
- #include <linux/err.h> /* IS_ERR etc. */
- #include <linux/fs.h>
- #include <linux/device.h>
- #include <linux/sched.h> /* TASK_INTERRUPTIBLE */
- #include <linux/uaccess.h> /* copy_to_user */
- #include <linux/tsif_api.h>
- struct tsif_chrdev {
- struct cdev cdev;
- struct device *dev;
- wait_queue_head_t wq_read;
- void *cookie;
- /* mirror for tsif data */
- void *data_buffer;
- unsigned buf_size_packets; /**< buffer size in packets */
- unsigned ri, wi;
- enum tsif_state state;
- unsigned rptr;
- };
- static ssize_t tsif_open(struct inode *inode, struct file *file)
- {
- int rc;
- struct tsif_chrdev *the_dev =
- container_of(inode->i_cdev, struct tsif_chrdev, cdev);
- if (!the_dev->cookie) /* not bound yet */
- return -ENODEV;
- file->private_data = the_dev;
- rc = tsif_start(the_dev->cookie);
- if (rc)
- return rc;
- tsif_get_info(the_dev->cookie, &the_dev->data_buffer,
- &the_dev->buf_size_packets);
- the_dev->rptr = 0;
- return nonseekable_open(inode, file);
- }
- static ssize_t tsif_release(struct inode *inode, struct file *filp)
- {
- struct tsif_chrdev *the_dev = filp->private_data;
- tsif_stop(the_dev->cookie);
- return 0;
- }
- static ssize_t tsif_read(struct file *filp, char __user *buf, size_t count,
- loff_t *f_pos)
- {
- int avail = 0;
- int wi;
- struct tsif_chrdev *the_dev = filp->private_data;
- tsif_get_state(the_dev->cookie, &the_dev->ri, &the_dev->wi,
- &the_dev->state);
- /* consistency check */
- if (the_dev->ri != (the_dev->rptr / TSIF_PKT_SIZE)) {
- dev_err(the_dev->dev,
- "%s: inconsistent read pointers: ri %d rptr %d\n",
- __func__, the_dev->ri, the_dev->rptr);
- the_dev->rptr = the_dev->ri * TSIF_PKT_SIZE;
- }
- /* ri == wi if no data */
- if (the_dev->ri == the_dev->wi) {
- /* shall I block waiting for data? */
- if (filp->f_flags & O_NONBLOCK) {
- if (the_dev->state == tsif_state_running) {
- return -EAGAIN;
- } else {
- /* not running -> EOF */
- return 0;
- }
- }
- if (wait_event_interruptible(the_dev->wq_read,
- (the_dev->ri != the_dev->wi) ||
- (the_dev->state != tsif_state_running))) {
- /* got signal -> tell FS to handle it */
- return -ERESTARTSYS;
- }
- if (the_dev->ri == the_dev->wi) {
- /* still no data -> EOF */
- return 0;
- }
- }
- /* contiguous chunk last up to wi or end of buffer */
- wi = (the_dev->wi > the_dev->ri) ?
- the_dev->wi : the_dev->buf_size_packets;
- avail = min(wi * TSIF_PKT_SIZE - the_dev->rptr, count);
- if (copy_to_user(buf, the_dev->data_buffer + the_dev->rptr, avail))
- return -EFAULT;
- the_dev->rptr = (the_dev->rptr + avail) %
- (TSIF_PKT_SIZE * the_dev->buf_size_packets);
- the_dev->ri = the_dev->rptr / TSIF_PKT_SIZE;
- *f_pos += avail;
- tsif_reclaim_packets(the_dev->cookie, the_dev->ri);
- return avail;
- }
- static void tsif_notify(void *data)
- {
- struct tsif_chrdev *the_dev = data;
- tsif_get_state(the_dev->cookie, &the_dev->ri, &the_dev->wi,
- &the_dev->state);
- wake_up_interruptible(&the_dev->wq_read);
- }
- static const struct file_operations tsif_fops = {
- .owner = THIS_MODULE,
- .read = tsif_read,
- .open = tsif_open,
- .release = tsif_release,
- };
- static struct class *tsif_class;
- static dev_t tsif_dev; /**< 1-st dev_t from allocated range */
- static dev_t tsif_dev0; /**< next not yet assigned dev_t */
- static int tsif_init_one(struct tsif_chrdev *the_dev, int index)
- {
- int rc;
- pr_info("%s[%d]\n", __func__, index);
- cdev_init(&the_dev->cdev, &tsif_fops);
- the_dev->cdev.owner = THIS_MODULE;
- init_waitqueue_head(&the_dev->wq_read);
- rc = cdev_add(&the_dev->cdev, tsif_dev0++, 1);
- the_dev->dev = device_create(tsif_class, NULL, the_dev->cdev.dev,
- the_dev, "tsif%d", index);
- if (IS_ERR(the_dev->dev)) {
- rc = PTR_ERR(the_dev->dev);
- pr_err("device_create failed: %d\n", rc);
- goto err_create;
- }
- the_dev->cookie = tsif_attach(index, tsif_notify, the_dev);
- if (IS_ERR(the_dev->cookie)) {
- rc = PTR_ERR(the_dev->cookie);
- pr_err("tsif_attach failed: %d\n", rc);
- goto err_attach;
- }
- /* now data buffer is not allocated yet */
- tsif_get_info(the_dev->cookie, &the_dev->data_buffer, NULL);
- dev_info(the_dev->dev,
- "Device %d.%d attached to TSIF, buffer size %d\n",
- MAJOR(the_dev->cdev.dev), MINOR(the_dev->cdev.dev),
- the_dev->buf_size_packets);
- return 0;
- err_attach:
- device_destroy(tsif_class, the_dev->cdev.dev);
- err_create:
- cdev_del(&the_dev->cdev);
- return rc;
- }
- static void tsif_exit_one(struct tsif_chrdev *the_dev)
- {
- dev_info(the_dev->dev, "%s\n", __func__);
- tsif_detach(the_dev->cookie);
- device_destroy(tsif_class, the_dev->cdev.dev);
- cdev_del(&the_dev->cdev);
- }
- #define TSIF_NUM_DEVS 1 /**< support this many devices */
- struct tsif_chrdev the_devices[TSIF_NUM_DEVS];
- static int __init mod_init(void)
- {
- int rc;
- int instance;
- rc = alloc_chrdev_region(&tsif_dev, 0, TSIF_NUM_DEVS, "tsif");
- if (rc) {
- pr_err("alloc_chrdev_region failed: %d\n", rc);
- goto err_devrgn;
- }
- tsif_dev0 = tsif_dev;
- tsif_class = class_create(THIS_MODULE, "tsif");
- if (IS_ERR(tsif_class)) {
- rc = PTR_ERR(tsif_class);
- pr_err("Error creating tsif class: %d\n", rc);
- goto err_class;
- }
- instance = tsif_get_active();
- if (instance >= 0)
- rc = tsif_init_one(&the_devices[0], instance);
- else
- rc = instance;
- if (rc)
- goto err_init1;
- return 0;
- err_init1:
- class_destroy(tsif_class);
- err_class:
- unregister_chrdev_region(tsif_dev, TSIF_NUM_DEVS);
- err_devrgn:
- return rc;
- }
- static void __exit mod_exit(void)
- {
- tsif_exit_one(&the_devices[0]);
- class_destroy(tsif_class);
- unregister_chrdev_region(tsif_dev, TSIF_NUM_DEVS);
- }
- module_init(mod_init);
- module_exit(mod_exit);
- MODULE_DESCRIPTION("TSIF character device interface");
- MODULE_LICENSE("GPL v2");
|