blob: a53bc9a2825c0b0b7c61307637848a96a09288d2 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/**
2 * TSIF driver client
3 *
4 * Character device that, being read
5 * returns stream of TSIF packets.
6 *
Duy Truong790f06d2013-02-13 16:38:12 -08007 * Copyright (c) 2009-2011, The Linux Foundation. All rights
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07008 * reserved.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 and
12 * only version 2 as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
19
20#include <linux/module.h> /* Needed by all modules */
21#include <linux/kernel.h> /* Needed for KERN_INFO */
22#include <linux/cdev.h>
23#include <linux/err.h> /* IS_ERR etc. */
24#include <linux/fs.h>
25#include <linux/device.h>
26#include <linux/sched.h> /* TASK_INTERRUPTIBLE */
27
28#include <linux/uaccess.h> /* copy_to_user */
29
30#include <linux/tsif_api.h>
31
32struct tsif_chrdev {
33 struct cdev cdev;
34 struct device *dev;
35 wait_queue_head_t wq_read;
36 void *cookie;
37 /* mirror for tsif data */
38 void *data_buffer;
39 unsigned buf_size_packets; /**< buffer size in packets */
40 unsigned ri, wi;
41 enum tsif_state state;
42 unsigned rptr;
43};
44
45static ssize_t tsif_open(struct inode *inode, struct file *file)
46{
47 int rc;
48 struct tsif_chrdev *the_dev =
49 container_of(inode->i_cdev, struct tsif_chrdev, cdev);
50 if (!the_dev->cookie) /* not bound yet */
51 return -ENODEV;
52 file->private_data = the_dev;
53 rc = tsif_start(the_dev->cookie);
54 if (rc)
55 return rc;
56 tsif_get_info(the_dev->cookie, &the_dev->data_buffer,
57 &the_dev->buf_size_packets);
58 the_dev->rptr = 0;
59 return nonseekable_open(inode, file);
60}
61
62static ssize_t tsif_release(struct inode *inode, struct file *filp)
63{
64 struct tsif_chrdev *the_dev = filp->private_data;
65 tsif_stop(the_dev->cookie);
66 return 0;
67}
68
69static ssize_t tsif_read(struct file *filp, char __user *buf, size_t count,
70 loff_t *f_pos)
71{
72 int avail = 0;
73 int wi;
74 struct tsif_chrdev *the_dev = filp->private_data;
75 tsif_get_state(the_dev->cookie, &the_dev->ri, &the_dev->wi,
76 &the_dev->state);
77 /* consistency check */
78 if (the_dev->ri != (the_dev->rptr / TSIF_PKT_SIZE)) {
79 dev_err(the_dev->dev,
80 "%s: inconsistent read pointers: ri %d rptr %d\n",
81 __func__, the_dev->ri, the_dev->rptr);
82 the_dev->rptr = the_dev->ri * TSIF_PKT_SIZE;
83 }
84 /* ri == wi if no data */
85 if (the_dev->ri == the_dev->wi) {
86 /* shall I block waiting for data? */
87 if (filp->f_flags & O_NONBLOCK) {
88 if (the_dev->state == tsif_state_running) {
89 return -EAGAIN;
90 } else {
91 /* not running -> EOF */
92 return 0;
93 }
94 }
95 if (wait_event_interruptible(the_dev->wq_read,
96 (the_dev->ri != the_dev->wi) ||
97 (the_dev->state != tsif_state_running))) {
98 /* got signal -> tell FS to handle it */
99 return -ERESTARTSYS;
100 }
101 if (the_dev->ri == the_dev->wi) {
102 /* still no data -> EOF */
103 return 0;
104 }
105 }
106 /* contiguous chunk last up to wi or end of buffer */
107 wi = (the_dev->wi > the_dev->ri) ?
108 the_dev->wi : the_dev->buf_size_packets;
109 avail = min(wi * TSIF_PKT_SIZE - the_dev->rptr, count);
110 if (copy_to_user(buf, the_dev->data_buffer + the_dev->rptr, avail))
111 return -EFAULT;
112 the_dev->rptr = (the_dev->rptr + avail) %
113 (TSIF_PKT_SIZE * the_dev->buf_size_packets);
114 the_dev->ri = the_dev->rptr / TSIF_PKT_SIZE;
115 *f_pos += avail;
116 tsif_reclaim_packets(the_dev->cookie, the_dev->ri);
117 return avail;
118}
119
120static void tsif_notify(void *data)
121{
122 struct tsif_chrdev *the_dev = data;
123 tsif_get_state(the_dev->cookie, &the_dev->ri, &the_dev->wi,
124 &the_dev->state);
125 wake_up_interruptible(&the_dev->wq_read);
126}
127
128static const struct file_operations tsif_fops = {
129 .owner = THIS_MODULE,
130 .read = tsif_read,
131 .open = tsif_open,
132 .release = tsif_release,
133};
134
135static struct class *tsif_class;
136static dev_t tsif_dev; /**< 1-st dev_t from allocated range */
137static dev_t tsif_dev0; /**< next not yet assigned dev_t */
138
139static int tsif_init_one(struct tsif_chrdev *the_dev, int index)
140{
141 int rc;
142 pr_info("%s[%d]\n", __func__, index);
143 cdev_init(&the_dev->cdev, &tsif_fops);
144 the_dev->cdev.owner = THIS_MODULE;
145 init_waitqueue_head(&the_dev->wq_read);
146 rc = cdev_add(&the_dev->cdev, tsif_dev0++, 1);
147 the_dev->dev = device_create(tsif_class, NULL, the_dev->cdev.dev,
148 the_dev, "tsif%d", index);
149 if (IS_ERR(the_dev->dev)) {
150 rc = PTR_ERR(the_dev->dev);
151 pr_err("device_create failed: %d\n", rc);
152 goto err_create;
153 }
154 the_dev->cookie = tsif_attach(index, tsif_notify, the_dev);
155 if (IS_ERR(the_dev->cookie)) {
156 rc = PTR_ERR(the_dev->cookie);
157 pr_err("tsif_attach failed: %d\n", rc);
158 goto err_attach;
159 }
160 /* now data buffer is not allocated yet */
161 tsif_get_info(the_dev->cookie, &the_dev->data_buffer, NULL);
162 dev_info(the_dev->dev,
163 "Device %d.%d attached to TSIF, buffer size %d\n",
164 MAJOR(the_dev->cdev.dev), MINOR(the_dev->cdev.dev),
165 the_dev->buf_size_packets);
166 return 0;
167err_attach:
168 device_destroy(tsif_class, the_dev->cdev.dev);
169err_create:
170 cdev_del(&the_dev->cdev);
171 return rc;
172}
173
174static void tsif_exit_one(struct tsif_chrdev *the_dev)
175{
176 dev_info(the_dev->dev, "%s\n", __func__);
177 tsif_detach(the_dev->cookie);
178 device_destroy(tsif_class, the_dev->cdev.dev);
179 cdev_del(&the_dev->cdev);
180}
181
182#define TSIF_NUM_DEVS 1 /**< support this many devices */
183
184struct tsif_chrdev the_devices[TSIF_NUM_DEVS];
185
186static int __init mod_init(void)
187{
188 int rc;
Joel Nider5578bdb2011-08-12 09:37:11 +0300189 int instance;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700190 rc = alloc_chrdev_region(&tsif_dev, 0, TSIF_NUM_DEVS, "tsif");
191 if (rc) {
192 pr_err("alloc_chrdev_region failed: %d\n", rc);
193 goto err_devrgn;
194 }
195 tsif_dev0 = tsif_dev;
196 tsif_class = class_create(THIS_MODULE, "tsif");
197 if (IS_ERR(tsif_class)) {
198 rc = PTR_ERR(tsif_class);
199 pr_err("Error creating tsif class: %d\n", rc);
200 goto err_class;
201 }
Joel Nider5578bdb2011-08-12 09:37:11 +0300202 instance = tsif_get_active();
203 if (instance >= 0)
204 rc = tsif_init_one(&the_devices[0], instance);
205 else
206 rc = instance;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700207 if (rc)
208 goto err_init1;
209 return 0;
210err_init1:
211 class_destroy(tsif_class);
212err_class:
213 unregister_chrdev_region(tsif_dev, TSIF_NUM_DEVS);
214err_devrgn:
215 return rc;
216}
217
218static void __exit mod_exit(void)
219{
220 tsif_exit_one(&the_devices[0]);
221 class_destroy(tsif_class);
222 unregister_chrdev_region(tsif_dev, TSIF_NUM_DEVS);
223}
224
225module_init(mod_init);
226module_exit(mod_exit);
227
228MODULE_DESCRIPTION("TSIF character device interface");
229MODULE_LICENSE("GPL v2");
230