Commit 27b12023 authored by Jean-François Moine's avatar Jean-François Moine Committed by Mauro Carvalho Chehab

[media] gspca - main: Use a better altsetting for image transfer

Previously image transfer used to use the highest altsetting, which, most of
the time, took the whole USB bandwidth.
Now, the altsetting is chosen according to an estimation of the bandwidth
needed for image transfer.
Signed-off-by: default avatarJean-François Moine <moinejf@free.fr>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 2d06d5dd
......@@ -629,53 +629,104 @@ static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,
return NULL;
}
/* compute the minimum bandwidth for the current transfer */
static u32 which_bandwidth(struct gspca_dev *gspca_dev)
{
u32 bandwidth;
int i;
i = gspca_dev->curr_mode;
bandwidth = gspca_dev->cam.cam_mode[i].sizeimage;
/* if the image is compressed, estimate the mean image size */
if (bandwidth < gspca_dev->cam.cam_mode[i].width *
gspca_dev->cam.cam_mode[i].height)
bandwidth /= 3;
/* estimate the frame rate */
if (gspca_dev->sd_desc->get_streamparm) {
struct v4l2_streamparm parm;
parm.parm.capture.timeperframe.denominator = 15;
gspca_dev->sd_desc->get_streamparm(gspca_dev, &parm);
bandwidth *= parm.parm.capture.timeperframe.denominator;
} else {
bandwidth *= 15; /* 15 fps */
}
PDEBUG(D_STREAM, "min bandwidth: %d", bandwidth);
return bandwidth;
}
/* endpoint table */
#define MAX_ALT 16
struct ep_tb_s {
u32 alt;
u32 bandwidth;
};
/*
* look for an input (isoc or bulk) endpoint
*
* The endpoint is defined by the subdriver.
* Use only the first isoc (some Zoran - 0x0572:0x0001 - have two such ep).
* This routine may be called many times when the bandwidth is too small
* (the bandwidth is checked on urb submit).
* build the table of the endpoints
* and compute the minimum bandwidth for the image transfer
*/
static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev)
static int build_ep_tb(struct gspca_dev *gspca_dev,
struct usb_interface *intf,
int xfer,
struct ep_tb_s *ep_tb)
{
struct usb_interface *intf;
struct usb_host_endpoint *ep;
int xfer, i, ret;
int i, j, nbalt, psize, found;
u32 bandwidth, last_bw;
intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
ep = NULL;
xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
: USB_ENDPOINT_XFER_ISOC;
i = gspca_dev->alt; /* previous alt setting */
if (gspca_dev->cam.reverse_alts) {
while (++i < gspca_dev->nbalt) {
ep = alt_xfer(&intf->altsetting[i], xfer);
if (ep)
break;
}
} else {
while (--i >= 0) {
ep = alt_xfer(&intf->altsetting[i], xfer);
if (ep)
break;
}
}
if (ep == NULL) {
pr_err("no transfer endpoint found\n");
return NULL;
nbalt = intf->num_altsetting;
if (nbalt > MAX_ALT)
nbalt = MAX_ALT; /* fixme: should warn */
/* build the endpoint table */
i = 0;
last_bw = 0;
for (;;) {
ep_tb->bandwidth = 2000 * 2000 * 120;
found = 0;
for (j = 0; j < nbalt; j++) {
ep = alt_xfer(&intf->altsetting[j], xfer);
if (ep == NULL)
continue;
psize = le16_to_cpu(ep->desc.wMaxPacketSize);
if (!gspca_dev->cam.bulk) /* isoc */
psize = (psize & 0x07ff) *
(1 + ((psize >> 11) & 3));
bandwidth = psize * ep->desc.bInterval * 1000;
if (gspca_dev->dev->speed == USB_SPEED_HIGH
|| gspca_dev->dev->speed == USB_SPEED_SUPER)
bandwidth *= 8;
if (bandwidth <= last_bw)
continue;
if (bandwidth < ep_tb->bandwidth) {
ep_tb->bandwidth = bandwidth;
ep_tb->alt = j;
found = 1;
}
PDEBUG(D_STREAM, "use alt %d ep 0x%02x",
i, ep->desc.bEndpointAddress);
gspca_dev->alt = i; /* memorize the current alt setting */
if (gspca_dev->nbalt > 1) {
ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i);
if (ret < 0) {
pr_err("set alt %d err %d\n", i, ret);
ep = NULL;
}
if (!found)
break;
PDEBUG(D_STREAM, "alt %d bandwidth %d",
ep_tb->alt, ep_tb->bandwidth);
last_bw = ep_tb->bandwidth;
i++;
ep_tb++;
}
/* get the requested bandwidth and start at the highest atlsetting */
bandwidth = which_bandwidth(gspca_dev);
ep_tb--;
while (i > 1) {
ep_tb--;
if (ep_tb->bandwidth < bandwidth)
break;
i--;
}
return ep;
return i;
}
/*
......@@ -766,9 +817,11 @@ static int create_urbs(struct gspca_dev *gspca_dev,
*/
static int gspca_init_transfer(struct gspca_dev *gspca_dev)
{
struct usb_interface *intf;
struct usb_host_endpoint *ep;
struct urb *urb;
int n, ret;
struct ep_tb_s ep_tb[MAX_ALT];
int n, ret, xfer, alt, alt_idx;
if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
......@@ -786,30 +839,63 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
gspca_dev->usb_err = 0;
/* set the higher alternate setting and
* loop until urb submit succeeds */
if (gspca_dev->cam.reverse_alts)
/* do the specific subdriver stuff before endpoint selection */
gspca_dev->alt = 0;
else
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 unlock;
}
intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
: USB_ENDPOINT_XFER_ISOC;
gspca_input_destroy_urb(gspca_dev);
ep = get_ep(gspca_dev);
/* if the subdriver forced an altsetting, get the endpoint */
if (gspca_dev->alt != 0) {
gspca_dev->alt--; /* (previous version compatibility) */
ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer);
if (ep == NULL) {
pr_err("bad altsetting %d\n", gspca_dev->alt);
ret = -EIO;
goto out;
}
ep_tb[0].alt = gspca_dev->alt;
alt_idx = 1;
} else {
/* else, compute the minimum bandwidth
* and build the endpoint table */
alt_idx = build_ep_tb(gspca_dev, intf, xfer, ep_tb);
if (alt_idx <= 0) {
pr_err("no transfer endpoint found\n");
ret = -EIO;
goto unlock;
}
}
/* set the highest alternate setting and
* loop until urb submit succeeds */
gspca_input_destroy_urb(gspca_dev);
gspca_dev->alt = ep_tb[--alt_idx].alt;
alt = -1;
for (;;) {
if (alt != gspca_dev->alt) {
alt = gspca_dev->alt;
if (gspca_dev->nbalt > 1) {
ret = usb_set_interface(gspca_dev->dev,
gspca_dev->iface,
alt);
if (ret < 0) {
pr_err("set alt %d err %d\n", alt, ret);
goto out;
}
}
}
if (!gspca_dev->cam.no_urb_create) {
PDEBUG(D_STREAM, "init transfer alt %d",
gspca_dev->alt);
ret = create_urbs(gspca_dev, ep);
PDEBUG(D_STREAM, "init transfer alt %d", alt);
ret = create_urbs(gspca_dev,
alt_xfer(&intf->altsetting[alt], xfer));
if (ret < 0) {
destroy_urbs(gspca_dev);
goto out;
......@@ -843,7 +929,10 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
break;
}
if (ret >= 0)
break;
break; /* transfer is started */
/* something when wrong
* stop the webcam and free the transfer resources */
gspca_stream_off(gspca_dev);
if (ret != -ENOSPC) {
pr_err("usb_submit_urb alt %d err %d\n",
......@@ -854,18 +943,20 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
/* the bandwidth is not wide enough
* negotiate or try a lower alternate setting */
PDEBUG(D_ERR|D_STREAM,
"bandwidth not wide enough - trying again");
"alt %d - bandwidth not wide enough - trying again",
alt);
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) {
if (alt_idx <= 0) {
pr_err("no transfer endpoint found\n");
ret = -EIO;
goto out;
}
alt = ep_tb[--alt_idx].alt;
}
}
out:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment