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