dvbdev.c revision e454cea20bdcff10ee698d11b8882662a0153a47
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>
355794e1b14bcd9817c5fa27d3254996f0d9551296Jonathan Corbet#include <linux/smp_lock.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "dvbdev.h"
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
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
715794e1b14bcd9817c5fa27d3254996f0d9551296Jonathan Corbet	lock_kernel();
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);
945794e1b14bcd9817c5fa27d3254996f0d9551296Jonathan Corbet		unlock_kernel();
95afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		return err;
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
97f41ced8f108cc80f16509b907cd7ac93944459bcLaurent Pinchartfail:
985dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	up_read(&minor_rwsem);
995794e1b14bcd9817c5fa27d3254996f0d9551296Jonathan Corbet	unlock_kernel();
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,
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1107e7654a92a5a75a350cc3458389bc76273498f6aGreg Kroah-Hartmanstatic struct cdev dvb_device_cdev;
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_generic_open(struct inode *inode, struct file *file)
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
114afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	struct dvb_device *dvbdev = file->private_data;
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
116afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	if (!dvbdev)
117afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		return -ENODEV;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dvbdev->users)
120afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		return -EBUSY;
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
123afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		if (!dvbdev->readers)
124afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab			return -EBUSY;
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvbdev->readers--;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
127afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		if (!dvbdev->writers)
128afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab			return -EBUSY;
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvbdev->writers--;
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->users--;
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_generic_open);
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_generic_release(struct inode *inode, struct file *file)
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
140afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	struct dvb_device *dvbdev = file->private_data;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dvbdev)
143afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		return -ENODEV;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvbdev->readers++;
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvbdev->writers++;
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->users++;
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_generic_release);
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_generic_ioctl(struct inode *inode, struct file *file,
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      unsigned int cmd, unsigned long arg)
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
160afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	struct dvb_device *dvbdev = file->private_data;
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
162afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	if (!dvbdev)
163afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		return -ENODEV;
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dvbdev->kernel_ioctl)
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return dvb_usercopy (inode, file, cmd, arg, dvbdev->kernel_ioctl);
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_generic_ioctl);
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvbdev_get_free_id (struct dvb_adapter *adap, int type)
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 id = 0;
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (id < DVB_MAX_IDS) {
1787948261942ffdb35e274b8e1a0889601f45d4603Trent Piepho		struct dvb_device *dev;
1797948261942ffdb35e274b8e1a0889601f45d4603Trent Piepho		list_for_each_entry(dev, &adap->device_list, list_head)
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (dev->type == type && dev->id == id)
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto skip;
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return id;
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsskip:
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		id++;
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENFILE;
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			const struct dvb_device *template, void *priv, int type)
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dvb_device *dvbdev;
194b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	struct file_operations *dvbdevfops;
1955f553388b06532b495681f5d6c8e8fbff64ea86aKay Sievers	struct device *clsdev;
1965dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	int minor;
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int id;
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
199c278850206fd9df0bb62a72ca0b277fe20c5a452Simon Arlott	mutex_lock(&dvbdev_register_lock);
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
201b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	if ((id = dvbdev_get_free_id (adap, type)) < 0){
2021e4baed379a2bff3c728ea34602d3432fb047af8Ingo Molnar		mutex_unlock(&dvbdev_register_lock);
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*pdvbdev = NULL;
20446b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison		printk(KERN_ERR "%s: couldn't find free device id\n", __func__);
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENFILE;
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL);
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
210b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	if (!dvbdev){
211b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert		mutex_unlock(&dvbdev_register_lock);
212b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert		return -ENOMEM;
213b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	}
214b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert
215b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	dvbdevfops = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
216b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert
217b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	if (!dvbdevfops){
218b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert		kfree (dvbdev);
2191e4baed379a2bff3c728ea34602d3432fb047af8Ingo Molnar		mutex_unlock(&dvbdev_register_lock);
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(dvbdev, template, sizeof(struct dvb_device));
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->type = type;
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->id = id;
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->adapter = adap;
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvbdev->priv = priv;
228b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	dvbdev->fops = dvbdevfops;
229ca5be9cd0516629cb8ee335b7dad076e66d72a22Markus Rechberger	init_waitqueue_head (&dvbdev->wait_queue);
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
231784e29d2031b535637f65a8b81fb0871c7c51b3fJan Engelhardt	memcpy(dvbdevfops, template->fops, sizeof(struct file_operations));
232784e29d2031b535637f65a8b81fb0871c7c51b3fJan Engelhardt	dvbdevfops->owner = adap->module;
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_add_tail (&dvbdev->list_head, &adap->device_list);
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2365dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	down_write(&minor_rwsem);
2375dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter#ifdef CONFIG_DVB_DYNAMIC_MINORS
2385dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	for (minor = 0; minor < MAX_DVB_MINORS; minor++)
2395dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter		if (dvb_minors[minor] == NULL)
2405dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter			break;
2415dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter
2425dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	if (minor == MAX_DVB_MINORS) {
2435dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter		kfree(dvbdevfops);
2445dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter		kfree(dvbdev);
2455dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter		mutex_unlock(&dvbdev_register_lock);
2465dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter		return -EINVAL;
2475dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	}
2485dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter#else
2495dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	minor = nums2minor(adap->num, type, id);
2505dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter#endif
2515dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter
2525dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	dvbdev->minor = minor;
2535dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	dvb_minors[minor] = dvbdev;
2545dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	up_write(&minor_rwsem);
2555dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter
256f47f4763cde162656448fcd1ada9d5e8101a00d2Andrew de Quincey	mutex_unlock(&dvbdev_register_lock);
257f47f4763cde162656448fcd1ada9d5e8101a00d2Andrew de Quincey
258a9b12619f7b6f19c871437ec24a088787a04b1deGreg Kroah-Hartman	clsdev = device_create(dvb_class, adap->device,
259b7496780e800061522de2262b4b4d7bf7602ecfaHans Verkuil			       MKDEV(DVB_MAJOR, minor),
260a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers			       dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id);
2614abdcf933f647763592db6bef001d1fae61a5527Simon Arlott	if (IS_ERR(clsdev)) {
2624abdcf933f647763592db6bef001d1fae61a5527Simon Arlott		printk(KERN_ERR "%s: failed to create device dvb%d.%s%d (%ld)\n",
26346b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison		       __func__, adap->num, dnames[type], id, PTR_ERR(clsdev));
2644abdcf933f647763592db6bef001d1fae61a5527Simon Arlott		return PTR_ERR(clsdev);
2654abdcf933f647763592db6bef001d1fae61a5527Simon Arlott	}
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
267900858ecb30c27ac94369052be650e25c0fd7d2aSimon Arlott	dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
2685dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter		adap->num, dnames[type], id, minor, minor);
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_register_device);
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid dvb_unregister_device(struct dvb_device *dvbdev)
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dvbdev)
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2805dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	down_write(&minor_rwsem);
2815dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	dvb_minors[dvbdev->minor] = NULL;
2825dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	up_write(&minor_rwsem);
2835dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter
2845dd3f3071070f5a306bdf8d474c80062f5691cbaAndreas Oberritter	device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_del (&dvbdev->list_head);
287b61901024776b25ce7b8edc31bb1757c7382a88eMarcel Siegert	kfree (dvbdev->fops);
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree (dvbdev);
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_unregister_device);
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29278e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunaustatic int dvbdev_check_free_adapter_num(int num)
29378e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau{
29478e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	struct list_head *entry;
29578e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	list_for_each(entry, &dvb_adapter_list) {
29678e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		struct dvb_adapter *adap;
29778e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		adap = list_entry(entry, struct dvb_adapter, list_head);
29878e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		if (adap->num == num)
29978e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau			return 0;
30078e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	}
30178e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	return 1;
30278e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau}
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvbdev_get_free_adapter_num (void)
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int num = 0;
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (num < DVB_MAX_ADAPTERS) {
30978e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		if (dvbdev_check_free_adapter_num(num))
31078e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau			return num;
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		num++;
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENFILE;
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
31878e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunauint dvb_register_adapter(struct dvb_adapter *adap, const char *name,
31978e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau			 struct module *module, struct device *device,
32078e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau			 short *adapter_nums)
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
32278e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	int i, num;
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
324c278850206fd9df0bb62a72ca0b277fe20c5a452Simon Arlott	mutex_lock(&dvbdev_register_lock);
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
32678e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	for (i = 0; i < DVB_MAX_ADAPTERS; ++i) {
32778e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		num = adapter_nums[i];
32878e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		if (num >= 0  &&  num < DVB_MAX_ADAPTERS) {
32978e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		/* use the one the driver asked for */
33078e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau			if (dvbdev_check_free_adapter_num(num))
33178e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau				break;
33278e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		} else {
33378e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau			num = dvbdev_get_free_adapter_num();
33478e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau			break;
33578e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		}
33678e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau		num = -1;
33778e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	}
33878e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau
33978e92006f410a4044f8c1760c25ac9d11d259aa2Janne Grunau	if (num < 0) {
3401e4baed379a2bff3c728ea34602d3432fb047af8Ingo Molnar		mutex_unlock(&dvbdev_register_lock);
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENFILE;
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset (adap, 0, sizeof(struct dvb_adapter));
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	INIT_LIST_HEAD (&adap->device_list);
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
347900858ecb30c27ac94369052be650e25c0fd7d2aSimon Arlott	printk(KERN_INFO "DVB: registering new adapter (%s)\n", name);
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	adap->num = num;
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	adap->name = name;
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	adap->module = module;
352d09dbf92ada861244056d914a8f68b8be99891edAndrew de Quincey	adap->device = device;
35359b1842da1c6f33ad2e8da82d3dfb3445751d964Darron Broad	adap->mfe_shared = 0;
35459b1842da1c6f33ad2e8da82d3dfb3445751d964Darron Broad	adap->mfe_dvbdev = NULL;
35559b1842da1c6f33ad2e8da82d3dfb3445751d964Darron Broad	mutex_init (&adap->mfe_lock);
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_add_tail (&adap->list_head, &dvb_adapter_list);
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3591e4baed379a2bff3c728ea34602d3432fb047af8Ingo Molnar	mutex_unlock(&dvbdev_register_lock);
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return num;
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_register_adapter);
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_unregister_adapter(struct dvb_adapter *adap)
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
368c278850206fd9df0bb62a72ca0b277fe20c5a452Simon Arlott	mutex_lock(&dvbdev_register_lock);
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_del (&adap->list_head);
3701e4baed379a2bff3c728ea34602d3432fb047af8Ingo Molnar	mutex_unlock(&dvbdev_register_lock);
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_unregister_adapter);
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* if the miracle happens and "generic_usercopy()" is included into
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   the kernel, then this can vanish. please don't make the mistake and
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   define this as video_usercopy(). this will introduce a dependecy
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   to the v4l "videodev.o" module, which is unnecessary for some
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   cards (ie. the budget dvb-cards don't need the v4l module...) */
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_usercopy(struct inode *inode, struct file *file,
381afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		     unsigned int cmd, unsigned long arg,
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		     int (*func)(struct inode *inode, struct file *file,
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		     unsigned int cmd, void *arg))
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
385afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	char    sbuf[128];
386afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	void    *mbuf = NULL;
387afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	void    *parg = NULL;
388afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	int     err  = -EINVAL;
389afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab
390afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	/*  Copy arguments into temp kernel buffer  */
391afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	switch (_IOC_DIR(cmd)) {
392afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	case _IOC_NONE:
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * For this command, the pointer is actually an integer
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * argument.
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		parg = (void *) arg;
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
399afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	case _IOC_READ: /* some v4l ioctls are marked wrong ... */
400afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	case _IOC_WRITE:
401afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	case (_IOC_WRITE | _IOC_READ):
402afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
40350c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			parg = sbuf;
404afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		} else {
40550c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			/* too big to allocate from stack */
40650c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL);
40750c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			if (NULL == mbuf)
40850c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky				return -ENOMEM;
40950c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			parg = mbuf;
410afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		}
411afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab
412afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		err = -EFAULT;
413afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
41450c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			goto out;
415afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		break;
416afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	}
417afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab
418afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	/* call driver */
419afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	if ((err = func(inode, file, cmd, parg)) == -ENOIOCTLCMD)
420afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		err = -EINVAL;
421afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab
422afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	if (err < 0)
423afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		goto out;
424afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab
425afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	/*  Copy results into user buffer  */
426afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	switch (_IOC_DIR(cmd))
427afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	{
428afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	case _IOC_READ:
429afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	case (_IOC_WRITE | _IOC_READ):
430afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
43150c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			err = -EFAULT;
432afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab		break;
433afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	}
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
436afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	kfree(mbuf);
437afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	return err;
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
440a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sieversstatic int dvb_uevent(struct device *dev, struct kobj_uevent_env *env)
441a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers{
442a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers	struct dvb_device *dvbdev = dev_get_drvdata(dev);
443a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers
444a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers	add_uevent_var(env, "DVB_ADAPTER_NUM=%d", dvbdev->adapter->num);
445763d19bb90a005a339b7d5ba70a710bb17db2babKay Sievers	add_uevent_var(env, "DVB_DEVICE_TYPE=%s", dnames[dvbdev->type]);
446763d19bb90a005a339b7d5ba70a710bb17db2babKay Sievers	add_uevent_var(env, "DVB_DEVICE_NUM=%d", dvbdev->id);
447a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers	return 0;
448a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers}
449a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers
450e454cea20bdcff10ee698d11b8882662a0153a47Kay Sieversstatic char *dvb_devnode(struct device *dev, mode_t *mode)
4518a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers{
4528a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers	struct dvb_device *dvbdev = dev_get_drvdata(dev);
4538a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers
4548a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers	return kasprintf(GFP_KERNEL, "dvb/adapter%d/%s%d",
4558a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers		dvbdev->adapter->num, dnames[dvbdev->type], dvbdev->id);
4568a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers}
4578a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers
4588a8bdcc7533b104d789d9bb3ed90da9352515e21Kay Sievers
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init init_dvbdev(void)
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_t dev = MKDEV(DVB_MAJOR, 0);
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB")) != 0) {
465900858ecb30c27ac94369052be650e25c0fd7d2aSimon Arlott		printk(KERN_ERR "dvb-core: unable to get major %d\n", DVB_MAJOR);
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cdev_init(&dvb_device_cdev, &dvb_device_fops);
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS)) != 0) {
471900858ecb30c27ac94369052be650e25c0fd7d2aSimon Arlott		printk(KERN_ERR "dvb-core: unable register character device\n");
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
47556b2293595b2eb52cc2aa2baf92c6cfa8265f9d5Greg Kroah-Hartman	dvb_class = class_create(THIS_MODULE, "dvb");
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (IS_ERR(dvb_class)) {
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = PTR_ERR(dvb_class);
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
480a5f4c0ce682efa60fa9552514118bb70a7646e28Kay Sievers	dvb_class->dev_uevent = dvb_uevent;
481e454cea20bdcff10ee698d11b8882662a0153a47Kay Sievers	dvb_class->devnode = dvb_devnode;
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserror:
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cdev_del(&dvb_device_cdev);
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unregister_chrdev_region(dev, MAX_DVB_MINORS);
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit exit_dvbdev(void)
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
49356b2293595b2eb52cc2aa2baf92c6cfa8265f9d5Greg Kroah-Hartman	class_destroy(dvb_class);
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cdev_del(&dvb_device_cdev);
495afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab	unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS);
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4984abdcf933f647763592db6bef001d1fae61a5527Simon Arlottsubsys_initcall(init_dvbdev);
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(exit_dvbdev);
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("DVB Core Driver");
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler");
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
504