diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/media/dvb/dvb-core/dvbdev.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/media/dvb/dvb-core/dvbdev.c')
-rw-r--r-- | drivers/media/dvb/dvb-core/dvbdev.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c new file mode 100644 index 000000000000..cf4ffe38fda3 --- /dev/null +++ b/drivers/media/dvb/dvb-core/dvbdev.c | |||
@@ -0,0 +1,449 @@ | |||
1 | /* | ||
2 | * dvbdev.c | ||
3 | * | ||
4 | * Copyright (C) 2000 Ralph Metzler <ralph@convergence.de> | ||
5 | * & Marcus Metzler <marcus@convergence.de> | ||
6 | * for convergence integrated media GmbH | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU Lesser General Public License | ||
10 | * as published by the Free Software Foundation; either version 2.1 | ||
11 | * of the License, or (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU Lesser General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <linux/types.h> | ||
25 | #include <linux/errno.h> | ||
26 | #include <linux/string.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/moduleparam.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/sched.h> | ||
31 | #include <linux/init.h> | ||
32 | #include <linux/slab.h> | ||
33 | #include <linux/device.h> | ||
34 | #include <linux/fs.h> | ||
35 | #include <linux/cdev.h> | ||
36 | |||
37 | #include "dvbdev.h" | ||
38 | |||
39 | static int dvbdev_debug; | ||
40 | |||
41 | module_param(dvbdev_debug, int, 0644); | ||
42 | MODULE_PARM_DESC(dvbdev_debug, "Turn on/off device debugging (default:off)."); | ||
43 | |||
44 | #define dprintk if (dvbdev_debug) printk | ||
45 | |||
46 | static LIST_HEAD(dvb_adapter_list); | ||
47 | static DECLARE_MUTEX(dvbdev_register_lock); | ||
48 | |||
49 | static const char * const dnames[] = { | ||
50 | "video", "audio", "sec", "frontend", "demux", "dvr", "ca", | ||
51 | "net", "osd" | ||
52 | }; | ||
53 | |||
54 | #define DVB_MAX_ADAPTERS 8 | ||
55 | #define DVB_MAX_IDS 4 | ||
56 | #define nums2minor(num,type,id) ((num << 6) | (id << 4) | type) | ||
57 | #define MAX_DVB_MINORS (DVB_MAX_ADAPTERS*64) | ||
58 | |||
59 | struct class_simple *dvb_class; | ||
60 | EXPORT_SYMBOL(dvb_class); | ||
61 | |||
62 | static struct dvb_device* dvbdev_find_device (int minor) | ||
63 | { | ||
64 | struct list_head *entry; | ||
65 | |||
66 | list_for_each (entry, &dvb_adapter_list) { | ||
67 | struct list_head *entry0; | ||
68 | struct dvb_adapter *adap; | ||
69 | adap = list_entry (entry, struct dvb_adapter, list_head); | ||
70 | list_for_each (entry0, &adap->device_list) { | ||
71 | struct dvb_device *dev; | ||
72 | dev = list_entry (entry0, struct dvb_device, list_head); | ||
73 | if (nums2minor(adap->num, dev->type, dev->id) == minor) | ||
74 | return dev; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | return NULL; | ||
79 | } | ||
80 | |||
81 | |||
82 | static int dvb_device_open(struct inode *inode, struct file *file) | ||
83 | { | ||
84 | struct dvb_device *dvbdev; | ||
85 | |||
86 | dvbdev = dvbdev_find_device (iminor(inode)); | ||
87 | |||
88 | if (dvbdev && dvbdev->fops) { | ||
89 | int err = 0; | ||
90 | struct file_operations *old_fops; | ||
91 | |||
92 | file->private_data = dvbdev; | ||
93 | old_fops = file->f_op; | ||
94 | file->f_op = fops_get(dvbdev->fops); | ||
95 | if(file->f_op->open) | ||
96 | err = file->f_op->open(inode,file); | ||
97 | if (err) { | ||
98 | fops_put(file->f_op); | ||
99 | file->f_op = fops_get(old_fops); | ||
100 | } | ||
101 | fops_put(old_fops); | ||
102 | return err; | ||
103 | } | ||
104 | return -ENODEV; | ||
105 | } | ||
106 | |||
107 | |||
108 | static struct file_operations dvb_device_fops = | ||
109 | { | ||
110 | .owner = THIS_MODULE, | ||
111 | .open = dvb_device_open, | ||
112 | }; | ||
113 | |||
114 | static struct cdev dvb_device_cdev = { | ||
115 | .kobj = {.name = "dvb", }, | ||
116 | .owner = THIS_MODULE, | ||
117 | }; | ||
118 | |||
119 | int dvb_generic_open(struct inode *inode, struct file *file) | ||
120 | { | ||
121 | struct dvb_device *dvbdev = file->private_data; | ||
122 | |||
123 | if (!dvbdev) | ||
124 | return -ENODEV; | ||
125 | |||
126 | if (!dvbdev->users) | ||
127 | return -EBUSY; | ||
128 | |||
129 | if ((file->f_flags & O_ACCMODE) == O_RDONLY) { | ||
130 | if (!dvbdev->readers) | ||
131 | return -EBUSY; | ||
132 | dvbdev->readers--; | ||
133 | } else { | ||
134 | if (!dvbdev->writers) | ||
135 | return -EBUSY; | ||
136 | dvbdev->writers--; | ||
137 | } | ||
138 | |||
139 | dvbdev->users--; | ||
140 | return 0; | ||
141 | } | ||
142 | EXPORT_SYMBOL(dvb_generic_open); | ||
143 | |||
144 | |||
145 | int dvb_generic_release(struct inode *inode, struct file *file) | ||
146 | { | ||
147 | struct dvb_device *dvbdev = file->private_data; | ||
148 | |||
149 | if (!dvbdev) | ||
150 | return -ENODEV; | ||
151 | |||
152 | if ((file->f_flags & O_ACCMODE) == O_RDONLY) { | ||
153 | dvbdev->readers++; | ||
154 | } else { | ||
155 | dvbdev->writers++; | ||
156 | } | ||
157 | |||
158 | dvbdev->users++; | ||
159 | return 0; | ||
160 | } | ||
161 | EXPORT_SYMBOL(dvb_generic_release); | ||
162 | |||
163 | |||
164 | int dvb_generic_ioctl(struct inode *inode, struct file *file, | ||
165 | unsigned int cmd, unsigned long arg) | ||
166 | { | ||
167 | struct dvb_device *dvbdev = file->private_data; | ||
168 | |||
169 | if (!dvbdev) | ||
170 | return -ENODEV; | ||
171 | |||
172 | if (!dvbdev->kernel_ioctl) | ||
173 | return -EINVAL; | ||
174 | |||
175 | return dvb_usercopy (inode, file, cmd, arg, dvbdev->kernel_ioctl); | ||
176 | } | ||
177 | EXPORT_SYMBOL(dvb_generic_ioctl); | ||
178 | |||
179 | |||
180 | static int dvbdev_get_free_id (struct dvb_adapter *adap, int type) | ||
181 | { | ||
182 | u32 id = 0; | ||
183 | |||
184 | while (id < DVB_MAX_IDS) { | ||
185 | struct list_head *entry; | ||
186 | list_for_each (entry, &adap->device_list) { | ||
187 | struct dvb_device *dev; | ||
188 | dev = list_entry (entry, struct dvb_device, list_head); | ||
189 | if (dev->type == type && dev->id == id) | ||
190 | goto skip; | ||
191 | } | ||
192 | return id; | ||
193 | skip: | ||
194 | id++; | ||
195 | } | ||
196 | return -ENFILE; | ||
197 | } | ||
198 | |||
199 | |||
200 | int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, | ||
201 | const struct dvb_device *template, void *priv, int type) | ||
202 | { | ||
203 | struct dvb_device *dvbdev; | ||
204 | int id; | ||
205 | |||
206 | if (down_interruptible (&dvbdev_register_lock)) | ||
207 | return -ERESTARTSYS; | ||
208 | |||
209 | if ((id = dvbdev_get_free_id (adap, type)) < 0) { | ||
210 | up (&dvbdev_register_lock); | ||
211 | *pdvbdev = NULL; | ||
212 | printk ("%s: could get find free device id...\n", __FUNCTION__); | ||
213 | return -ENFILE; | ||
214 | } | ||
215 | |||
216 | *pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL); | ||
217 | |||
218 | if (!dvbdev) { | ||
219 | up(&dvbdev_register_lock); | ||
220 | return -ENOMEM; | ||
221 | } | ||
222 | |||
223 | up (&dvbdev_register_lock); | ||
224 | |||
225 | memcpy(dvbdev, template, sizeof(struct dvb_device)); | ||
226 | dvbdev->type = type; | ||
227 | dvbdev->id = id; | ||
228 | dvbdev->adapter = adap; | ||
229 | dvbdev->priv = priv; | ||
230 | |||
231 | dvbdev->fops->owner = adap->module; | ||
232 | |||
233 | list_add_tail (&dvbdev->list_head, &adap->device_list); | ||
234 | |||
235 | devfs_mk_cdev(MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)), | ||
236 | S_IFCHR | S_IRUSR | S_IWUSR, | ||
237 | "dvb/adapter%d/%s%d", adap->num, dnames[type], id); | ||
238 | |||
239 | class_simple_device_add(dvb_class, MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)), | ||
240 | NULL, "dvb%d.%s%d", adap->num, dnames[type], id); | ||
241 | |||
242 | dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n", | ||
243 | adap->num, dnames[type], id, nums2minor(adap->num, type, id), | ||
244 | nums2minor(adap->num, type, id)); | ||
245 | |||
246 | return 0; | ||
247 | } | ||
248 | EXPORT_SYMBOL(dvb_register_device); | ||
249 | |||
250 | |||
251 | void dvb_unregister_device(struct dvb_device *dvbdev) | ||
252 | { | ||
253 | if (!dvbdev) | ||
254 | return; | ||
255 | |||
256 | devfs_remove("dvb/adapter%d/%s%d", dvbdev->adapter->num, | ||
257 | dnames[dvbdev->type], dvbdev->id); | ||
258 | |||
259 | class_simple_device_remove(MKDEV(DVB_MAJOR, nums2minor(dvbdev->adapter->num, | ||
260 | dvbdev->type, dvbdev->id))); | ||
261 | |||
262 | list_del (&dvbdev->list_head); | ||
263 | kfree (dvbdev); | ||
264 | } | ||
265 | EXPORT_SYMBOL(dvb_unregister_device); | ||
266 | |||
267 | |||
268 | static int dvbdev_get_free_adapter_num (void) | ||
269 | { | ||
270 | int num = 0; | ||
271 | |||
272 | while (num < DVB_MAX_ADAPTERS) { | ||
273 | struct list_head *entry; | ||
274 | list_for_each (entry, &dvb_adapter_list) { | ||
275 | struct dvb_adapter *adap; | ||
276 | adap = list_entry (entry, struct dvb_adapter, list_head); | ||
277 | if (adap->num == num) | ||
278 | goto skip; | ||
279 | } | ||
280 | return num; | ||
281 | skip: | ||
282 | num++; | ||
283 | } | ||
284 | |||
285 | return -ENFILE; | ||
286 | } | ||
287 | |||
288 | |||
289 | int dvb_register_adapter(struct dvb_adapter **padap, const char *name, struct module *module) | ||
290 | { | ||
291 | struct dvb_adapter *adap; | ||
292 | int num; | ||
293 | |||
294 | if (down_interruptible (&dvbdev_register_lock)) | ||
295 | return -ERESTARTSYS; | ||
296 | |||
297 | if ((num = dvbdev_get_free_adapter_num ()) < 0) { | ||
298 | up (&dvbdev_register_lock); | ||
299 | return -ENFILE; | ||
300 | } | ||
301 | |||
302 | if (!(*padap = adap = kmalloc(sizeof(struct dvb_adapter), GFP_KERNEL))) { | ||
303 | up(&dvbdev_register_lock); | ||
304 | return -ENOMEM; | ||
305 | } | ||
306 | |||
307 | memset (adap, 0, sizeof(struct dvb_adapter)); | ||
308 | INIT_LIST_HEAD (&adap->device_list); | ||
309 | |||
310 | printk ("DVB: registering new adapter (%s).\n", name); | ||
311 | |||
312 | devfs_mk_dir("dvb/adapter%d", num); | ||
313 | adap->num = num; | ||
314 | adap->name = name; | ||
315 | adap->module = module; | ||
316 | |||
317 | list_add_tail (&adap->list_head, &dvb_adapter_list); | ||
318 | |||
319 | up (&dvbdev_register_lock); | ||
320 | |||
321 | return num; | ||
322 | } | ||
323 | EXPORT_SYMBOL(dvb_register_adapter); | ||
324 | |||
325 | |||
326 | int dvb_unregister_adapter(struct dvb_adapter *adap) | ||
327 | { | ||
328 | devfs_remove("dvb/adapter%d", adap->num); | ||
329 | |||
330 | if (down_interruptible (&dvbdev_register_lock)) | ||
331 | return -ERESTARTSYS; | ||
332 | list_del (&adap->list_head); | ||
333 | up (&dvbdev_register_lock); | ||
334 | kfree (adap); | ||
335 | return 0; | ||
336 | } | ||
337 | EXPORT_SYMBOL(dvb_unregister_adapter); | ||
338 | |||
339 | /* if the miracle happens and "generic_usercopy()" is included into | ||
340 | the kernel, then this can vanish. please don't make the mistake and | ||
341 | define this as video_usercopy(). this will introduce a dependecy | ||
342 | to the v4l "videodev.o" module, which is unnecessary for some | ||
343 | cards (ie. the budget dvb-cards don't need the v4l module...) */ | ||
344 | int dvb_usercopy(struct inode *inode, struct file *file, | ||
345 | unsigned int cmd, unsigned long arg, | ||
346 | int (*func)(struct inode *inode, struct file *file, | ||
347 | unsigned int cmd, void *arg)) | ||
348 | { | ||
349 | char sbuf[128]; | ||
350 | void *mbuf = NULL; | ||
351 | void *parg = NULL; | ||
352 | int err = -EINVAL; | ||
353 | |||
354 | /* Copy arguments into temp kernel buffer */ | ||
355 | switch (_IOC_DIR(cmd)) { | ||
356 | case _IOC_NONE: | ||
357 | /* | ||
358 | * For this command, the pointer is actually an integer | ||
359 | * argument. | ||
360 | */ | ||
361 | parg = (void *) arg; | ||
362 | break; | ||
363 | case _IOC_READ: /* some v4l ioctls are marked wrong ... */ | ||
364 | case _IOC_WRITE: | ||
365 | case (_IOC_WRITE | _IOC_READ): | ||
366 | if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { | ||
367 | parg = sbuf; | ||
368 | } else { | ||
369 | /* too big to allocate from stack */ | ||
370 | mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL); | ||
371 | if (NULL == mbuf) | ||
372 | return -ENOMEM; | ||
373 | parg = mbuf; | ||
374 | } | ||
375 | |||
376 | err = -EFAULT; | ||
377 | if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) | ||
378 | goto out; | ||
379 | break; | ||
380 | } | ||
381 | |||
382 | /* call driver */ | ||
383 | if ((err = func(inode, file, cmd, parg)) == -ENOIOCTLCMD) | ||
384 | err = -EINVAL; | ||
385 | |||
386 | if (err < 0) | ||
387 | goto out; | ||
388 | |||
389 | /* Copy results into user buffer */ | ||
390 | switch (_IOC_DIR(cmd)) | ||
391 | { | ||
392 | case _IOC_READ: | ||
393 | case (_IOC_WRITE | _IOC_READ): | ||
394 | if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) | ||
395 | err = -EFAULT; | ||
396 | break; | ||
397 | } | ||
398 | |||
399 | out: | ||
400 | kfree(mbuf); | ||
401 | return err; | ||
402 | } | ||
403 | |||
404 | static int __init init_dvbdev(void) | ||
405 | { | ||
406 | int retval; | ||
407 | dev_t dev = MKDEV(DVB_MAJOR, 0); | ||
408 | |||
409 | if ((retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB")) != 0) { | ||
410 | printk("dvb-core: unable to get major %d\n", DVB_MAJOR); | ||
411 | return retval; | ||
412 | } | ||
413 | |||
414 | cdev_init(&dvb_device_cdev, &dvb_device_fops); | ||
415 | if ((retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS)) != 0) { | ||
416 | printk("dvb-core: unable to get major %d\n", DVB_MAJOR); | ||
417 | goto error; | ||
418 | } | ||
419 | |||
420 | devfs_mk_dir("dvb"); | ||
421 | |||
422 | dvb_class = class_simple_create(THIS_MODULE, "dvb"); | ||
423 | if (IS_ERR(dvb_class)) { | ||
424 | retval = PTR_ERR(dvb_class); | ||
425 | goto error; | ||
426 | } | ||
427 | return 0; | ||
428 | |||
429 | error: | ||
430 | cdev_del(&dvb_device_cdev); | ||
431 | unregister_chrdev_region(dev, MAX_DVB_MINORS); | ||
432 | return retval; | ||
433 | } | ||
434 | |||
435 | |||
436 | static void __exit exit_dvbdev(void) | ||
437 | { | ||
438 | devfs_remove("dvb"); | ||
439 | class_simple_destroy(dvb_class); | ||
440 | cdev_del(&dvb_device_cdev); | ||
441 | unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS); | ||
442 | } | ||
443 | |||
444 | module_init(init_dvbdev); | ||
445 | module_exit(exit_dvbdev); | ||
446 | |||
447 | MODULE_DESCRIPTION("DVB Core Driver"); | ||
448 | MODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler"); | ||
449 | MODULE_LICENSE("GPL"); | ||