aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/usb/em28xx
diff options
context:
space:
mode:
authorFrank Schaefer <fschaefer.oss@googlemail.com>2012-11-08 12:11:52 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-12-22 15:17:52 -0500
commitc647a91a2558c4031eddd013e5860ca5a41363a7 (patch)
tree90a0b103355b59ef61fa7d2f88a6ec78e24e189b /drivers/media/usb/em28xx
parentc8e9d95b41f2a441b2af0a1899448dd45ad7632d (diff)
[media] em28xx: improve USB endpoint logic, also use bulk transfers
The current enpoint logic ignores all bulk endpoints and uses a fixed mapping between endpint addresses and the supported data stream types (analog/audio/DVB): Ep 0x82, isoc => analog Ep 0x83, isoc => audio Ep 0x84, isoc => DVB Now that the code can also do bulk transfers, the endpoint logic has to be extended to also consider bulk endpoints. The new logic preserves backwards compatibility and reflects the endpoint configurations we have seen so far: Ep 0x82, isoc => analog Ep 0x82, bulk => analog Ep 0x83, isoc* => audio Ep 0x84, isoc => digital Ep 0x84, bulk => analog or digital** (*: audio should always be isoc) (**: analog, if ep 0x82 is isoc, otherwise digital) [mchehab@redhat.com: Fix a CodingStyle issue: don't break strings into separate lines] Signed-off-by: Frank Schäfer <fschaefer.oss@googlemail.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/usb/em28xx')
-rw-r--r--drivers/media/usb/em28xx/em28xx-cards.c95
-rw-r--r--drivers/media/usb/em28xx/em28xx-core.c32
-rw-r--r--drivers/media/usb/em28xx/em28xx-dvb.c34
-rw-r--r--drivers/media/usb/em28xx/em28xx-reg.h4
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c10
-rw-r--r--drivers/media/usb/em28xx/em28xx.h12
6 files changed, 147 insertions, 40 deletions
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index 873b52f71ab2..a6f00181c246 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -6,6 +6,7 @@
6 Markus Rechberger <mrechberger@gmail.com> 6 Markus Rechberger <mrechberger@gmail.com>
7 Mauro Carvalho Chehab <mchehab@infradead.org> 7 Mauro Carvalho Chehab <mchehab@infradead.org>
8 Sascha Sommer <saschasommer@freenet.de> 8 Sascha Sommer <saschasommer@freenet.de>
9 Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com>
9 10
10 This program is free software; you can redistribute it and/or modify 11 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by 12 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,
3209 if (udev->speed == USB_SPEED_HIGH) 3210 if (udev->speed == USB_SPEED_HIGH)
3210 size = size * hb_mult(sizedescr); 3211 size = size * hb_mult(sizedescr);
3211 3212
3212 if (usb_endpoint_xfer_isoc(e) && 3213 if (usb_endpoint_dir_in(e)) {
3213 usb_endpoint_dir_in(e)) {
3214 switch (e->bEndpointAddress) { 3214 switch (e->bEndpointAddress) {
3215 case EM28XX_EP_AUDIO: 3215 case 0x82:
3216 has_audio = true;
3217 break;
3218 case EM28XX_EP_ANALOG:
3219 has_video = true; 3216 has_video = true;
3220 dev->alt_max_pkt_size_isoc[i] = size; 3217 if (usb_endpoint_xfer_isoc(e)) {
3218 dev->analog_ep_isoc =
3219 e->bEndpointAddress;
3220 dev->alt_max_pkt_size_isoc[i] = size;
3221 } else if (usb_endpoint_xfer_bulk(e)) {
3222 dev->analog_ep_bulk =
3223 e->bEndpointAddress;
3224 }
3225 break;
3226 case 0x83:
3227 if (usb_endpoint_xfer_isoc(e)) {
3228 has_audio = true;
3229 } else {
3230 printk(KERN_INFO DRIVER_NAME
3231 ": error: skipping audio endpoint 0x83, because it uses bulk transfers !\n");
3232 }
3221 break; 3233 break;
3222 case EM28XX_EP_DIGITAL: 3234 case 0x84:
3223 has_dvb = true; 3235 if (has_video &&
3224 if (size > dev->dvb_max_pkt_size_isoc) { 3236 (usb_endpoint_xfer_bulk(e))) {
3225 dev->dvb_max_pkt_size_isoc = 3237 dev->analog_ep_bulk =
3226 size; 3238 e->bEndpointAddress;
3227 dev->dvb_alt_isoc = i; 3239 } else {
3240 has_dvb = true;
3241 if (usb_endpoint_xfer_isoc(e)) {
3242 dev->dvb_ep_isoc = e->bEndpointAddress;
3243 if (size > dev->dvb_max_pkt_size_isoc) {
3244 dev->dvb_max_pkt_size_isoc = size;
3245 dev->dvb_alt_isoc = i;
3246 }
3247 } else {
3248 dev->dvb_ep_bulk = e->bEndpointAddress;
3249 }
3228 } 3250 }
3229 break; 3251 break;
3230 } 3252 }
3231 } 3253 }
3254 /* NOTE:
3255 * Old logic with support for isoc transfers only was:
3256 * 0x82 isoc => analog
3257 * 0x83 isoc => audio
3258 * 0x84 isoc => digital
3259 *
3260 * New logic with support for bulk transfers
3261 * 0x82 isoc => analog
3262 * 0x82 bulk => analog
3263 * 0x83 isoc* => audio
3264 * 0x84 isoc => digital
3265 * 0x84 bulk => analog or digital**
3266 * (*: audio should always be isoc)
3267 * (**: analog, if ep 0x82 is isoc, otherwise digital)
3268 *
3269 * The new logic preserves backwards compatibility and
3270 * reflects the endpoint configurations we have seen
3271 * so far. But there might be devices for which this
3272 * logic is not sufficient...
3273 */
3232 } 3274 }
3233 } 3275 }
3234 3276
@@ -3289,6 +3331,12 @@ static int em28xx_usb_probe(struct usb_interface *interface,
3289 goto err_free; 3331 goto err_free;
3290 } 3332 }
3291 3333
3334 /* Select USB transfer types to use */
3335 if (has_video && !dev->analog_ep_isoc)
3336 dev->analog_xfer_bulk = 1;
3337 if (has_dvb && !dev->dvb_ep_isoc)
3338 dev->dvb_xfer_bulk = 1;
3339
3292 snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr); 3340 snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr);
3293 dev->devno = nr; 3341 dev->devno = nr;
3294 dev->model = id->driver_info; 3342 dev->model = id->driver_info;
@@ -3323,12 +3371,23 @@ static int em28xx_usb_probe(struct usb_interface *interface,
3323 } 3371 }
3324 3372
3325 if (has_dvb) { 3373 if (has_dvb) {
3326 /* pre-allocate DVB isoc transfer buffers */ 3374 /* pre-allocate DVB usb transfer buffers */
3327 retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, 0, 3375 if (dev->dvb_xfer_bulk) {
3328 EM28XX_DVB_NUM_BUFS, 3376 retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE,
3329 dev->dvb_max_pkt_size_isoc, 3377 dev->dvb_xfer_bulk,
3330 EM28XX_DVB_NUM_ISOC_PACKETS); 3378 EM28XX_DVB_NUM_BUFS,
3379 512,
3380 EM28XX_DVB_BULK_PACKET_MULTIPLIER);
3381 } else {
3382 retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE,
3383 dev->dvb_xfer_bulk,
3384 EM28XX_DVB_NUM_BUFS,
3385 dev->dvb_max_pkt_size_isoc,
3386 EM28XX_DVB_NUM_ISOC_PACKETS);
3387 }
3331 if (retval) { 3388 if (retval) {
3389 printk(DRIVER_NAME
3390 ": Failed to pre-allocate USB transfer buffers for DVB.\n");
3332 goto unlock_and_free; 3391 goto unlock_and_free;
3333 } 3392 }
3334 } 3393 }
diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c
index cdf4cd24007d..b10d959fefe5 100644
--- a/drivers/media/usb/em28xx/em28xx-core.c
+++ b/drivers/media/usb/em28xx/em28xx-core.c
@@ -847,11 +847,13 @@ set_alt:
847 if (dev->alt != prev_alt) { 847 if (dev->alt != prev_alt) {
848 if (dev->analog_xfer_bulk) { 848 if (dev->analog_xfer_bulk) {
849 dev->max_pkt_size = 512; /* USB 2.0 spec */ 849 dev->max_pkt_size = 512; /* USB 2.0 spec */
850 dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER;
850 } else { /* isoc */ 851 } else { /* isoc */
851 em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", 852 em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n",
852 min_pkt_size, dev->alt); 853 min_pkt_size, dev->alt);
853 dev->max_pkt_size = 854 dev->max_pkt_size =
854 dev->alt_max_pkt_size_isoc[dev->alt]; 855 dev->alt_max_pkt_size_isoc[dev->alt];
856 dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS;
855 } 857 }
856 em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", 858 em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n",
857 dev->alt, dev->max_pkt_size); 859 dev->alt, dev->max_pkt_size);
@@ -1054,10 +1056,28 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk,
1054 1056
1055 em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode); 1057 em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode);
1056 1058
1057 if (mode == EM28XX_DIGITAL_MODE) 1059 /* Check mode and if we have an endpoint for the selected
1060 transfer type, select buffer */
1061 if (mode == EM28XX_DIGITAL_MODE) {
1062 if ((xfer_bulk && !dev->dvb_ep_bulk) ||
1063 (!xfer_bulk && !dev->dvb_ep_isoc)) {
1064 em28xx_errdev("no endpoint for DVB mode and transfer type %d\n",
1065 xfer_bulk > 0);
1066 return -EINVAL;
1067 }
1058 usb_bufs = &dev->usb_ctl.digital_bufs; 1068 usb_bufs = &dev->usb_ctl.digital_bufs;
1059 else 1069 } else if (mode == EM28XX_ANALOG_MODE) {
1070 if ((xfer_bulk && !dev->analog_ep_bulk) ||
1071 (!xfer_bulk && !dev->analog_ep_isoc)) {
1072 em28xx_errdev("no endpoint for analog mode and transfer type %d\n",
1073 xfer_bulk > 0);
1074 return -EINVAL;
1075 }
1060 usb_bufs = &dev->usb_ctl.analog_bufs; 1076 usb_bufs = &dev->usb_ctl.analog_bufs;
1077 } else {
1078 em28xx_errdev("invalid mode selected\n");
1079 return -EINVAL;
1080 }
1061 1081
1062 /* De-allocates all pending stuff */ 1082 /* De-allocates all pending stuff */
1063 em28xx_uninit_usb_xfer(dev, mode); 1083 em28xx_uninit_usb_xfer(dev, mode);
@@ -1113,8 +1133,8 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk,
1113 if (xfer_bulk) { /* bulk */ 1133 if (xfer_bulk) { /* bulk */
1114 pipe = usb_rcvbulkpipe(dev->udev, 1134 pipe = usb_rcvbulkpipe(dev->udev,
1115 mode == EM28XX_ANALOG_MODE ? 1135 mode == EM28XX_ANALOG_MODE ?
1116 EM28XX_EP_ANALOG : 1136 dev->analog_ep_bulk :
1117 EM28XX_EP_DIGITAL); 1137 dev->dvb_ep_bulk);
1118 usb_fill_bulk_urb(urb, dev->udev, pipe, 1138 usb_fill_bulk_urb(urb, dev->udev, pipe,
1119 usb_bufs->transfer_buffer[i], sb_size, 1139 usb_bufs->transfer_buffer[i], sb_size,
1120 em28xx_irq_callback, dev); 1140 em28xx_irq_callback, dev);
@@ -1122,8 +1142,8 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk,
1122 } else { /* isoc */ 1142 } else { /* isoc */
1123 pipe = usb_rcvisocpipe(dev->udev, 1143 pipe = usb_rcvisocpipe(dev->udev,
1124 mode == EM28XX_ANALOG_MODE ? 1144 mode == EM28XX_ANALOG_MODE ?
1125 EM28XX_EP_ANALOG : 1145 dev->analog_ep_isoc :
1126 EM28XX_EP_DIGITAL); 1146 dev->dvb_ep_isoc);
1127 usb_fill_int_urb(urb, dev->udev, pipe, 1147 usb_fill_int_urb(urb, dev->udev, pipe,
1128 usb_bufs->transfer_buffer[i], sb_size, 1148 usb_bufs->transfer_buffer[i], sb_size,
1129 em28xx_irq_callback, dev, 1); 1149 em28xx_irq_callback, dev, 1);
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 24962520df80..a70b19e07e37 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -176,25 +176,39 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb)
176{ 176{
177 int rc; 177 int rc;
178 struct em28xx *dev = dvb->adapter.priv; 178 struct em28xx *dev = dvb->adapter.priv;
179 int max_dvb_packet_size; 179 int dvb_max_packet_size, packet_multiplier, dvb_alt;
180
181 if (dev->dvb_xfer_bulk) {
182 if (!dev->dvb_ep_bulk)
183 return -ENODEV;
184 dvb_max_packet_size = 512; /* USB 2.0 spec */
185 packet_multiplier = EM28XX_DVB_BULK_PACKET_MULTIPLIER;
186 dvb_alt = 0;
187 } else { /* isoc */
188 if (!dev->dvb_ep_isoc)
189 return -ENODEV;
190 dvb_max_packet_size = dev->dvb_max_pkt_size_isoc;
191 if (dvb_max_packet_size < 0)
192 return dvb_max_packet_size;
193 packet_multiplier = EM28XX_DVB_NUM_ISOC_PACKETS;
194 dvb_alt = dev->dvb_alt_isoc;
195 }
180 196
181 usb_set_interface(dev->udev, 0, dev->dvb_alt_isoc); 197 usb_set_interface(dev->udev, 0, dvb_alt);
182 rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); 198 rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
183 if (rc < 0) 199 if (rc < 0)
184 return rc; 200 return rc;
185 201
186 max_dvb_packet_size = dev->dvb_max_pkt_size_isoc;
187 if (max_dvb_packet_size < 0)
188 return max_dvb_packet_size;
189 dprintk(1, "Using %d buffers each with %d x %d bytes\n", 202 dprintk(1, "Using %d buffers each with %d x %d bytes\n",
190 EM28XX_DVB_NUM_BUFS, 203 EM28XX_DVB_NUM_BUFS,
191 EM28XX_DVB_NUM_ISOC_PACKETS, 204 packet_multiplier,
192 max_dvb_packet_size); 205 dvb_max_packet_size);
193 206
194 return em28xx_init_usb_xfer(dev, EM28XX_DIGITAL_MODE, 0, 207 return em28xx_init_usb_xfer(dev, EM28XX_DIGITAL_MODE,
208 dev->dvb_xfer_bulk,
195 EM28XX_DVB_NUM_BUFS, 209 EM28XX_DVB_NUM_BUFS,
196 max_dvb_packet_size, 210 dvb_max_packet_size,
197 EM28XX_DVB_NUM_ISOC_PACKETS, 211 packet_multiplier,
198 em28xx_dvb_urb_data_copy); 212 em28xx_dvb_urb_data_copy);
199} 213}
200 214
diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h
index 2ad357354d81..885089e22bcd 100644
--- a/drivers/media/usb/em28xx/em28xx-reg.h
+++ b/drivers/media/usb/em28xx/em28xx-reg.h
@@ -13,9 +13,9 @@
13#define EM_GPO_3 (1 << 3) 13#define EM_GPO_3 (1 << 3)
14 14
15/* em28xx endpoints */ 15/* em28xx endpoints */
16#define EM28XX_EP_ANALOG 0x82 16/* 0x82: (always ?) analog */
17#define EM28XX_EP_AUDIO 0x83 17#define EM28XX_EP_AUDIO 0x83
18#define EM28XX_EP_DIGITAL 0x84 18/* 0x84: digital or analog */
19 19
20/* em2800 registers */ 20/* em2800 registers */
21#define EM2800_R08_AUDIOSRC 0x08 21#define EM2800_R08_AUDIOSRC 0x08
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index d9c15b6da1af..c7e23dd73b88 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -788,16 +788,18 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
788 788
789 if (urb_init) { 789 if (urb_init) {
790 if (em28xx_vbi_supported(dev) == 1) 790 if (em28xx_vbi_supported(dev) == 1)
791 rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, 0, 791 rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE,
792 dev->analog_xfer_bulk,
792 EM28XX_NUM_BUFS, 793 EM28XX_NUM_BUFS,
793 dev->max_pkt_size, 794 dev->max_pkt_size,
794 EM28XX_NUM_ISOC_PACKETS, 795 dev->packet_multiplier,
795 em28xx_urb_data_copy_vbi); 796 em28xx_urb_data_copy_vbi);
796 else 797 else
797 rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, 0, 798 rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE,
799 dev->analog_xfer_bulk,
798 EM28XX_NUM_BUFS, 800 EM28XX_NUM_BUFS,
799 dev->max_pkt_size, 801 dev->max_pkt_size,
800 EM28XX_NUM_ISOC_PACKETS, 802 dev->packet_multiplier,
801 em28xx_urb_data_copy); 803 em28xx_urb_data_copy);
802 if (rc < 0) 804 if (rc < 0)
803 goto fail; 805 goto fail;
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index f5be5229f304..aa413bd76dec 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -165,6 +165,12 @@
165#define EM28XX_NUM_ISOC_PACKETS 64 165#define EM28XX_NUM_ISOC_PACKETS 64
166#define EM28XX_DVB_NUM_ISOC_PACKETS 64 166#define EM28XX_DVB_NUM_ISOC_PACKETS 64
167 167
168/* bulk transfers: transfer buffer size = packet size * packet multiplier
169 USB 2.0 spec says bulk packet size is always 512 bytes
170 */
171#define EM28XX_BULK_PACKET_MULTIPLIER 384
172#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 384
173
168#define EM28XX_INTERLACED_DEFAULT 1 174#define EM28XX_INTERLACED_DEFAULT 1
169 175
170/* 176/*
@@ -584,8 +590,14 @@ struct em28xx {
584 590
585 /* usb transfer */ 591 /* usb transfer */
586 struct usb_device *udev; /* the usb device */ 592 struct usb_device *udev; /* the usb device */
593 u8 analog_ep_isoc; /* address of isoc endpoint for analog */
594 u8 analog_ep_bulk; /* address of bulk endpoint for analog */
595 u8 dvb_ep_isoc; /* address of isoc endpoint for DVB */
596 u8 dvb_ep_bulk; /* address of bulk endpoint for DVC */
587 int alt; /* alternate setting */ 597 int alt; /* alternate setting */
588 int max_pkt_size; /* max packet size of the selected ep at alt */ 598 int max_pkt_size; /* max packet size of the selected ep at alt */
599 int packet_multiplier; /* multiplier for wMaxPacketSize, used for
600 URB buffer size definition */
589 int num_alt; /* number of alternative settings */ 601 int num_alt; /* number of alternative settings */
590 unsigned int *alt_max_pkt_size_isoc; /* array of isoc wMaxPacketSize */ 602 unsigned int *alt_max_pkt_size_isoc; /* array of isoc wMaxPacketSize */
591 unsigned int analog_xfer_bulk:1; /* use bulk instead of isoc 603 unsigned int analog_xfer_bulk:1; /* use bulk instead of isoc