Skip to content

Commit

Permalink
[media] gspca - main: Use a better altsetting for image transfer
Browse files Browse the repository at this point in the history
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: Jean-François Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
  • Loading branch information
Jean-François Moine authored and Mauro Carvalho Chehab committed Sep 24, 2011
1 parent 2d06d5d commit 27b1202
Showing 1 changed file with 151 additions and 60 deletions.
211 changes: 151 additions & 60 deletions drivers/media/video/gspca/gspca.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
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;
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;
}
}
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;
}

/*
Expand Down Expand Up @@ -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;
Expand All @@ -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)
gspca_dev->alt = 0;
else
gspca_dev->alt = gspca_dev->nbalt;

/* do the specific subdriver stuff before endpoint selection */
gspca_dev->alt = 0;
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 (ep == NULL) {
ret = -EIO;
goto out;
/* 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;
Expand Down Expand Up @@ -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",
Expand All @@ -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:
Expand Down

0 comments on commit 27b1202

Please sign in to comment.