aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/usb/em28xx/em28xx-cards.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/usb/em28xx/em28xx-cards.c')
-rw-r--r--drivers/media/usb/em28xx/em28xx-cards.c95
1 files changed, 77 insertions, 18 deletions
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index 873b52f71ab..a6f00181c24 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -6,6 +6,7 @@
Markus Rechberger <mrechberger@gmail.com>
Mauro Carvalho Chehab <mchehab@infradead.org>
Sascha Sommer <saschasommer@freenet.de>
+ Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -3209,26 +3210,67 @@ static int em28xx_usb_probe(struct usb_interface *interface,
if (udev->speed == USB_SPEED_HIGH)
size = size * hb_mult(sizedescr);
- if (usb_endpoint_xfer_isoc(e) &&
- usb_endpoint_dir_in(e)) {
+ if (usb_endpoint_dir_in(e)) {
switch (e->bEndpointAddress) {
- case EM28XX_EP_AUDIO:
- has_audio = true;
- break;
- case EM28XX_EP_ANALOG:
+ case 0x82:
has_video = true;
- dev->alt_max_pkt_size_isoc[i] = size;
+ if (usb_endpoint_xfer_isoc(e)) {
+ dev->analog_ep_isoc =
+ e->bEndpointAddress;
+ dev->alt_max_pkt_size_isoc[i] = size;
+ } else if (usb_endpoint_xfer_bulk(e)) {
+ dev->analog_ep_bulk =
+ e->bEndpointAddress;
+ }
+ break;
+ case 0x83:
+ if (usb_endpoint_xfer_isoc(e)) {
+ has_audio = true;
+ } else {
+ printk(KERN_INFO DRIVER_NAME
+ ": error: skipping audio endpoint 0x83, because it uses bulk transfers !\n");
+ }
break;
- case EM28XX_EP_DIGITAL:
- has_dvb = true;
- if (size > dev->dvb_max_pkt_size_isoc) {
- dev->dvb_max_pkt_size_isoc =
- size;
- dev->dvb_alt_isoc = i;
+ case 0x84:
+ if (has_video &&
+ (usb_endpoint_xfer_bulk(e))) {
+ dev->analog_ep_bulk =
+ e->bEndpointAddress;
+ } else {
+ has_dvb = true;
+ if (usb_endpoint_xfer_isoc(e)) {
+ dev->dvb_ep_isoc = e->bEndpointAddress;
+ if (size > dev->dvb_max_pkt_size_isoc) {
+ dev->dvb_max_pkt_size_isoc = size;
+ dev->dvb_alt_isoc = i;
+ }
+ } else {
+ dev->dvb_ep_bulk = e->bEndpointAddress;
+ }
}
break;
}
}
+ /* NOTE:
+ * Old logic with support for isoc transfers only was:
+ * 0x82 isoc => analog
+ * 0x83 isoc => audio
+ * 0x84 isoc => digital
+ *
+ * New logic with support for bulk transfers
+ * 0x82 isoc => analog
+ * 0x82 bulk => analog
+ * 0x83 isoc* => audio
+ * 0x84 isoc => digital
+ * 0x84 bulk => analog or digital**
+ * (*: audio should always be isoc)
+ * (**: analog, if ep 0x82 is isoc, otherwise digital)
+ *
+ * The new logic preserves backwards compatibility and
+ * reflects the endpoint configurations we have seen
+ * so far. But there might be devices for which this
+ * logic is not sufficient...
+ */
}
}
@@ -3289,6 +3331,12 @@ static int em28xx_usb_probe(struct usb_interface *interface,
goto err_free;
}
+ /* Select USB transfer types to use */
+ if (has_video && !dev->analog_ep_isoc)
+ dev->analog_xfer_bulk = 1;
+ if (has_dvb && !dev->dvb_ep_isoc)
+ dev->dvb_xfer_bulk = 1;
+
snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr);
dev->devno = nr;
dev->model = id->driver_info;
@@ -3323,12 +3371,23 @@ static int em28xx_usb_probe(struct usb_interface *interface,
}
if (has_dvb) {
- /* pre-allocate DVB isoc transfer buffers */
- retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, 0,
- EM28XX_DVB_NUM_BUFS,
- dev->dvb_max_pkt_size_isoc,
- EM28XX_DVB_NUM_ISOC_PACKETS);
+ /* pre-allocate DVB usb transfer buffers */
+ if (dev->dvb_xfer_bulk) {
+ retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE,
+ dev->dvb_xfer_bulk,
+ EM28XX_DVB_NUM_BUFS,
+ 512,
+ EM28XX_DVB_BULK_PACKET_MULTIPLIER);
+ } else {
+ retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE,
+ dev->dvb_xfer_bulk,
+ EM28XX_DVB_NUM_BUFS,
+ dev->dvb_max_pkt_size_isoc,
+ EM28XX_DVB_NUM_ISOC_PACKETS);
+ }
if (retval) {
+ printk(DRIVER_NAME
+ ": Failed to pre-allocate USB transfer buffers for DVB.\n");
goto unlock_and_free;
}
}