11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * dvbdev.c
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2000 Ralph  Metzler <ralph@convergence.de>
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *                  & Marcus Metzler <marcus@convergence.de>
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *                    for convergence integrated media GmbH
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * modify it under the terms of the GNU Lesser General Public License
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * as published by the Free Software Foundation; either version 2.1
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * of the License, or (at your option) any later version.
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful,
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details.
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU Lesser General Public License
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/device.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fs.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/cdev.h>
341e4baed379a2bff3c728ea34602d3432fb047af8Ingo Molnar#include <linux/mutex.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "dvbdev.h"
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3772024f1ec5164a70d84dd8cf4458fe4064a6b692Arnd Bergmannstatic DEFINE_MUTEX(dvbdev_mutex);
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvbdev_debug;
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(dvbdev_debug, int, 0644);
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(dvbdev_debug, "Turn on/off device debugging (default:off).");
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define dprintk if (dvbdev_debug) printk
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic LIST_HEAD(dvb_adapter_list);
461e4baed379a2bff3c728ea34602d3432fb047af8Ingo Molnarstatic DEFINE_MUTEX(dvbdev_register_lock);
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char * const dnames[] = {
49afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	"video", "audio", "sec", "frontend", "demux", "dvr", "ca",
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"net", "osd"
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
535dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter#ifdef CONFIG_DVB_DYNAMIC_MINORS
545dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter#define MAX_DVB_MINORS		256
555dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter#define DVB_MAX_IDS		MAX_DVB_MINORS
565dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter#else
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DVB_MAX_IDS		4
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define nums2minor(num,type,id)	((num << 6) | (id << 4) | type)
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MAX_DVB_MINORS		(DVB_MAX_ADAPTERS*64)
605dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter#endif
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6256b2293595b2eb52cc2aa2baf92c6cfa8265f9d5Greg Kroah-Hartmanstatic struct class *dvb_class;
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
645dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritterstatic struct dvb_device *dvb_minors[MAX_DVB_MINORS];
655dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritterstatic DECLARE_RWSEM(minor_rwsem);
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_device_open(struct inode *inode, struct file *file)
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dvb_device *dvbdev;
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7172024f1ec5164a70d84dd8cf4458fe4064a6b692Arnd Bergmann	mutex_lock(&dvbdev_mutex);
725dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	down_read(&minor_rwsem);
735dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	dvbdev = dvb_minors[iminor(inode)];
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dvbdev && dvbdev->fops) {
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int err = 0;
7799ac48f54a91d02140c497edc31dc57d4bc5c85dArjan van de Ven		const struct file_operations *old_fops;
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		file->private_data = dvbdev;
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		old_fops = file->f_op;
81afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		file->f_op = fops_get(dvbdev->fops);
82f41ced8f108cc80f16509b907cd7ac93944459bcLaurent Pinchart		if (file->f_op == NULL) {
83f41ced8f108cc80f16509b907cd7ac93944459bcLaurent Pinchart			file->f_op = old_fops;
84f41ced8f108cc80f16509b907cd7ac93944459bcLaurent Pinchart			goto fail;
85f41ced8f108cc80f16509b907cd7ac93944459bcLaurent Pinchart		}
86afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		if(file->f_op->open)
8750c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			err = file->f_op->open(inode,file);
88afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		if (err) {
8950c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			fops_put(file->f_op);
9050c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			file->f_op = fops_get(old_fops);
91afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		}
92afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		fops_put(old_fops);
935dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter		up_read(&minor_rwsem);
9472024f1ec5164a70d84dd8cf4458fe4064a6b692Arnd Bergmann		mutex_unlock(&dvbdev_mutex);
95afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		return err;
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
97f41ced8f108cc80f16509b907cd7ac93944459bcLaurent Pinchartfail:
985dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	up_read(&minor_rwsem);
9972024f1ec5164a70d84dd8cf4458fe4064a6b692Arnd Bergmann	mutex_unlock(&dvbdev_mutex);
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENODEV;
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10427a643b1a9eded6a1b54df2743a7fb4383fd0cf9Jan Engelhardtstatic const struct file_operations dvb_device_fops =
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner =	THIS_MODULE,
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =		dvb_device_open,
1086038f373a3dc1f1c26496e60b6c40b164716f07eArnd Bergmann	.llseek =	noop_llseek,
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1117e7654a92a5a75a350cc3458389bc76273498f6aGreg Kroah-Hartmanstatic struct cdev dvb_device_cdev;
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_generic_open(struct inode *inode, struct file *file)
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
115afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	struct dvb_device *dvbdev = file->private_data;
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
117afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	if (!dvbdev)
118afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		return -ENODEV;
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dvbdev->users)
121afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		return -EBUSY;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
124afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		if (!dvbdev->readers)
125afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab			return -EBUSY;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvbdev->readers--;
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
128afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		if (!dvbdev->writers)
129afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab			return -EBUSY;
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvbdev->writers--;
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->users--;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_generic_open);
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_generic_release(struct inode *inode, struct file *file)
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
141afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	struct dvb_device *dvbdev = file->private_data;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dvbdev)
144afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		return -ENODEV;
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvbdev->readers++;
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvbdev->writers++;
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->users++;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_generic_release);
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15816ef8def80ea97c3cacdcaa765bdf62b2d94f86dArnd Bergmannlong dvb_generic_ioctl(struct file *file,
15916ef8def80ea97c3cacdcaa765bdf62b2d94f86dArnd Bergmann		       unsigned int cmd, unsigned long arg)
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
161afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	struct dvb_device *dvbdev = file->private_data;
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
163afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	if (!dvbdev)
164afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		return -ENODEV;
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dvbdev->kernel_ioctl)
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16972024f1ec5164a70d84dd8cf4458fe4064a6b692Arnd Bergmann	return dvb_usercopy(file, cmd, arg, dvbdev->kernel_ioctl);
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_generic_ioctl);
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvbdev_get_free_id (struct dvb_adapter *adap, int type)
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 id = 0;
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (id < DVB_MAX_IDS) {
1797948261942ffdb35e274b8e1a0889601f45d4603Trent Piepho		struct dvb_device *dev;
1807948261942ffdb35e274b8e1a0889601f45d4603Trent Piepho		list_for_each_entry(dev, &adap->device_list, list_head)
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (dev->type == type && dev->id == id)
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto skip;
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return id;
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip:
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		id++;
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENFILE;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			const struct dvb_device *template, void *priv, int type)
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dvb_device *dvbdev;
195b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	struct file_operations *dvbdevfops;
1965f553388b06532b495681f5d6c8e8fbff64ea86aKay Sievers	struct device *clsdev;
1975dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	int minor;
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int id;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
200c278850206fd9df0bb62a72ca0b277fe20c5a452Simon Arlott	mutex_lock(&dvbdev_register_lock);
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
202b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	if ((id = dvbdev_get_free_id (adap, type)) < 0){
2031e4baed379a2bff3c728ea34602d3432fb047af8Ingo Molnar		mutex_unlock(&dvbdev_register_lock);
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*pdvbdev = NULL;
20546b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison		printk(KERN_ERR "%s: couldn't find free device id\n", __func__);
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENFILE;
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL);
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
211b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	if (!dvbdev){
212b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert		mutex_unlock(&dvbdev_register_lock);
213b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert		return -ENOMEM;
214b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	}
215b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert
216b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	dvbdevfops = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
217b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert
218b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	if (!dvbdevfops){
219b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert		kfree (dvbdev);
2201e4baed379a2bff3c728ea34602d3432fb047af8Ingo Molnar		mutex_unlock(&dvbdev_register_lock);
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(dvbdev, template, sizeof(struct dvb_device));
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->type = type;
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->id = id;
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->adapter = adap;
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->priv = priv;
229b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	dvbdev->fops = dvbdevfops;
230ca5be9cd0516629cb8ee335b7dad076e66d72a22Markus Rechberger	init_waitqueue_head (&dvbdev->wait_queue);
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
232784e29d2031b535637f65a8b81fb0871c7c51b3fJan Engelhardt	memcpy(dvbdevfops, template->fops, sizeof(struct file_operations));
233784e29d2031b535637f65a8b81fb0871c7c51b3fJan Engelhardt	dvbdevfops->owner = adap->module;
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_add_tail (&dvbdev->list_head, &adap->device_list);
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2375dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	down_write(&minor_rwsem);
2385dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter#ifdef CONFIG_DVB_DYNAMIC_MINORS
2395dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	for (minor = 0; minor < MAX_DVB_MINORS; minor++)
2405dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter		if (dvb_minors[minor] == NULL)
2415dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter			break;
2425dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter
2435dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	if (minor == MAX_DVB_MINORS) {
2445dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter		kfree(dvbdevfops);
2455dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter		kfree(dvbdev);
2465dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter		mutex_unlock(&dvbdev_register_lock);
2475dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter		return -EINVAL;
2485dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	}
2495dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter#else
2505dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	minor = nums2minor(adap->num, type, id);
2515dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter#endif
2525dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter
2535dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	dvbdev->minor = minor;
2545dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	dvb_minors[minor] = dvbdev;
2555dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	up_write(&minor_rwsem);
2565dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter
257f47f4763cde162656448fcd1ada9d5e8101a00d2Andrew de Quincey	mutex_unlock(&dvbdev_register_lock);
258f47f4763cde162656448fcd1ada9d5e8101a00d2Andrew de Quincey
259a9b12619f7b6f19c871437ec24a088787a04b1deGreg Kroah-Hartman	clsdev = device_create(dvb_class, adap->device,
260b7496780e800061522de2262b4b4d7bf7602ecfaHans Verkuil			       MKDEV(DVB_MAJOR, minor),
261a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers			       dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id);
2624abdcf933f647763592db6bef001d1fae61a5527Simon Arlott	if (IS_ERR(clsdev)) {
2634abdcf933f647763592db6bef001d1fae61a5527Simon Arlott		printk(KERN_ERR "%s: failed to create device dvb%d.%s%d (%ld)\n",
26446b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison		       __func__, adap->num, dnames[type], id, PTR_ERR(clsdev));
2654abdcf933f647763592db6bef001d1fae61a5527Simon Arlott		return PTR_ERR(clsdev);
2664abdcf933f647763592db6bef001d1fae61a5527Simon Arlott	}
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
268900858ecb30c27ac94369052be650e25c0fd7d2aSimon Arlott	dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
2695dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter		adap->num, dnames[type], id, minor, minor);
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_register_device);
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid dvb_unregister_device(struct dvb_device *dvbdev)
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dvbdev)
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2815dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	down_write(&minor_rwsem);
2825dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	dvb_minors[dvbdev->minor] = NULL;
2835dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	up_write(&minor_rwsem);
2845dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter
2855dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_del (&dvbdev->list_head);
288b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	kfree (dvbdev->fops);
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree (dvbdev);
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_unregister_device);
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29378e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunaustatic int dvbdev_check_free_adapter_num(int num)
29478e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau{
29578e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	struct list_head *entry;
29678e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	list_for_each(entry, &dvb_adapter_list) {
29778e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		struct dvb_adapter *adap;
29878e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		adap = list_entry(entry, struct dvb_adapter, list_head);
29978e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		if (adap->num == num)
30078e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau			return 0;
30178e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	}
30278e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	return 1;
30378e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau}
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvbdev_get_free_adapter_num (void)
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int num = 0;
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (num < DVB_MAX_ADAPTERS) {
31078e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		if (dvbdev_check_free_adapter_num(num))
31178e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau			return num;
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		num++;
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENFILE;
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
31978e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunauint dvb_register_adapter(struct dvb_adapter *adap, const char *name,
32078e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau			 struct module *module, struct device *device,
32178e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau			 short *adapter_nums)
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
32378e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	int i, num;
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
325c278850206fd9df0bb62a72ca0b277fe20c5a452Simon Arlott	mutex_lock(&dvbdev_register_lock);
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
32778e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	for (i = 0; i < DVB_MAX_ADAPTERS; ++i) {
32878e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		num = adapter_nums[i];
32978e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		if (num >= 0  &&  num < DVB_MAX_ADAPTERS) {
33078e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		/* use the one the driver asked for */
33178e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau			if (dvbdev_check_free_adapter_num(num))
33278e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau				break;
33378e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		} else {
33478e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau			num = dvbdev_get_free_adapter_num();
33578e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau			break;
33678e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		}
33778e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		num = -1;
33878e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	}
33978e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau
34078e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	if (num < 0) {
3411e4baed379a2bff3c728ea34602d3432fb047af8Ingo Molnar		mutex_unlock(&dvbdev_register_lock);
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENFILE;
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset (adap, 0, sizeof(struct dvb_adapter));
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	INIT_LIST_HEAD (&adap->device_list);
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
348900858ecb30c27ac94369052be650e25c0fd7d2aSimon Arlott	printk(KERN_INFO "DVB: registering new adapter (%s)\n", name);
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	adap->num = num;
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	adap->name = name;
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	adap->module = module;
353d09dbf92ada861244056d914a8f68b8be99891edAndrew de Quincey	adap->device = device;
35459b1842da1c6f33ad2e8da82d3dfb3445751d964Darron Broad	adap->mfe_shared = 0;
35559b1842da1c6f33ad2e8da82d3dfb3445751d964Darron Broad	adap->mfe_dvbdev = NULL;
35659b1842da1c6f33ad2e8da82d3dfb3445751d964Darron Broad	mutex_init (&adap->mfe_lock);
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_add_tail (&adap->list_head, &dvb_adapter_list);
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3601e4baed379a2bff3c728ea34602d3432fb047af8Ingo Molnar	mutex_unlock(&dvbdev_register_lock);
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return num;
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_register_adapter);
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_unregister_adapter(struct dvb_adapter *adap)
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
369c278850206fd9df0bb62a72ca0b277fe20c5a452Simon Arlott	mutex_lock(&dvbdev_register_lock);
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_del (&adap->list_head);
3711e4baed379a2bff3c728ea34602d3432fb047af8Ingo Molnar	mutex_unlock(&dvbdev_register_lock);
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_unregister_adapter);
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* if the miracle happens and "generic_usercopy()" is included into
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   the kernel, then this can vanish. please don't make the mistake and
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   define this as video_usercopy(). this will introduce a dependecy
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   to the v4l "videodev.o" module, which is unnecessary for some
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   cards (ie. the budget dvb-cards don't need the v4l module...) */
38116ef8def80ea97c3cacdcaa765bdf62b2d94f86dArnd Bergmannint dvb_usercopy(struct file *file,
382afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		     unsigned int cmd, unsigned long arg,
38316ef8def80ea97c3cacdcaa765bdf62b2d94f86dArnd Bergmann		     int (*func)(struct file *file,
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		     unsigned int cmd, void *arg))
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
386afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	char    sbuf[128];
387afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	void    *mbuf = NULL;
388afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	void    *parg = NULL;
389afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	int     err  = -EINVAL;
390afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab
391afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	/*  Copy arguments into temp kernel buffer  */
392afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	switch (_IOC_DIR(cmd)) {
393afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	case _IOC_NONE:
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * For this command, the pointer is actually an integer
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * argument.
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		parg = (void *) arg;
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
400afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	case _IOC_READ: /* some v4l ioctls are marked wrong ... */
401afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	case _IOC_WRITE:
402afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	case (_IOC_WRITE | _IOC_READ):
403afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
40450c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			parg = sbuf;
405afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		} else {
40650c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			/* too big to allocate from stack */
40750c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL);
40850c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			if (NULL == mbuf)
40950c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky				return -ENOMEM;
41050c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			parg = mbuf;
411afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		}
412afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab
413afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		err = -EFAULT;
414afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
41550c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			goto out;
416afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		break;
417afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	}
418afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab
419afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	/* call driver */
42072024f1ec5164a70d84dd8cf4458fe4064a6b692Arnd Bergmann	mutex_lock(&dvbdev_mutex);
42116ef8def80ea97c3cacdcaa765bdf62b2d94f86dArnd Bergmann	if ((err = func(file, cmd, parg)) == -ENOIOCTLCMD)
422afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		err = -EINVAL;
42372024f1ec5164a70d84dd8cf4458fe4064a6b692Arnd Bergmann	mutex_unlock(&dvbdev_mutex);
424afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab
425afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	if (err < 0)
426afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		goto out;
427afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab
428afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	/*  Copy results into user buffer  */
429afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	switch (_IOC_DIR(cmd))
430afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	{
431afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	case _IOC_READ:
432afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	case (_IOC_WRITE | _IOC_READ):
433afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
43450c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			err = -EFAULT;
435afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		break;
436afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	}
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
439afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	kfree(mbuf);
440afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	return err;
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
443a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sieversstatic int dvb_uevent(struct device *dev, struct kobj_uevent_env *env)
444a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers{
445a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers	struct dvb_device *dvbdev = dev_get_drvdata(dev);
446a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers
447a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers	add_uevent_var(env, "DVB_ADAPTER_NUM=%d", dvbdev->adapter->num);
448763d19bb90a005a339b7d5ba70a710bb17db2babKay Sievers	add_uevent_var(env, "DVB_DEVICE_TYPE=%s", dnames[dvbdev->type]);
449763d19bb90a005a339b7d5ba70a710bb17db2babKay Sievers	add_uevent_var(env, "DVB_DEVICE_NUM=%d", dvbdev->id);
450a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers	return 0;
451a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers}
452a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers
4532c9ede55ecec58099b72e4bb8eab719f32f72c31Al Virostatic char *dvb_devnode(struct device *dev, umode_t *mode)
4548a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers{
4558a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers	struct dvb_device *dvbdev = dev_get_drvdata(dev);
4568a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers
4578a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers	return kasprintf(GFP_KERNEL, "dvb/adapter%d/%s%d",
4588a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers		dvbdev->adapter->num, dnames[dvbdev->type], dvbdev->id);
4598a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers}
4608a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers
4618a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init init_dvbdev(void)
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_t dev = MKDEV(DVB_MAJOR, 0);
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB")) != 0) {
468900858ecb30c27ac94369052be650e25c0fd7d2aSimon Arlott		printk(KERN_ERR "dvb-core: unable to get major %d\n", DVB_MAJOR);
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cdev_init(&dvb_device_cdev, &dvb_device_fops);
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS)) != 0) {
474900858ecb30c27ac94369052be650e25c0fd7d2aSimon Arlott		printk(KERN_ERR "dvb-core: unable register character device\n");
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
47856b2293595b2eb52cc2aa2baf92c6cfa8265f9d5Greg Kroah-Hartman	dvb_class = class_create(THIS_MODULE, "dvb");
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (IS_ERR(dvb_class)) {
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = PTR_ERR(dvb_class);
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
483a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers	dvb_class->dev_uevent = dvb_uevent;
484e454cea20bdcff10ee698d11b8882662a0153a47Kay Sievers	dvb_class->devnode = dvb_devnode;
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserror:
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cdev_del(&dvb_device_cdev);
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unregister_chrdev_region(dev, MAX_DVB_MINORS);
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit exit_dvbdev(void)
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
49656b2293595b2eb52cc2aa2baf92c6cfa8265f9d5Greg Kroah-Hartman	class_destroy(dvb_class);
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cdev_del(&dvb_device_cdev);
498afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS);
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5014abdcf933f647763592db6bef001d1fae61a5527Simon Arlottsubsys_initcall(init_dvbdev);
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(exit_dvbdev);
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("DVB Core Driver");
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler");
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
507