diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index b8561dfb6c8c7bc08880a9057239f6854c582a5f..1cc67ae3bfabb428ef63a2c3b1a22614ef4445ae 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -512,7 +512,10 @@ static int create_urbs(struct gspca_dev *gspca_dev, if (!gspca_dev->cam.bulk) { /* isoc */ /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */ - psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + if (gspca_dev->pkt_size == 0) + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + else + psize = gspca_dev->pkt_size; npkt = gspca_dev->cam.npkt; if (npkt == 0) npkt = 32; /* default value */ @@ -597,13 +600,18 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) /* set the higher alternate setting and * loop until urb submit succeeds */ gspca_dev->alt = gspca_dev->nbalt; + if (gspca_dev->sd_desc->isoc_init) { + ret = gspca_dev->sd_desc->isoc_init(gspca_dev); + if (ret < 0) + goto out; + } + ep = get_ep(gspca_dev); + if (ep == NULL) { + ret = -EIO; + goto out; + } for (;;) { PDEBUG(D_STREAM, "init transfer alt %d", gspca_dev->alt); - ep = get_ep(gspca_dev); - if (ep == NULL) { - ret = -EIO; - goto out; - } ret = create_urbs(gspca_dev, ep); if (ret < 0) goto out; @@ -628,21 +636,32 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) /* submit the URBs */ for (n = 0; n < gspca_dev->nurbs; n++) { ret = usb_submit_urb(gspca_dev->urb[n], GFP_KERNEL); - if (ret < 0) { - PDEBUG(D_ERR|D_STREAM, - "usb_submit_urb [%d] err %d", n, ret); - gspca_dev->streaming = 0; - destroy_urbs(gspca_dev); - if (ret == -ENOSPC) { - msleep(20); /* wait for kill - * complete */ - break; /* try the previous alt */ - } - goto out; - } + if (ret < 0) + break; } if (ret >= 0) break; + PDEBUG(D_ERR|D_STREAM, + "usb_submit_urb alt %d err %d", gspca_dev->alt, ret); + gspca_dev->streaming = 0; + destroy_urbs(gspca_dev); + if (ret != -ENOSPC) + goto out; + + /* the bandwidth is not wide enough + * negociate or try a lower alternate setting */ + msleep(20); /* wait for kill complete */ + if (gspca_dev->sd_desc->isoc_nego) { + ret = gspca_dev->sd_desc->isoc_nego(gspca_dev); + if (ret < 0) + goto out; + } else { + ep = get_ep(gspca_dev); + if (ep == NULL) { + ret = -EIO; + goto out; + } + } } out: mutex_unlock(&gspca_dev->usb_lock); diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 46c4effdfcd5b03776f6798eaafb58aebe9a78fa..70b1fd830876af2bee2a5c2d6f85c562ab2c1112 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -98,9 +98,11 @@ struct sd_desc { /* mandatory operations */ cam_cf_op config; /* called on probe */ cam_op init; /* called on probe and resume */ - cam_op start; /* called on stream on */ + cam_op start; /* called on stream on after URBs creation */ cam_pkt_op pkt_scan; /* optional operations */ + cam_op isoc_init; /* called on stream on before getting the EP */ + cam_op isoc_nego; /* called when URB submit failed with NOSPC */ cam_v_op stopN; /* called on stream off - main alt */ cam_v_op stop0; /* called on stream off & disconnect - alt 0 */ cam_v_op dq_callback; /* called when a frame has been dequeued */ @@ -178,6 +180,7 @@ struct gspca_dev { __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ __u8 nbalt; /* number of USB alternate settings */ + u16 pkt_size; /* ISOC packet size */ }; int gspca_dev_probe(struct usb_interface *intf,