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[];