aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dsp/syslink/devh/devh.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dsp/syslink/devh/devh.c')
-rw-r--r--drivers/dsp/syslink/devh/devh.c273
1 files changed, 273 insertions, 0 deletions
diff --git a/drivers/dsp/syslink/devh/devh.c b/drivers/dsp/syslink/devh/devh.c
new file mode 100644
index 00000000000..0e3cabe665f
--- /dev/null
+++ b/drivers/dsp/syslink/devh/devh.c
@@ -0,0 +1,273 @@
1/*
2 * OMAP Device Handler driver
3 *
4 * Copyright (C) 2010 Texas Instruments Inc.
5 *
6 * Written by Angela Stegmaier <angelabaker@ti.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 *
22 */
23
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <linux/interrupt.h>
27#include <linux/device.h>
28#include <linux/delay.h>
29#include <linux/file.h>
30#include <linux/poll.h>
31#include <linux/mm.h>
32#include <linux/slab.h>
33#include <linux/platform_device.h>
34
35#include "devh.h"
36
37#define OMAP_DEVH_NAME "omap-devh"
38#define DRV_NAME "omap-devicehandler"
39
40static struct class *omap_devh_class;
41static dev_t omap_devh_dev;
42static atomic_t num_of_devhs;
43static struct platform_driver omap_devh_driver;
44
45static int omap_devh_open(struct inode *inode, struct file *filp)
46{
47 int ret = 0;
48 struct omap_devh *devh;
49
50 devh = container_of(inode->i_cdev, struct omap_devh, cdev);
51 if (!devh->dev)
52 return -EINVAL;
53
54 filp->private_data = devh;
55
56 return ret;
57}
58
59static int omap_devh_release(struct inode *inode, struct file *filp)
60{
61 struct omap_devh *devh = filp->private_data;
62 if (!devh || !devh->dev)
63 return -EINVAL;
64
65 return 0;
66}
67
68static int omap_devh_ioctl(struct inode *inode, struct file *filp,
69 unsigned int cmd, unsigned long arg)
70{
71 int rc = 0;
72 struct omap_devh *devh = filp->private_data;
73 struct omap_devh_platform_data *pdata =
74 (struct omap_devh_platform_data *)devh->dev->platform_data;
75
76 if (!devh)
77 return -EINVAL;
78
79 if (_IOC_TYPE(cmd) != DEVH_IOC_MAGIC)
80 return -ENOTTY;
81 if (_IOC_NR(cmd) > DEVH_IOC_MAXNR)
82 return -ENOTTY;
83 if (_IOC_DIR(cmd) & _IOC_READ) {
84 if (!access_ok(VERIFY_WRITE, (void __user *)arg,
85 _IOC_SIZE(cmd)))
86 return -EFAULT;
87 } else if (_IOC_DIR(cmd) & _IOC_WRITE) {
88 if (!access_ok(VERIFY_READ, (void __user *)arg,
89 _IOC_SIZE(cmd)))
90 return -EFAULT;
91 }
92
93 switch (cmd) {
94
95 /*
96 * this will be updated to support user registering for event
97 * notifications.
98 */
99 case DEVH_IOCWAITONEVENTS:
100 /*rc = omap_devh_wait_on_events(devh);*/
101 break;
102 case DEVH_IOCEVENTREG:
103 rc = pdata->ops->register_event_notification(devh,
104 (const void __user *)arg);
105 break;
106 case DEVH_IOCEVENTUNREG:
107 rc = pdata->ops->unregister_event_notification(devh,
108 (const void __user *)arg);
109 break;
110 default:
111 return -ENOTTY;
112 }
113
114 return rc;
115}
116
117static const struct file_operations omap_devh_fops = {
118 .open = omap_devh_open,
119 .release = omap_devh_release,
120 .ioctl = omap_devh_ioctl,
121 .owner = THIS_MODULE,
122};
123
124static int omap_devh_probe(struct platform_device *pdev)
125{
126 int ret = 0, major, minor;
127 struct device *tmpdev;
128 struct device *dev = &pdev->dev;
129 struct omap_devh_platform_data *pdata = dev->platform_data;
130 struct omap_devh *devh;
131
132 if (!pdata || !pdata->name || !pdata->ops)
133 return -EINVAL;
134
135 dev_info(dev, "%s: adding devh %s\n", __func__, pdata->name);
136
137 devh = kzalloc(sizeof(struct omap_devh), GFP_KERNEL);
138 if (!devh) {
139 dev_err(dev, "%s: kzalloc failed\n", __func__);
140 ret = -ENOMEM;
141 goto out;
142 }
143
144 platform_set_drvdata(pdev, devh);
145 major = MAJOR(omap_devh_dev);
146 minor = atomic_read(&num_of_devhs);
147 atomic_inc(&num_of_devhs);
148
149 devh->dev = dev;
150 devh->minor = minor;
151 devh->name = pdata->name;
152
153 cdev_init(&devh->cdev, &omap_devh_fops);
154 devh->cdev.owner = THIS_MODULE;
155 ret = cdev_add(&devh->cdev, MKDEV(major, minor), 1);
156 if (ret) {
157 dev_err(dev, "%s: cdev_add failed: %d\n", __func__, ret);
158 goto free_devh;
159 }
160
161 tmpdev = device_create(omap_devh_class, NULL,
162 MKDEV(major, minor),
163 NULL,
164 OMAP_DEVH_NAME "%d", minor);
165 if (IS_ERR(tmpdev)) {
166 ret = PTR_ERR(tmpdev);
167 pr_err("%s: device_create failed: %d\n", __func__, ret);
168 goto clean_cdev;
169 }
170
171 pr_info("%s initialized %s, major: %d, base-minor: %d\n",
172 OMAP_DEVH_NAME,
173 pdata->name,
174 MAJOR(omap_devh_dev),
175 minor);
176
177 INIT_LIST_HEAD(&(devh->event_list));
178 spin_lock_init(&(devh->event_lock));
179 if (pdata->ops->register_notifiers)
180 pdata->ops->register_notifiers(devh);
181
182 return 0;
183
184clean_cdev:
185 cdev_del(&devh->cdev);
186free_devh:
187 kfree(devh);
188out:
189 return ret;
190}
191
192static int omap_devh_remove(struct platform_device *pdev)
193{
194 int major = MAJOR(omap_devh_dev);
195 struct device *dev = &pdev->dev;
196 struct omap_devh_platform_data *pdata = dev->platform_data;
197 struct omap_devh *devh = platform_get_drvdata(pdev);
198
199 if (!pdata || !devh)
200 return -EINVAL;
201
202 if (pdata->ops->unregister_notifiers)
203 pdata->ops->unregister_notifiers(devh);
204
205 dev_info(dev, "%s removing %s, major: %d, base-minor: %d\n",
206 OMAP_DEVH_NAME,
207 pdata->name,
208 major,
209 devh->minor);
210
211 device_destroy(omap_devh_class, MKDEV(major, devh->minor));
212 cdev_del(&devh->cdev);
213 kfree(devh);
214
215 return 0;
216}
217
218static struct platform_driver omap_devh_driver = {
219 .probe = omap_devh_probe,
220 .remove = omap_devh_remove,
221 .driver = {
222 .name = DRV_NAME,
223 .owner = THIS_MODULE,
224 },
225};
226
227static int __init omap_devh_init(void)
228{
229 int num = devh_get_plat_data_size();
230 int ret;
231
232 ret = alloc_chrdev_region(&omap_devh_dev, 0, num, OMAP_DEVH_NAME);
233 if (ret) {
234 pr_err("%s: alloc_chrdev_region failed: %d\n", __func__, ret);
235 goto out;
236 }
237
238 omap_devh_class = class_create(THIS_MODULE, OMAP_DEVH_NAME);
239 if (IS_ERR(omap_devh_class)) {
240 ret = PTR_ERR(omap_devh_class);
241 pr_err("%s: class_create failed: %d\n", __func__, ret);
242 goto unreg_region;
243 }
244
245 atomic_set(&num_of_devhs, 0);
246
247 ret = platform_driver_register(&omap_devh_driver);
248 if (ret) {
249 pr_err("%s: platform_driver_register failed: %d\n",
250 __func__, ret);
251 goto out;
252 }
253 return 0;
254unreg_region:
255 unregister_chrdev_region(omap_devh_dev, num);
256out:
257 return ret;
258}
259module_init(omap_devh_init);
260
261static void __exit omap_devh_exit(void)
262{
263 int num = devh_get_plat_data_size();
264 pr_info("%s\n", __func__);
265 platform_driver_unregister(&omap_devh_driver);
266 class_destroy(omap_devh_class);
267 unregister_chrdev_region(omap_devh_dev, num);
268}
269module_exit(omap_devh_exit);
270
271MODULE_LICENSE("GPL v2");
272MODULE_DESCRIPTION("OMAP Device Handler driver");
273MODULE_AUTHOR("Angela Stegmaier <angelabaker@ti.com>");