diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index f656fd7a39a2..82968017a253 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -49,12 +49,15 @@ #define TM6000_MIN_BUF 4 #define TM6000_DEF_BUF 8 +#define TM6000_NUM_URB_BUF 8 + #define TM6000_MAX_ISO_PACKETS 46 /* Max number of ISO packets */ /* Declare static vars that will be used as parameters */ static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */ +static int keep_urb; /* keep urb buffers allocated */ /* Debug level */ int tm6000_debug; @@ -537,6 +540,71 @@ static void tm6000_irq_callback(struct urb *urb) urb->status); } +/* + * Allocate URB buffers + */ +static int tm6000_alloc_urb_buffers(struct tm6000_core *dev) +{ + int num_bufs = TM6000_NUM_URB_BUF; + int i; + + if (dev->urb_buffer != NULL) + return 0; + + dev->urb_buffer = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->urb_buffer) { + tm6000_err("cannot allocate memory for urb buffers\n"); + return -ENOMEM; + } + + dev->urb_dma = kmalloc(sizeof(dma_addr_t *)*num_bufs, GFP_KERNEL); + if (!dev->urb_dma) { + tm6000_err("cannot allocate memory for urb dma pointers\n"); + return -ENOMEM; + } + + for (i = 0; i < num_bufs; i++) { + dev->urb_buffer[i] = usb_alloc_coherent( + dev->udev, dev->urb_size, + GFP_KERNEL, &dev->urb_dma[i]); + if (!dev->urb_buffer[i]) { + tm6000_err("unable to allocate %i bytes for transfer buffer %i\n", + dev->urb_size, i); + return -ENOMEM; + } + memset(dev->urb_buffer[i], 0, dev->urb_size); + } + + return 0; +} + +/* + * Free URB buffers + */ +static int tm6000_free_urb_buffers(struct tm6000_core *dev) +{ + int i; + + if (dev->urb_buffer == NULL) + return 0; + + for (i = 0; i < TM6000_NUM_URB_BUF; i++) { + if (dev->urb_buffer[i]) { + usb_free_coherent(dev->udev, + dev->urb_size, + dev->urb_buffer[i], + dev->urb_dma[i]); + dev->urb_buffer[i] = NULL; + } + } + kfree(dev->urb_buffer); + kfree(dev->urb_dma); + dev->urb_buffer = NULL; + dev->urb_dma = NULL; + + return 0; +} + /* * Stop and Deallocate URBs */ @@ -551,18 +619,15 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev) if (urb) { usb_kill_urb(urb); usb_unlink_urb(urb); - if (dev->isoc_ctl.transfer_buffer[i]) { - usb_free_coherent(dev->udev, - urb->transfer_buffer_length, - dev->isoc_ctl.transfer_buffer[i], - urb->transfer_dma); - } usb_free_urb(urb); dev->isoc_ctl.urb[i] = NULL; } dev->isoc_ctl.transfer_buffer[i] = NULL; } + if (!keep_urb) + tm6000_free_urb_buffers(dev); + kfree(dev->isoc_ctl.urb); kfree(dev->isoc_ctl.transfer_buffer); @@ -572,12 +637,13 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev) } /* - * Allocate URBs and start IRQ + * Assign URBs and start IRQ */ static int tm6000_prepare_isoc(struct tm6000_core *dev) { struct tm6000_dmaqueue *dma_q = &dev->vidq; - int i, j, sb_size, pipe, size, max_packets, num_bufs = 8; + int i, j, sb_size, pipe, size, max_packets; + int num_bufs = TM6000_NUM_URB_BUF; struct urb *urb; /* De-allocates all pending stuff */ @@ -605,6 +671,7 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) max_packets = TM6000_MAX_ISO_PACKETS; sb_size = max_packets * size; + dev->urb_size = sb_size; dev->isoc_ctl.num_bufs = num_bufs; @@ -627,6 +694,17 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) max_packets, num_bufs, sb_size, dev->isoc_in.maxsize, size); + + if (!dev->urb_buffer && tm6000_alloc_urb_buffers(dev) < 0) { + tm6000_err("cannot allocate memory for urb buffers\n"); + + /* call free, as some buffers might have been allocated */ + tm6000_free_urb_buffers(dev); + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + return -ENOMEM; + } + /* allocate urbs and transfer buffers */ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { urb = usb_alloc_urb(max_packets, GFP_KERNEL); @@ -638,17 +716,8 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) } dev->isoc_ctl.urb[i] = urb; - dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev, - sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!dev->isoc_ctl.transfer_buffer[i]) { - tm6000_err("unable to allocate %i bytes for transfer" - " buffer %i%s\n", - sb_size, i, - in_interrupt() ? " while in int" : ""); - tm6000_uninit_isoc(dev); - return -ENOMEM; - } - memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + urb->transfer_dma = dev->urb_dma[i]; + dev->isoc_ctl.transfer_buffer[i] = dev->urb_buffer[i]; usb_fill_bulk_urb(urb, dev->udev, pipe, dev->isoc_ctl.transfer_buffer[i], sb_size, @@ -1826,6 +1895,9 @@ int tm6000_v4l2_unregister(struct tm6000_core *dev) { video_unregister_device(dev->vfd); + /* if URB buffers are still allocated free them now */ + tm6000_free_urb_buffers(dev); + if (dev->radio_dev) { if (video_is_registered(dev->radio_dev)) video_unregister_device(dev->radio_dev); @@ -1851,3 +1923,5 @@ MODULE_PARM_DESC(debug, "activates debug info"); module_param(vid_limit, int, 0644); MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); +module_param(keep_urb, bool, 0); +MODULE_PARM_DESC(keep_urb, "Keep urb buffers allocated even when the device is closed by the user"); diff --git a/drivers/media/usb/tm6000/tm6000.h b/drivers/media/usb/tm6000/tm6000.h index 6df418658c9c..173dcd7a7284 100644 --- a/drivers/media/usb/tm6000/tm6000.h +++ b/drivers/media/usb/tm6000/tm6000.h @@ -264,6 +264,11 @@ struct tm6000_core { spinlock_t slock; + /* urb dma buffers */ + char **urb_buffer; + dma_addr_t *urb_dma; + unsigned int urb_size; + unsigned long quirks; };