Skip to content

Commit

Permalink
V4L/DVB (8209): uvcvideo: Don't free URB buffers on suspend.
Browse files Browse the repository at this point in the history
All submitted URBs must be killed at suspend time, but URB buffers don't have
to be freed. Avoiding a free on suspend/reallocate on resume lowers the presure
on system memory.

Signed-off-by: Laurent Pinchart <laurent.pinchart@skynet.be>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
  • Loading branch information
Laurent Pinchart authored and Mauro Carvalho Chehab committed Jul 20, 2008
1 parent 2913587 commit e01117c
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 32 deletions.
96 changes: 64 additions & 32 deletions drivers/media/video/uvc/uvc_video.c
Original file line number Diff line number Diff line change
Expand Up @@ -553,10 +553,57 @@ static void uvc_video_complete(struct urb *urb)
}
}

/*
* Free transfer buffers.
*/
static void uvc_free_urb_buffers(struct uvc_video_device *video)
{
unsigned int i;

for (i = 0; i < UVC_URBS; ++i) {
if (video->urb_buffer[i]) {
usb_buffer_free(video->dev->udev, video->urb_size,
video->urb_buffer[i], video->urb_dma[i]);
video->urb_buffer[i] = NULL;
}
}

video->urb_size = 0;
}

/*
* Allocate transfer buffers. This function can be called with buffers
* already allocated when resuming from suspend, in which case it will
* return without touching the buffers.
*
* Return 0 on success or -ENOMEM when out of memory.
*/
static int uvc_alloc_urb_buffers(struct uvc_video_device *video,
unsigned int size)
{
unsigned int i;

/* Buffers are already allocated, bail out. */
if (video->urb_size)
return 0;

for (i = 0; i < UVC_URBS; ++i) {
video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev,
size, GFP_KERNEL, &video->urb_dma[i]);
if (video->urb_buffer[i] == NULL) {
uvc_free_urb_buffers(video);
return -ENOMEM;
}
}

video->urb_size = size;
return 0;
}

/*
* Uninitialize isochronous/bulk URBs and free transfer buffers.
*/
static void uvc_uninit_video(struct uvc_video_device *video)
static void uvc_uninit_video(struct uvc_video_device *video, int free_buffers)
{
struct urb *urb;
unsigned int i;
Expand All @@ -566,19 +613,12 @@ static void uvc_uninit_video(struct uvc_video_device *video)
continue;

usb_kill_urb(urb);
/* urb->transfer_buffer_length is not touched by USB core, so
* we can use it here as the buffer length.
*/
if (video->urb_buffer[i]) {
usb_buffer_free(video->dev->udev,
urb->transfer_buffer_length,
video->urb_buffer[i], urb->transfer_dma);
video->urb_buffer[i] = NULL;
}

usb_free_urb(urb);
video->urb[i] = NULL;
}

if (free_buffers)
uvc_free_urb_buffers(video);
}

/*
Expand Down Expand Up @@ -610,18 +650,13 @@ static int uvc_init_video_isoc(struct uvc_video_device *video,

size = npackets * psize;

if (uvc_alloc_urb_buffers(video, size) < 0)
return -ENOMEM;

for (i = 0; i < UVC_URBS; ++i) {
urb = usb_alloc_urb(npackets, gfp_flags);
if (urb == NULL) {
uvc_uninit_video(video);
return -ENOMEM;
}

video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev,
size, gfp_flags, &urb->transfer_dma);
if (video->urb_buffer[i] == NULL) {
usb_free_urb(urb);
uvc_uninit_video(video);
uvc_uninit_video(video, 1);
return -ENOMEM;
}

Expand All @@ -632,6 +667,7 @@ static int uvc_init_video_isoc(struct uvc_video_device *video,
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
urb->interval = ep->desc.bInterval;
urb->transfer_buffer = video->urb_buffer[i];
urb->transfer_dma = video->urb_dma[i];
urb->complete = uvc_video_complete;
urb->number_of_packets = npackets;
urb->transfer_buffer_length = size;
Expand Down Expand Up @@ -671,27 +707,23 @@ static int uvc_init_video_bulk(struct uvc_video_device *video,
if (size > psize * UVC_MAX_ISO_PACKETS)
size = psize * UVC_MAX_ISO_PACKETS;

if (uvc_alloc_urb_buffers(video, size) < 0)
return -ENOMEM;

pipe = usb_rcvbulkpipe(video->dev->udev, ep->desc.bEndpointAddress);

for (i = 0; i < UVC_URBS; ++i) {
urb = usb_alloc_urb(0, gfp_flags);
if (urb == NULL) {
uvc_uninit_video(video);
return -ENOMEM;
}

video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev,
size, gfp_flags, &urb->transfer_dma);
if (video->urb_buffer[i] == NULL) {
usb_free_urb(urb);
uvc_uninit_video(video);
uvc_uninit_video(video, 1);
return -ENOMEM;
}

usb_fill_bulk_urb(urb, video->dev->udev, pipe,
video->urb_buffer[i], size, uvc_video_complete,
video);
urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
urb->transfer_dma = video->urb_dma[i];

video->urb[i] = urb;
}
Expand Down Expand Up @@ -766,7 +798,7 @@ static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags)
if ((ret = usb_submit_urb(video->urb[i], gfp_flags)) < 0) {
uvc_printk(KERN_ERR, "Failed to submit URB %u "
"(%d).\n", i, ret);
uvc_uninit_video(video);
uvc_uninit_video(video, 1);
return ret;
}
}
Expand All @@ -791,7 +823,7 @@ int uvc_video_suspend(struct uvc_video_device *video)
return 0;

video->frozen = 1;
uvc_uninit_video(video);
uvc_uninit_video(video, 0);
usb_set_interface(video->dev->udev, video->streaming->intfnum, 0);
return 0;
}
Expand Down Expand Up @@ -920,7 +952,7 @@ int uvc_video_enable(struct uvc_video_device *video, int enable)
int ret;

if (!enable) {
uvc_uninit_video(video);
uvc_uninit_video(video, 1);
usb_set_interface(video->dev->udev,
video->streaming->intfnum, 0);
uvc_queue_enable(&video->queue, 0);
Expand Down
2 changes: 2 additions & 0 deletions drivers/media/video/uvc/uvcvideo.h
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,8 @@ struct uvc_video_device {

struct urb *urb[UVC_URBS];
char *urb_buffer[UVC_URBS];
dma_addr_t urb_dma[UVC_URBS];
unsigned int urb_size;

__u8 last_fid;
};
Expand Down

0 comments on commit e01117c

Please sign in to comment.