Re: [PATCH] em28xx device mode detection based on endpoints

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On Sat, May 23, 2009 at 4:49 PM, Markus Rechberger
<mrechberger@xxxxxxxxx> wrote:
> On Sat, May 23, 2009 at 4:04 PM, Markus Rechberger
> <mrechberger@xxxxxxxxx> wrote:
>> 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.
>>
>

attached patch fixes the deregistration, as well loads the em28xx-dvb
module automatically as soon as an MPEG-TS endpoint was found.

Signed-off-by: Markus Rechberger <mrechberger@xxxxxxxxx>

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 17:03:00 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)
@@ -2119,7 +2194,7 @@
 	else if (dev->has_alsa_audio)
 		request_module("em28xx-alsa");
 
-	if (dev->board.has_dvb)
+	if (dev->board.has_dvb || dev->has_dvb)
 		request_module("em28xx-dvb");
 }
 
@@ -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 17:03:00 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 17:03:00 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 */
@@ -369,17 +376,22 @@
 	return 0;
 
 fail_fe_conn:
-	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+	if (dev->has_frontend)
+		dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
 fail_fe_mem:
-	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+	if (dev->has_frontend)
+		dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
 fail_fe_hw:
 	dvb_dmxdev_release(&dvb->dmxdev);
 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 +400,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 +420,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 +433,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 +484,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);
@@ -500,7 +524,7 @@
 
 static int dvb_fini(struct em28xx *dev)
 {
-	if (!dev->board.has_dvb) {
+	if (!dev->board.has_dvb && dev->has_dvb == 0) {
 		/* This device does not support the extension */
 		return 0;
 	}
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 17:03:00 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[];

[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux