Commit 5dd3f307 authored by Andreas Oberritter's avatar Andreas Oberritter Committed by Mauro Carvalho Chehab
Browse files

V4L/DVB (9361): Dynamic DVB minor allocation



Implement dynamic minor allocation for DVB, to allow more than four
devices of the same type per adapter, based on drivers/usb/core/file.c.

Add a new config option, DVB_DYNAMIC_MINORS, to make use of this
feature, which defaults to no for backwards compatibility.

Signed-off-by: default avatarAndreas Oberritter <obi@linuxtv.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 568e9bb8
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -2,6 +2,19 @@
# DVB device configuration
#

config DVB_DYNAMIC_MINORS
	bool "Dynamic DVB minor allocation"
	depends on DVB_CORE
	default n
	help
	  If you say Y here, the DVB subsystem will use dynamic minor
	  allocation for any device that uses the DVB major number.
	  This means that you can have more than 4 of a single type
	  of device (like demuxes and frontends) per adapter, but udev
	  will be required to manage the device nodes.

	  If you are unsure about this, say N here.

menuconfig DVB_CAPTURE_DRIVERS
	bool "DVB/ATSC adapters"
	depends on DVB_CORE
+38 −19
Original line number Diff line number Diff line
@@ -50,33 +50,27 @@ static const char * const dnames[] = {
	"net", "osd"
};

#ifdef CONFIG_DVB_DYNAMIC_MINORS
#define MAX_DVB_MINORS		256
#define DVB_MAX_IDS		MAX_DVB_MINORS
#else
#define DVB_MAX_IDS		4
#define nums2minor(num,type,id)	((num << 6) | (id << 4) | type)
#define MAX_DVB_MINORS		(DVB_MAX_ADAPTERS*64)
#endif

static struct class *dvb_class;

static struct dvb_device* dvbdev_find_device (int minor)
{
	struct dvb_adapter *adap;

	list_for_each_entry(adap, &dvb_adapter_list, list_head) {
		struct dvb_device *dev;
		list_for_each_entry(dev, &adap->device_list, list_head)
			if (nums2minor(adap->num, dev->type, dev->id) == minor)
				return dev;
	}

	return NULL;
}

static struct dvb_device *dvb_minors[MAX_DVB_MINORS];
static DECLARE_RWSEM(minor_rwsem);

static int dvb_device_open(struct inode *inode, struct file *file)
{
	struct dvb_device *dvbdev;

	lock_kernel();
	dvbdev = dvbdev_find_device (iminor(inode));
	down_read(&minor_rwsem);
	dvbdev = dvb_minors[iminor(inode)];

	if (dvbdev && dvbdev->fops) {
		int err = 0;
@@ -92,9 +86,11 @@ static int dvb_device_open(struct inode *inode, struct file *file)
			file->f_op = fops_get(old_fops);
		}
		fops_put(old_fops);
		up_read(&minor_rwsem);
		unlock_kernel();
		return err;
	}
	up_read(&minor_rwsem);
	unlock_kernel();
	return -ENODEV;
}
@@ -192,6 +188,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
	struct dvb_device *dvbdev;
	struct file_operations *dvbdevfops;
	struct device *clsdev;
	int minor;
	int id;

	mutex_lock(&dvbdev_register_lock);
@@ -231,6 +228,26 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,

	list_add_tail (&dvbdev->list_head, &adap->device_list);

	down_write(&minor_rwsem);
#ifdef CONFIG_DVB_DYNAMIC_MINORS
	for (minor = 0; minor < MAX_DVB_MINORS; minor++)
		if (dvb_minors[minor] == NULL)
			break;

	if (minor == MAX_DVB_MINORS) {
		kfree(dvbdevfops);
		kfree(dvbdev);
		mutex_unlock(&dvbdev_register_lock);
		return -EINVAL;
	}
#else
	minor = nums2minor(adap->num, type, id);
#endif

	dvbdev->minor = minor;
	dvb_minors[minor] = dvbdev;
	up_write(&minor_rwsem);

	mutex_unlock(&dvbdev_register_lock);

	clsdev = device_create(dvb_class, adap->device,
@@ -243,8 +260,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
	}

	dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
		adap->num, dnames[type], id, nums2minor(adap->num, type, id),
		nums2minor(adap->num, type, id));
		adap->num, dnames[type], id, minor, minor);

	return 0;
}
@@ -256,8 +272,11 @@ void dvb_unregister_device(struct dvb_device *dvbdev)
	if (!dvbdev)
		return;

	device_destroy(dvb_class, MKDEV(DVB_MAJOR, nums2minor(dvbdev->adapter->num,
		       dvbdev->type, dvbdev->id)));
	down_write(&minor_rwsem);
	dvb_minors[dvbdev->minor] = NULL;
	up_write(&minor_rwsem);

	device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));

	list_del (&dvbdev->list_head);
	kfree (dvbdev->fops);
+1 −0
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ struct dvb_device {
	struct file_operations *fops;
	struct dvb_adapter *adapter;
	int type;
	int minor;
	u32 id;

	/* in theory, 'users' can vanish now,