--- a/linux/drivers/media/dvb/dvb-core/dvbdev.h	Tue Jun 16 16:08:17 2009 -0400
+++ b/linux/drivers/media/dvb/dvb-core/dvbdev.h	Sat May 23 17:00:59 2009 -0400
@@ -51,6 +51,8 @@
 	module_param_array(adapter_nr, short, NULL, 0444); \
 	MODULE_PARM_DESC(adapter_nr, "DVB adapter numbers")

+struct dvb_frontend;
+
 struct dvb_adapter {
 	int num;
 	struct list_head list_head;
@@ -66,6 +68,32 @@
 	int mfe_shared;			/* indicates mutually exclusive frontends */
 	struct dvb_device *mfe_dvbdev;	/* frontend device in use */
 	struct mutex mfe_lock;		/* access lock for thread creation */
+
+	/* Allow the adapter/bridge driver to perform an action before and/or
+	 * after the core handles an ioctl:
+	 *
+	 * DVB_FE_IOCTL_PRE indicates that the ioctl has not yet been handled.
+	 * DVB_FE_IOCTL_POST indicates that the ioctl has been handled.
+	 *
+	 * When DVB_FE_IOCTL_PRE is passed to the callback as the stage arg:
+	 *
+	 * return 0 to allow dvb-core to handle the ioctl.
+	 * return a positive int to prevent dvb-core from handling the ioctl,
+	 * 	and exit without error.
+	 * return a negative int to prevent dvb-core from handling the ioctl,
+	 * 	and return that value as an error.
+	 *
+	 * When DVB_FE_IOCTL_POST is passed to the callback as the stage arg:
+	 *
+	 * return 0 to allow the dvb_frontend ioctl handler to exit normally.
+	 * return a negative int to cause the dvb_frontend ioctl handler to
+	 * 	return that value as an error.
+	 */
+#define DVB_FE_IOCTL_PRE 0
+#define DVB_FE_IOCTL_POST 1
+	int (*fe_ioctl_override)(struct dvb_frontend *fe,
+				 unsigned int cmd, void *parg,
+				 unsigned int stage);
 };





As you can see from the changeset, this allows the bridge to assert
control both before and after the frontend driver itself handles the
operation.  Depending on the value returned by the bridge driver,
dvb-core can exit with error, proceed unchanged, or exit without
error.

In order to allow some drivers to use the new feature, I had to make a
small change to videobuf-dvb:

--- a/linux/drivers/media/video/videobuf-dvb.c	Tue Jun 16 16:08:17 2009 -0400
+++ b/linux/drivers/media/video/videobuf-dvb.c	Sat May 23 17:00:59 2009 -0400
@@ -144,7 +144,9 @@
 			  struct device *device,
 			  char *adapter_name,
 			  short *adapter_nr,
-			  int mfe_shared)
+			  int mfe_shared,
+			  int (*fe_ioctl_override)(struct dvb_frontend *,
+					unsigned int, void *, unsigned int))
 {
 	int result;

@@ -159,6 +161,7 @@
 	}
 	fe->adapter.priv = adapter_priv;
 	fe->adapter.mfe_shared = mfe_shared;
+	fe->adapter.fe_ioctl_override = fe_ioctl_override;

 	return result;
 }
@@ -258,7 +261,9 @@
 			  void *adapter_priv,
 			  struct device *device,
 			  short *adapter_nr,
-			  int mfe_shared)
+			  int mfe_shared,
+			  int (*fe_ioctl_override)(struct dvb_frontend *,
+					unsigned int, void *, unsigned int))
 {
 	struct list_head *list, *q;
 	struct videobuf_dvb_frontend *fe;
@@ -272,7 +277,7 @@

 	/* Bring up the adapter */
 	res = videobuf_dvb_register_adapter(f, module, adapter_priv, device,
-		fe->dvb.name, adapter_nr, mfe_shared);
+		fe->dvb.name, adapter_nr, mfe_shared, fe_ioctl_override);
 	if (res < 0) {
 		printk(KERN_WARNING "videobuf_dvb_register_adapter failed (errno =
%d)\n", res);
 		return res;
--- a/linux/include/media/videobuf-dvb.h	Tue Jun 16 16:08:17 2009 -0400
+++ b/linux/include/media/videobuf-dvb.h	Sat May 23 17:00:59 2009 -0400
@@ -42,7 +42,9 @@
 			  void *adapter_priv,
 			  struct device *device,
 			  short *adapter_nr,
-			  int mfe_shared);
+			  int mfe_shared,
+			  int (*fe_ioctl_override)(struct dvb_frontend *,
+					unsigned int, void *, unsigned int));

 void videobuf_dvb_unregister_bus(struct videobuf_dvb_frontends *f);

--- a/linux/drivers/media/video/cx23885/cx23885-dvb.c	Tue Jun 16
16:08:17 2009 -0400
+++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c	Sat May 23
17:00:59 2009 -0400
@@ -856,7 +856,7 @@

 	/* register everything */
 	ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
-		&dev->pci->dev, adapter_nr, 0);
+					&dev->pci->dev, adapter_nr, 0, NULL);

 	/* init CI & MAC */
 	switch (dev->board) {
--- a/linux/drivers/media/video/cx88/cx88-dvb.c	Tue Jun 16 16:08:17 2009 -0400
+++ b/linux/drivers/media/video/cx88/cx88-dvb.c	Sat May 23 17:00:59 2009 -0400
@@ -1180,7 +1180,7 @@

 	/* register everything */
 	return videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
-		&dev->pci->dev, adapter_nr, mfe_shared);
+					 &dev->pci->dev, adapter_nr, mfe_shared, NULL);

 frontend_detach:
 	core->gate_ctrl = NULL;
--- a/linux/drivers/media/video/saa7134/saa7134-dvb.c	Tue Jun 16
16:08:17 2009 -0400
+++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c	Sat May 23
17:00:59 2009 -0400
@@ -1509,7 +1509,7 @@

 	/* register everything else */
 	ret = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
-		&dev->pci->dev, adapter_nr, 0);
+					&dev->pci->dev, adapter_nr, 0, NULL);

 	/* this sequence is necessary to make the tda1004x load its firmware
 	 * and to enter analog mode of hybrid boards




Finally, the actual implementation of this new feature in the cx23885
driver for the sake of RF input switching:

--- a/linux/drivers/media/video/cx23885/cx23885-dvb.c	Sat May 23
17:00:59 2009 -0400
+++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c	Fri May 08
21:39:24 2009 -0400
@@ -484,9 +484,33 @@
 		}
 		break;
 	}
-	return (port->set_frontend_save) ?
-		port->set_frontend_save(fe, param) : -ENODEV;
+	return 0;
 }
+
+static int cx23885_dvb_fe_ioctl_override(struct dvb_frontend *fe,
+					 unsigned int cmd, void *parg,
+					 unsigned int stage)
+{
+	int err = 0;
+
+	switch (stage) {
+	case DVB_FE_IOCTL_PRE:
+
+		switch (cmd) {
+		case FE_SET_FRONTEND:
+			err = cx23885_dvb_set_frontend(fe,
+				(struct dvb_frontend_parameters *) parg);
+			break;
+		}
+		break;
+
+	case DVB_FE_IOCTL_POST:
+		/* no post-ioctl handling required */
+		break;
+	}
+	return err;
+};
+

 static int dvb_register(struct cx23885_tsport *port)
 {
@@ -527,12 +551,6 @@
 				   0x60, &dev->i2c_bus[1].i2c_adap,
 				   &hauppauge_hvr127x_config);
 		}
-
-		/* FIXME: temporary hack */
-		/* define bridge override to set_frontend */
-		port->set_frontend_save = fe0->dvb.frontend->ops.set_frontend;
-		fe0->dvb.frontend->ops.set_frontend = cx23885_dvb_set_frontend;
-
 		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR1255:
 		i2c_bus = &dev->i2c_bus[0];
@@ -856,7 +874,8 @@

 	/* register everything */
 	ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
-					&dev->pci->dev, adapter_nr, 0, NULL);
+					&dev->pci->dev, adapter_nr, 0,
+					cx23885_dvb_fe_ioctl_override);

 	/* init CI & MAC */
 	switch (dev->board) {
--- a/linux/drivers/media/video/cx23885/cx23885.h	Sat May 23 17:00:59 2009 -0400
+++ b/linux/drivers/media/video/cx23885/cx23885.h	Fri May 08 21:39:24 2009 -0400
@@ -289,10 +289,6 @@
 	/* Allow a single tsport to have multiple frontends */
 	u32                        num_frontends;
 	void                       *port_priv;
-
-	/* FIXME: temporary hack */
-	int (*set_frontend_save) (struct dvb_frontend *,
-				  struct dvb_frontend_parameters *);
 };

 struct cx23885_dev {
