diff options
Diffstat (limited to 'drivers/media/video/em28xx/em28xx-core.c')
-rw-r--r-- | drivers/media/video/em28xx/em28xx-core.c | 145 |
1 files changed, 96 insertions, 49 deletions
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 0aacc96f9a2..53a9fb91e97 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -666,6 +666,7 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } +EXPORT_SYMBOL_GPL(em28xx_capture_start); int em28xx_vbi_supported(struct em28xx *dev) { @@ -961,146 +962,192 @@ static void em28xx_irq_callback(struct urb *urb) /* * Stop and Deallocate URBs */ -void em28xx_uninit_isoc(struct em28xx *dev) +void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) { struct urb *urb; + struct em28xx_usb_isoc_bufs *isoc_bufs; int i; - em28xx_isocdbg("em28xx: called em28xx_uninit_isoc\n"); + em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode); + + if (mode == EM28XX_DIGITAL_MODE) + isoc_bufs = &dev->isoc_ctl.digital_bufs; + else + isoc_bufs = &dev->isoc_ctl.analog_bufs; dev->isoc_ctl.nfields = -1; - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = dev->isoc_ctl.urb[i]; + for (i = 0; i < isoc_bufs->num_bufs; i++) { + urb = isoc_bufs->urb[i]; if (urb) { if (!irqs_disabled()) usb_kill_urb(urb); else usb_unlink_urb(urb); - if (dev->isoc_ctl.transfer_buffer[i]) { + if (isoc_bufs->transfer_buffer[i]) { usb_free_coherent(dev->udev, urb->transfer_buffer_length, - dev->isoc_ctl.transfer_buffer[i], + isoc_bufs->transfer_buffer[i], urb->transfer_dma); } usb_free_urb(urb); - dev->isoc_ctl.urb[i] = NULL; + isoc_bufs->urb[i] = NULL; } - dev->isoc_ctl.transfer_buffer[i] = NULL; + isoc_bufs->transfer_buffer[i] = NULL; } - kfree(dev->isoc_ctl.urb); - kfree(dev->isoc_ctl.transfer_buffer); + kfree(isoc_bufs->urb); + kfree(isoc_bufs->transfer_buffer); - dev->isoc_ctl.urb = NULL; - dev->isoc_ctl.transfer_buffer = NULL; - dev->isoc_ctl.num_bufs = 0; + isoc_bufs->urb = NULL; + isoc_bufs->transfer_buffer = NULL; + isoc_bufs->num_bufs = 0; em28xx_capture_start(dev, 0); } EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); /* - * Allocate URBs and start IRQ + * Allocate URBs */ -int em28xx_init_isoc(struct em28xx *dev, int max_packets, - int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) +int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, + int max_packets, int num_bufs, int max_pkt_size) { - struct em28xx_dmaqueue *dma_q = &dev->vidq; - struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; + struct em28xx_usb_isoc_bufs *isoc_bufs; int i; int sb_size, pipe; struct urb *urb; int j, k; - int rc; - em28xx_isocdbg("em28xx: called em28xx_prepare_isoc\n"); + em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode); + + if (mode == EM28XX_DIGITAL_MODE) + isoc_bufs = &dev->isoc_ctl.digital_bufs; + else + isoc_bufs = &dev->isoc_ctl.analog_bufs; /* De-allocates all pending stuff */ - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, mode); - dev->isoc_ctl.isoc_copy = isoc_copy; - dev->isoc_ctl.num_bufs = num_bufs; + isoc_bufs->num_bufs = num_bufs; - dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!dev->isoc_ctl.urb) { + isoc_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!isoc_bufs->urb) { em28xx_errdev("cannot alloc memory for usb buffers\n"); return -ENOMEM; } - dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, - GFP_KERNEL); - if (!dev->isoc_ctl.transfer_buffer) { + isoc_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs, + GFP_KERNEL); + if (!isoc_bufs->transfer_buffer) { em28xx_errdev("cannot allocate memory for usb transfer\n"); - kfree(dev->isoc_ctl.urb); + kfree(isoc_bufs->urb); return -ENOMEM; } - dev->isoc_ctl.max_pkt_size = max_pkt_size; + isoc_bufs->max_pkt_size = max_pkt_size; + isoc_bufs->num_packets = max_packets; dev->isoc_ctl.vid_buf = NULL; dev->isoc_ctl.vbi_buf = NULL; - sb_size = max_packets * dev->isoc_ctl.max_pkt_size; + sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size; /* allocate urbs and transfer buffers */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - urb = usb_alloc_urb(max_packets, GFP_KERNEL); + for (i = 0; i < isoc_bufs->num_bufs; i++) { + urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL); if (!urb) { em28xx_err("cannot alloc isoc_ctl.urb %i\n", i); - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, mode); return -ENOMEM; } - dev->isoc_ctl.urb[i] = urb; + isoc_bufs->urb[i] = urb; - dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev, + isoc_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!dev->isoc_ctl.transfer_buffer[i]) { + if (!isoc_bufs->transfer_buffer[i]) { em28xx_err("unable to allocate %i bytes for transfer" " buffer %i%s\n", sb_size, i, in_interrupt() ? " while in int" : ""); - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, mode); return -ENOMEM; } - memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + memset(isoc_bufs->transfer_buffer[i], 0, sb_size); /* FIXME: this is a hack - should be 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK' should also be using 'desc.bInterval' */ pipe = usb_rcvisocpipe(dev->udev, - dev->mode == EM28XX_ANALOG_MODE ? + mode == EM28XX_ANALOG_MODE ? EM28XX_EP_ANALOG : EM28XX_EP_DIGITAL); usb_fill_int_urb(urb, dev->udev, pipe, - dev->isoc_ctl.transfer_buffer[i], sb_size, + isoc_bufs->transfer_buffer[i], sb_size, em28xx_irq_callback, dev, 1); - urb->number_of_packets = max_packets; + urb->number_of_packets = isoc_bufs->num_packets; urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; k = 0; - for (j = 0; j < max_packets; j++) { + for (j = 0; j < isoc_bufs->num_packets; j++) { urb->iso_frame_desc[j].offset = k; urb->iso_frame_desc[j].length = - dev->isoc_ctl.max_pkt_size; - k += dev->isoc_ctl.max_pkt_size; + isoc_bufs->max_pkt_size; + k += isoc_bufs->max_pkt_size; } } + return 0; +} +EXPORT_SYMBOL_GPL(em28xx_alloc_isoc); + +/* + * Allocate URBs and start IRQ + */ +int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, + int max_packets, int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) +{ + struct em28xx_dmaqueue *dma_q = &dev->vidq; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; + struct em28xx_usb_isoc_bufs *isoc_bufs; + int i; + int rc; + int alloc; + + em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode); + + dev->isoc_ctl.isoc_copy = isoc_copy; + + if (mode == EM28XX_DIGITAL_MODE) { + isoc_bufs = &dev->isoc_ctl.digital_bufs; + /* no need to free/alloc isoc buffers in digital mode */ + alloc = 0; + } else { + isoc_bufs = &dev->isoc_ctl.analog_bufs; + alloc = 1; + } + + if (alloc) { + rc = em28xx_alloc_isoc(dev, mode, max_packets, + num_bufs, max_pkt_size); + if (rc) + return rc; + } + init_waitqueue_head(&dma_q->wq); init_waitqueue_head(&vbi_dma_q->wq); em28xx_capture_start(dev, 1); /* submit urbs and enables IRQ */ - for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { - rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); + for (i = 0; i < isoc_bufs->num_bufs; i++) { + rc = usb_submit_urb(isoc_bufs->urb[i], GFP_ATOMIC); if (rc) { em28xx_err("submit of urb %i failed (error=%i)\n", i, rc); - em28xx_uninit_isoc(dev); + em28xx_uninit_isoc(dev, mode); return rc; } } |