Hi,

for em28xx devices the device node detection can be based on the
encoded endpoint address, for example EP 0x81 (USB IN, Interrupt),
0x82 (analog video EP), 0x83 (analog audio ep), 0x84 (mpeg-ts input
EP).
It is not necessary that digital TV devices have a frontend, the
em28xx chip only specifies an MPEG-TS input EP.

Following patch adds a check based on the Endpoints, although it might
be extended that all devices match the possible devicenodes based on
the endpoints, currently the driver registers an analog TV node by
default for all unknown devices which is not necessarily correct, this
patch disables the ATV node if no analog TV endpoint is available.

best regards,
Markus
diff -r 315bc4b65b4f linux/drivers/media/video/em28xx/em28xx-cards.c
--- a/linux/drivers/media/video/em28xx/em28xx-cards.c	Sun May 17 12:28:55 2009 +0000
+++ b/linux/drivers/media/video/em28xx/em28xx-cards.c	Sat May 23 16:03:39 2009 +0200
@@ -1596,9 +1596,13 @@
 /* Since em28xx_pre_card_setup() requires a proper dev->model,
  * this won't work for boards with generic PCI IDs
  */
-void em28xx_pre_card_setup(struct em28xx *dev)
+static int em28xx_pre_card_setup(struct em28xx *dev,
+				struct usb_interface *intf)
 {
 	int rc;
+	int i;
+	struct usb_host_interface *host_interface = &intf->altsetting[0];
+	int select_alt;
 
 	em28xx_set_model(dev);
 
@@ -1647,6 +1651,18 @@
 		}
 	}
 
+	/* this is for protecting wrong devices against the rest of the control
+	   commands
+	   for example:
+	   $ cd /sys/bus/usb/drivers/em28xx
+	   $ echo "1234 1234" > new_id
+	*/
+
+
+	if (dev->model == EM2800_BOARD_UNKNOWN &&
+		dev->chip_id >= CHIP_ID_EM2883)
+		dev->lock_control_commands = 1;
+
 	/* Prepopulate cached GPO register content */
 	rc = em28xx_read_reg(dev, dev->reg_gpo_num);
 	if (rc >= 0)
@@ -1658,8 +1674,66 @@
 	em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
 	msleep(50);
 
+	if (dev->model != EM2800_BOARD_UNKNOWN)
+		/* defaulting to have the same behaviour as we always had */
+		dev->has_atv = 1;
+
 	/* request some modules */
 	switch (dev->model) {
+	case EM2800_BOARD_UNKNOWN:
+		if (dev->chip_id < CHIP_ID_EM2820) {
+			/* defaulting again .. */
+			dev->has_atv = 1;
+			break;
+		}
+
+		em28xx_info("Probing device modes (ignore all upcoming"
+			     "errors)\n");
+		em28xx_info("Found endpoints: %d\n",
+				host_interface->desc.bNumEndpoints);
+		em28xx_info("Found alternate: %d\n", dev->num_alt);
+
+		switch (dev->num_alt) {
+		case 2:
+			select_alt = 1;
+			break;
+		case 8:
+			select_alt = 7;
+			break;
+		default:
+			/* guaranteed no EETI TV device */
+			return -EINVAL;
+		}
+		for (i = 0; i < host_interface->desc.bNumEndpoints; i++) {
+			em28xx_info("Alternate setting %d [%02x]\n",
+				select_alt,
+				intf->altsetting[select_alt].endpoint[i].
+					desc.bEndpointAddress);
+
+			switch (intf->altsetting[select_alt].endpoint[i].
+				desc.bEndpointAddress) {
+			case EM28XX_INTERRUPT_EP:
+				/* currently not implemented */
+				break;
+			case EM28XX_ANALOG_VIDEO_EP:
+				/* registered by default already which
+				   is bogus */
+				em28xx_info("FOUND ATV EP\n");
+				dev->has_atv = 1;
+				break;
+			case EM28XX_ANALOG_AUDIO_EP:
+				em28xx_info("Found PCMAUDIO EP\n");
+				dev->has_alsa_audio = 1;
+				break;
+			case EM28XX_DIGITALTV_EP:
+				em28xx_info("Found MPEG-TS EP\n");
+				dev->has_dvb = 1;
+				break;
+			default:
+				return -EINVAL;
+			}
+		}
+		break;
 	case EM2861_BOARD_PLEXTOR_PX_TV100U:
 		/* FIXME guess */
 		/* Turn on analog audio output */
@@ -1748,6 +1822,7 @@
 
 	/* Unlock device */
 	em28xx_set_mode(dev, EM28XX_SUSPEND);
+	return 0;
 }
 
 static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
@@ -2191,8 +2266,12 @@
 	dev->em28xx_write_regs_req = em28xx_write_regs_req;
 	dev->em28xx_read_reg_req = em28xx_read_reg_req;
 	dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;
+	dev->has_dvb = dev->board.has_dvb;
 
-	em28xx_pre_card_setup(dev);
+	retval = em28xx_pre_card_setup(dev, interface);
+
+	if (retval)
+		return retval;
 
 	if (!dev->board.is_em2800) {
 		/* Sets I2C speed to 100 KHz */
@@ -2265,16 +2344,19 @@
 
 	em28xx_add_into_devlist(dev);
 
-	retval = em28xx_register_analog_devices(dev);
-	if (retval < 0) {
-		em28xx_release_resources(dev);
-		goto fail_reg_devices;
+	if (dev->has_atv) {
+		retval = em28xx_register_analog_devices(dev);
+		if (retval < 0) {
+			em28xx_release_resources(dev);
+			goto fail_reg_devices;
+		}
 	}
 
 	em28xx_init_extension(dev);
 
-	/* Save some power by putting tuner to sleep */
-	v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby);
+	if (dev->has_atv)
+		/* Save some power by putting tuner to sleep */
+		v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby);
 
 	return 0;
 
diff -r 315bc4b65b4f linux/drivers/media/video/em28xx/em28xx-core.c
--- a/linux/drivers/media/video/em28xx/em28xx-core.c	Sun May 17 12:28:55 2009 +0000
+++ b/linux/drivers/media/video/em28xx/em28xx-core.c	Sat May 23 16:03:39 2009 +0200
@@ -68,7 +68,12 @@
 				   char *buf, int len)
 {
 	int ret;
-	int pipe = usb_rcvctrlpipe(dev->udev, 0);
+	int pipe;
+
+	if (dev->lock_control_commands)
+		return -EINVAL;
+
+	pipe = usb_rcvctrlpipe(dev->udev, 0);
 
 	if (dev->state & DEV_DISCONNECTED)
 		return -ENODEV;
@@ -143,7 +148,12 @@
 				 int len)
 {
 	int ret;
-	int pipe = usb_sndctrlpipe(dev->udev, 0);
+	int pipe;
+
+	if (dev->lock_control_commands)
+		return -EINVAL;
+
+	pipe = usb_sndctrlpipe(dev->udev, 0);
 
 	if (dev->state & DEV_DISCONNECTED)
 		return -ENODEV;
diff -r 315bc4b65b4f linux/drivers/media/video/em28xx/em28xx-dvb.c
--- a/linux/drivers/media/video/em28xx/em28xx-dvb.c	Sun May 17 12:28:55 2009 +0000
+++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c	Sat May 23 16:03:39 2009 +0200
@@ -301,17 +301,20 @@
 		goto fail_adapter;
 	}
 
-	/* Ensure all frontends negotiate bus access */
-	dvb->frontend->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl;
+	if (dev->has_frontend)
+		/* Ensure all frontends negotiate bus access */
+		dvb->frontend->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl;
 
 	dvb->adapter.priv = dev;
 
-	/* register frontend */
-	result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
-	if (result < 0) {
-		printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n",
-		       dev->name, result);
-		goto fail_frontend;
+	if (dev->has_frontend) {
+		/* register frontend */
+		result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
+		if (result < 0) {
+			printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n",
+					dev->name, result);
+			goto fail_frontend;
+		}
 	}
 
 	/* register demux stuff */
@@ -349,19 +352,23 @@
 		goto fail_fe_hw;
 	}
 
-	dvb->fe_mem.source = DMX_MEMORY_FE;
-	result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
-	if (result < 0) {
-		printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
-		       dev->name, result);
-		goto fail_fe_mem;
-	}
+	if (dev->has_frontend) {
+		dvb->fe_mem.source = DMX_MEMORY_FE;
+		result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx,
+							&dvb->fe_mem);
+		if (result < 0) {
+			printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
+					dev->name, result);
+			goto fail_fe_mem;
+		}
 
-	result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
-	if (result < 0) {
-		printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n",
-		       dev->name, result);
-		goto fail_fe_conn;
+		result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx,
+								&dvb->fe_hw);
+		if (result < 0) {
+			printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n",
+					dev->name, result);
+			goto fail_fe_conn;
+		}
 	}
 
 	/* register network adapter */
@@ -377,9 +384,12 @@
 fail_dmxdev:
 	dvb_dmx_release(&dvb->demux);
 fail_dmx:
-	dvb_unregister_frontend(dvb->frontend);
+	if (dev->has_frontend)
+		dvb_unregister_frontend(dvb->frontend);
 fail_frontend:
-	dvb_frontend_detach(dvb->frontend);
+	if (dev->has_frontend)
+		dvb_frontend_detach(dvb->frontend);
+
 	dvb_unregister_adapter(&dvb->adapter);
 fail_adapter:
 	return result;
@@ -388,12 +398,17 @@
 static void unregister_dvb(struct em28xx_dvb *dvb)
 {
 	dvb_net_release(&dvb->net);
-	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
-	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+	if (dvb->frontend) {
+		dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+		dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+	}
 	dvb_dmxdev_release(&dvb->dmxdev);
 	dvb_dmx_release(&dvb->demux);
-	dvb_unregister_frontend(dvb->frontend);
-	dvb_frontend_detach(dvb->frontend);
+	if (dvb->frontend) {
+		dvb_unregister_frontend(dvb->frontend);
+		dvb_frontend_detach(dvb->frontend);
+		dvb->frontend = NULL;
+	}
 	dvb_unregister_adapter(&dvb->adapter);
 }
 
@@ -403,8 +418,9 @@
 	int result = 0;
 	struct em28xx_dvb *dvb;
 
-	if (!dev->board.has_dvb) {
+	if (!dev->board.has_dvb && dev->has_dvb == 0) {
 		/* This device does not support the extension */
+		printk(KERN_INFO "em28xx_dvb: This device does not support the DVB extension\n");
 		return 0;
 	}
 
@@ -415,6 +431,7 @@
 		return -ENOMEM;
 	}
 	dev->dvb = dvb;
+	dev->has_frontend = 1; /* defaulting for old devices */
 
 	em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
 	/* init frontend */
@@ -465,21 +482,26 @@
 		}
 		break;
 #endif
+	case EM2800_BOARD_UNKNOWN:
+		dev->has_frontend = 0;
+		break;
 	default:
 		printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
 				" isn't supported yet\n",
 		       dev->name);
 		break;
 	}
-	if (NULL == dvb->frontend) {
+	if (NULL == dvb->frontend && dev->has_frontend == 1) {
 		printk(KERN_ERR
 		       "%s/2: frontend initialization failed\n",
 		       dev->name);
 		result = -EINVAL;
 		goto out_free;
 	}
-	/* define general-purpose callback pointer */
-	dvb->frontend->callback = em28xx_tuner_callback;
+
+	if (dev->has_frontend)
+		/* define general-purpose callback pointer */
+		dvb->frontend->callback = em28xx_tuner_callback;
 
 	/* register everything */
 	result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
diff -r 315bc4b65b4f linux/drivers/media/video/em28xx/em28xx.h
--- a/linux/drivers/media/video/em28xx/em28xx.h	Sun May 17 12:28:55 2009 +0000
+++ b/linux/drivers/media/video/em28xx/em28xx.h	Sat May 23 16:03:39 2009 +0200
@@ -429,6 +429,11 @@
 #define EM28XX_AUDIO   0x10
 #define EM28XX_DVB     0x20
 
+#define EM28XX_INTERRUPT_EP	0x81
+#define EM28XX_ANALOG_VIDEO_EP	0x82
+#define EM28XX_ANALOG_AUDIO_EP	0x83
+#define EM28XX_DIGITALTV_EP	0x84
+
 struct em28xx_audio {
 	char name[50];
 	char *transfer_buffer[EM28XX_AUDIO_BUFS];
@@ -479,6 +484,13 @@
 	unsigned int stream_on:1;	/* Locks streams */
 	unsigned int has_audio_class:1;
 	unsigned int has_alsa_audio:1;
+	/* some devices do not have a frontend,
+	   eg reading TS stream only from alternative
+	   sources eg. mpeg encoder devices */
+	unsigned int has_frontend:1;
+	/* private section, only if _this_ board supports mpeg-ts */
+	unsigned int has_dvb:1;
+	unsigned int has_atv:1;
 
 	struct em28xx_fmt *format;
 
@@ -580,6 +592,8 @@
 	struct delayed_work sbutton_query_work;
 
 	struct em28xx_dvb *dvb;
+
+	unsigned int lock_control_commands:1;
 };
 
 struct em28xx_ops {
@@ -645,7 +659,6 @@
 
 /* Provided by em28xx-cards.c */
 extern int em2800_variant_detect(struct usb_device *udev, int model);
-extern void em28xx_pre_card_setup(struct em28xx *dev);
 extern void em28xx_card_setup(struct em28xx *dev);
 extern struct em28xx_board em28xx_boards[];
 extern struct usb_device_id em28xx_id_table[];

Reply via email to