dvbdev.c revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac
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/moduleparam.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/sched.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/device.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fs.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/cdev.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "dvbdev.h"
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvbdev_debug;
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(dvbdev_debug, int, 0644);
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(dvbdev_debug, "Turn on/off device debugging (default:off).");
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define dprintk if (dvbdev_debug) printk
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic LIST_HEAD(dvb_adapter_list);
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DECLARE_MUTEX(dvbdev_register_lock);
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char * const dnames[] = {
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        "video", "audio", "sec", "frontend", "demux", "dvr", "ca",
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"net", "osd"
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DVB_MAX_ADAPTERS	8
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DVB_MAX_IDS		4
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define nums2minor(num,type,id)	((num << 6) | (id << 4) | type)
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MAX_DVB_MINORS		(DVB_MAX_ADAPTERS*64)
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct class_simple *dvb_class;
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_class);
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct dvb_device* dvbdev_find_device (int minor)
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct list_head *entry;
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_for_each (entry, &dvb_adapter_list) {
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct list_head *entry0;
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct dvb_adapter *adap;
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		adap = list_entry (entry, struct dvb_adapter, list_head);
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		list_for_each (entry0, &adap->device_list) {
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct dvb_device *dev;
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev = list_entry (entry0, struct dvb_device, list_head);
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (nums2minor(adap->num, dev->type, dev->id) == minor)
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return dev;
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NULL;
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_device_open(struct inode *inode, struct file *file)
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dvb_device *dvbdev;
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev = dvbdev_find_device (iminor(inode));
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dvbdev && dvbdev->fops) {
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int err = 0;
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct file_operations *old_fops;
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		file->private_data = dvbdev;
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		old_fops = file->f_op;
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                file->f_op = fops_get(dvbdev->fops);
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                if(file->f_op->open)
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        err = file->f_op->open(inode,file);
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                if (err) {
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        fops_put(file->f_op);
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        file->f_op = fops_get(old_fops);
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                }
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                fops_put(old_fops);
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                return err;
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENODEV;
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct file_operations dvb_device_fops =
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner =	THIS_MODULE,
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =		dvb_device_open,
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct cdev dvb_device_cdev = {
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.kobj   = {.name = "dvb", },
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner  =       THIS_MODULE,
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_generic_open(struct inode *inode, struct file *file)
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        struct dvb_device *dvbdev = file->private_data;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        if (!dvbdev)
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                return -ENODEV;
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dvbdev->users)
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                return -EBUSY;
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                if (!dvbdev->readers)
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		        return -EBUSY;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvbdev->readers--;
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                if (!dvbdev->writers)
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		        return -EBUSY;
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvbdev->writers--;
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->users--;
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_generic_open);
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_generic_release(struct inode *inode, struct file *file)
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        struct dvb_device *dvbdev = file->private_data;
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dvbdev)
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                return -ENODEV;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvbdev->readers++;
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvbdev->writers++;
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->users++;
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_generic_release);
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_generic_ioctl(struct inode *inode, struct file *file,
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      unsigned int cmd, unsigned long arg)
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        struct dvb_device *dvbdev = file->private_data;
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        if (!dvbdev)
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	        return -ENODEV;
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dvbdev->kernel_ioctl)
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return dvb_usercopy (inode, file, cmd, arg, dvbdev->kernel_ioctl);
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_generic_ioctl);
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvbdev_get_free_id (struct dvb_adapter *adap, int type)
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 id = 0;
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (id < DVB_MAX_IDS) {
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct list_head *entry;
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		list_for_each (entry, &adap->device_list) {
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct dvb_device *dev;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev = list_entry (entry, struct dvb_device, list_head);
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (dev->type == type && dev->id == id)
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto skip;
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return id;
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip:
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		id++;
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENFILE;
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			const struct dvb_device *template, void *priv, int type)
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dvb_device *dvbdev;
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int id;
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (down_interruptible (&dvbdev_register_lock))
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ERESTARTSYS;
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((id = dvbdev_get_free_id (adap, type)) < 0) {
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		up (&dvbdev_register_lock);
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*pdvbdev = NULL;
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk ("%s: could get find free device id...\n", __FUNCTION__);
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENFILE;
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL);
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dvbdev) {
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		up(&dvbdev_register_lock);
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	up (&dvbdev_register_lock);
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(dvbdev, template, sizeof(struct dvb_device));
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->type = type;
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->id = id;
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->adapter = adap;
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->priv = priv;
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->fops->owner = adap->module;
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_add_tail (&dvbdev->list_head, &adap->device_list);
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	devfs_mk_cdev(MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)),
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			S_IFCHR | S_IRUSR | S_IWUSR,
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			"dvb/adapter%d/%s%d", adap->num, dnames[type], id);
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	class_simple_device_add(dvb_class, MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)),
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				NULL, "dvb%d.%s%d", adap->num, dnames[type], id);
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		adap->num, dnames[type], id, nums2minor(adap->num, type, id),
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nums2minor(adap->num, type, id));
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_register_device);
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid dvb_unregister_device(struct dvb_device *dvbdev)
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dvbdev)
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	devfs_remove("dvb/adapter%d/%s%d", dvbdev->adapter->num,
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dnames[dvbdev->type], dvbdev->id);
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	class_simple_device_remove(MKDEV(DVB_MAJOR, nums2minor(dvbdev->adapter->num,
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dvbdev->type, dvbdev->id)));
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_del (&dvbdev->list_head);
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree (dvbdev);
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_unregister_device);
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvbdev_get_free_adapter_num (void)
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int num = 0;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (num < DVB_MAX_ADAPTERS) {
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct list_head *entry;
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		list_for_each (entry, &dvb_adapter_list) {
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct dvb_adapter *adap;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			adap = list_entry (entry, struct dvb_adapter, list_head);
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (adap->num == num)
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto skip;
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return num;
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip:
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		num++;
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENFILE;
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_register_adapter(struct dvb_adapter **padap, const char *name, struct module *module)
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dvb_adapter *adap;
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int num;
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (down_interruptible (&dvbdev_register_lock))
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ERESTARTSYS;
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((num = dvbdev_get_free_adapter_num ()) < 0) {
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		up (&dvbdev_register_lock);
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENFILE;
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(*padap = adap = kmalloc(sizeof(struct dvb_adapter), GFP_KERNEL))) {
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		up(&dvbdev_register_lock);
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset (adap, 0, sizeof(struct dvb_adapter));
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	INIT_LIST_HEAD (&adap->device_list);
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk ("DVB: registering new adapter (%s).\n", name);
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	devfs_mk_dir("dvb/adapter%d", num);
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	adap->num = num;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	adap->name = name;
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	adap->module = module;
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_add_tail (&adap->list_head, &dvb_adapter_list);
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	up (&dvbdev_register_lock);
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return num;
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_register_adapter);
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_unregister_adapter(struct dvb_adapter *adap)
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	devfs_remove("dvb/adapter%d", adap->num);
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (down_interruptible (&dvbdev_register_lock))
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ERESTARTSYS;
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_del (&adap->list_head);
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	up (&dvbdev_register_lock);
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree (adap);
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_unregister_adapter);
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* if the miracle happens and "generic_usercopy()" is included into
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   the kernel, then this can vanish. please don't make the mistake and
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   define this as video_usercopy(). this will introduce a dependecy
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   to the v4l "videodev.o" module, which is unnecessary for some
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   cards (ie. the budget dvb-cards don't need the v4l module...) */
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_usercopy(struct inode *inode, struct file *file,
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	             unsigned int cmd, unsigned long arg,
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		     int (*func)(struct inode *inode, struct file *file,
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		     unsigned int cmd, void *arg))
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        char    sbuf[128];
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        void    *mbuf = NULL;
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        void    *parg = NULL;
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        int     err  = -EINVAL;
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        /*  Copy arguments into temp kernel buffer  */
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        switch (_IOC_DIR(cmd)) {
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        case _IOC_NONE:
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * For this command, the pointer is actually an integer
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * argument.
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		parg = (void *) arg;
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        case _IOC_READ: /* some v4l ioctls are marked wrong ... */
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        case _IOC_WRITE:
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        case (_IOC_WRITE | _IOC_READ):
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        parg = sbuf;
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                } else {
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        /* too big to allocate from stack */
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL);
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        if (NULL == mbuf)
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                                return -ENOMEM;
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        parg = mbuf;
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                }
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                err = -EFAULT;
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        goto out;
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                break;
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        /* call driver */
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        if ((err = func(inode, file, cmd, parg)) == -ENOIOCTLCMD)
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                err = -EINVAL;
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        if (err < 0)
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                goto out;
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        /*  Copy results into user buffer  */
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        switch (_IOC_DIR(cmd))
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        {
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        case _IOC_READ:
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        case (_IOC_WRITE | _IOC_READ):
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        err = -EFAULT;
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                break;
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        kfree(mbuf);
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        return err;
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init init_dvbdev(void)
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_t dev = MKDEV(DVB_MAJOR, 0);
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB")) != 0) {
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("dvb-core: unable to get major %d\n", DVB_MAJOR);
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cdev_init(&dvb_device_cdev, &dvb_device_fops);
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS)) != 0) {
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("dvb-core: unable to get major %d\n", DVB_MAJOR);
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	devfs_mk_dir("dvb");
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvb_class = class_simple_create(THIS_MODULE, "dvb");
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (IS_ERR(dvb_class)) {
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = PTR_ERR(dvb_class);
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserror:
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cdev_del(&dvb_device_cdev);
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unregister_chrdev_region(dev, MAX_DVB_MINORS);
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit exit_dvbdev(void)
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        devfs_remove("dvb");
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	class_simple_destroy(dvb_class);
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cdev_del(&dvb_device_cdev);
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS);
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(init_dvbdev);
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(exit_dvbdev);
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("DVB Core Driver");
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler");
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
450