diff options
author | Mauro Carvalho Chehab <mchehab@infradead.org> | 2008-04-13 14:37:52 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2008-04-24 14:08:00 -0300 |
commit | ad0ebb96c220c461386e9a765fca3daf5590d01e (patch) | |
tree | 4c13c24682703cbb94f415eb944c573ac0835ccc /drivers | |
parent | 78e92006f410a4044f8c1760c25ac9d11d259aa2 (diff) |
V4L/DVB (7540): em28xx: convert to use videobuf-vmalloc
The usage of videobuf-vmalloc allows to cleanup em28xx logic.
Also, it reduced its size by about 5.42% on i386 arch (and about 7.5% on x86_64):
39113 4876 40 44029 abfd old/em28xx.ko
36731 4868 40 41639 a2a7 /home/v4l/master/v4l/em28xx.ko
Also, the preliminary tests, made on a single core 1.5 MHz Centrino showed
that CPU usage reduced from 42%-75% to 28%-33% (reports from "top") command.
A test with time command presented an even better result:
This is the performance tests I did, running code_example to get 1,000 frames
@29.995 Hz (about 35 seconds of stream), tested on a i386 machine, running at
1,5GHz:
The old driver:
$ time -f "%E: %Us User time, %Ss Kernel time, %P CPU used" ./capture_example
0:34.21: 8.22s User time, 25.16s Kernel time, 97% CPU used
The videobuf-based driver:
$ time -f "%E: %Us User time, %Ss Kernel time, %P CPU used" ./capture_example
0:35.36: 0.01s User time, 0.05s Kernel time, 0% CPU used
Conclusion:
The time consumption to receive the stream where reduced from about 33.38
seconds to 0.05 seconds.
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/em28xx/em28xx-core.c | 417 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-video.c | 1198 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx.h | 94 |
3 files changed, 811 insertions, 898 deletions
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 9aa96f1713a..95bc18d0e78 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -49,87 +49,10 @@ MODULE_PARM_DESC(reg_debug,"enable debug messages [URB reg]"); printk(KERN_INFO "%s %s :"fmt, \ dev->name, __func__ , ##arg); } while (0) -static unsigned int isoc_debug; -module_param(isoc_debug,int,0644); -MODULE_PARM_DESC(isoc_debug,"enable debug messages [isoc transfers]"); - -#define em28xx_isocdbg(fmt, arg...) do {\ - if (isoc_debug) \ - printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __func__ , ##arg); } while (0) - static int alt = EM28XX_PINOUT; module_param(alt, int, 0644); MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); - -/* - * em28xx_request_buffers() - * allocate a number of buffers - */ -u32 em28xx_request_buffers(struct em28xx *dev, u32 count) -{ - const size_t imagesize = PAGE_ALIGN(dev->frame_size); /*needs to be page aligned cause the buffers can be mapped individually! */ - void *buff = NULL; - u32 i; - em28xx_coredbg("requested %i buffers with size %zi\n", - count, imagesize); - if (count > EM28XX_NUM_FRAMES) - count = EM28XX_NUM_FRAMES; - - dev->num_frames = count; - while (dev->num_frames > 0) { - if ((buff = vmalloc_32(dev->num_frames * imagesize))) { - memset(buff, 0, dev->num_frames * imagesize); - break; - } - dev->num_frames--; - } - - for (i = 0; i < dev->num_frames; i++) { - dev->frame[i].bufmem = buff + i * imagesize; - dev->frame[i].buf.index = i; - dev->frame[i].buf.m.offset = i * imagesize; - dev->frame[i].buf.length = dev->frame_size; - dev->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dev->frame[i].buf.sequence = 0; - dev->frame[i].buf.field = V4L2_FIELD_NONE; - dev->frame[i].buf.memory = V4L2_MEMORY_MMAP; - dev->frame[i].buf.flags = 0; - } - return dev->num_frames; -} - -/* - * em28xx_queue_unusedframes() - * add all frames that are not currently in use to the inbuffer queue - */ -void em28xx_queue_unusedframes(struct em28xx *dev) -{ - unsigned long lock_flags; - u32 i; - - for (i = 0; i < dev->num_frames; i++) - if (dev->frame[i].state == F_UNUSED) { - dev->frame[i].state = F_QUEUED; - spin_lock_irqsave(&dev->queue_lock, lock_flags); - list_add_tail(&dev->frame[i].frame, &dev->inqueue); - spin_unlock_irqrestore(&dev->queue_lock, lock_flags); - } -} - -/* - * em28xx_release_buffers() - * free frame buffers - */ -void em28xx_release_buffers(struct em28xx *dev) -{ - if (dev->num_frames) { - vfree(dev->frame[0].bufmem); - dev->num_frames = 0; - } -} - /* * em28xx_read_reg_req() * reads data from the usb device specifying bRequest @@ -469,346 +392,6 @@ int em28xx_resolution_set(struct em28xx *dev) return em28xx_scaler_set(dev, dev->hscale, dev->vscale); } - -/******************* isoc transfer handling ****************************/ - -#ifdef ENABLE_DEBUG_ISOC_FRAMES -static void em28xx_isoc_dump(struct urb *urb) -{ - int len = 0; - int ntrans = 0; - int i; - - printk(KERN_DEBUG "isocIrq: sf=%d np=%d ec=%x\n", - urb->start_frame, urb->number_of_packets, - urb->error_count); - for (i = 0; i < urb->number_of_packets; i++) { - unsigned char *buf = - urb->transfer_buffer + - urb->iso_frame_desc[i].offset; - int alen = urb->iso_frame_desc[i].actual_length; - if (alen > 0) { - if (buf[0] == 0x88) { - ntrans++; - len += alen; - } else if (buf[0] == 0x22) { - printk(KERN_DEBUG - "= l=%d nt=%d bpp=%d\n", - len - 4 * ntrans, ntrans, - ntrans == 0 ? 0 : len / ntrans); - ntrans = 1; - len = alen; - } else - printk(KERN_DEBUG "!\n"); - } - printk(KERN_DEBUG " n=%d s=%d al=%d %x\n", i, - urb->iso_frame_desc[i].status, - urb->iso_frame_desc[i].actual_length, - (unsigned int) - *((unsigned char *)(urb->transfer_buffer + - urb->iso_frame_desc[i]. - offset))); - } -} -#endif - -static inline int em28xx_isoc_video(struct em28xx *dev,struct em28xx_frame_t **f, - unsigned long *lock_flags, unsigned char buf) -{ - if (!(buf & 0x01)) { - if ((*f)->state == F_GRABBING) { - /*previous frame is incomplete */ - if ((*f)->fieldbytesused < dev->field_size) { - (*f)->state = F_ERROR; - em28xx_isocdbg ("dropping incomplete bottom field (%i missing bytes)", - dev->field_size-(*f)->fieldbytesused); - } else { - (*f)->state = F_DONE; - (*f)->buf.bytesused = dev->frame_size; - } - } - if ((*f)->state == F_DONE || (*f)->state == F_ERROR) { - /* move current frame to outqueue and get next free buffer from inqueue */ - spin_lock_irqsave(&dev-> queue_lock, *lock_flags); - list_move_tail(&(*f)->frame, &dev->outqueue); - if (!list_empty(&dev->inqueue)) - (*f) = list_entry(dev-> inqueue.next, - struct em28xx_frame_t,frame); - else - (*f) = NULL; - spin_unlock_irqrestore(&dev->queue_lock,*lock_flags); - } - if (!(*f)) { - em28xx_isocdbg ("new frame but no buffer is free"); - return -1; - } - do_gettimeofday(&(*f)->buf.timestamp); - (*f)->buf.sequence = ++dev->frame_count; - (*f)->buf.field = V4L2_FIELD_INTERLACED; - (*f)->state = F_GRABBING; - (*f)->buf.bytesused = 0; - (*f)->top_field = 1; - (*f)->fieldbytesused = 0; - } else { - /* acquiring bottom field */ - if ((*f)->state == F_GRABBING) { - if (!(*f)->top_field) { - (*f)->state = F_ERROR; - em28xx_isocdbg ("unexpected begin of bottom field; discarding it"); - } else if ((*f)-> fieldbytesused < dev->field_size - 172) { - (*f)->state = F_ERROR; - em28xx_isocdbg ("dropping incomplete top field (%i missing bytes)", - dev->field_size-(*f)->fieldbytesused); - } else { - (*f)->top_field = 0; - (*f)->fieldbytesused = 0; - } - } - } - return (0); -} - -static inline void em28xx_isoc_video_copy(struct em28xx *dev, - struct em28xx_frame_t **f, unsigned char *buf, int len) -{ - void *fieldstart, *startwrite, *startread; - int linesdone, currlinedone, offset, lencopy,remain; - - if(dev->frame_size != (*f)->buf.length){ - em28xx_err("frame_size %i and buf.length %i are different!!!\n",dev->frame_size,(*f)->buf.length); - return; - } - - if ((*f)->fieldbytesused + len > dev->field_size) - len =dev->field_size - (*f)->fieldbytesused; - - if (buf[0] != 0x88 && buf[0] != 0x22) { - em28xx_isocdbg("frame is not complete\n"); - startread = buf; - len+=4; - } else - startread = buf + 4; - - remain = len; - - if ((*f)->top_field) - fieldstart = (*f)->bufmem; - else - fieldstart = (*f)->bufmem + dev->bytesperline; - - linesdone = (*f)->fieldbytesused / dev->bytesperline; - currlinedone = (*f)->fieldbytesused % dev->bytesperline; - offset = linesdone * dev->bytesperline * 2 + currlinedone; - startwrite = fieldstart + offset; - lencopy = dev->bytesperline - currlinedone; - lencopy = lencopy > remain ? remain : lencopy; - - memcpy(startwrite, startread, lencopy); - remain -= lencopy; - - while (remain > 0) { - startwrite += lencopy + dev->bytesperline; - startread += lencopy; - if (dev->bytesperline > remain) - lencopy = remain; - else - lencopy = dev->bytesperline; - - memcpy(startwrite, startread, lencopy); - remain -= lencopy; - } - - (*f)->fieldbytesused += len; -} - -/* - * em28xx_isoIrq() - * handles the incoming isoc urbs and fills the frames from our inqueue - */ -static void em28xx_isocIrq(struct urb *urb) -{ - struct em28xx *dev = urb->context; - int i, status; - struct em28xx_frame_t **f; - unsigned long lock_flags; - - if (!dev) - return; -#ifdef ENABLE_DEBUG_ISOC_FRAMES - if (isoc_debug>1) - em28xx_isoc_dump(urb); -#endif - - if (urb->status == -ENOENT) - return; - - f = &dev->frame_current; - - if (dev->stream == STREAM_INTERRUPT) { - dev->stream = STREAM_OFF; - if ((*f)) - (*f)->state = F_QUEUED; - em28xx_isocdbg("stream interrupted"); - wake_up_interruptible(&dev->wait_stream); - } - - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) - return; - - if (dev->stream == STREAM_ON && !list_empty(&dev->inqueue)) { - if (!(*f)) - (*f) = list_entry(dev->inqueue.next, - struct em28xx_frame_t, frame); - - for (i = 0; i < urb->number_of_packets; i++) { - unsigned char *buf = urb->transfer_buffer + - urb->iso_frame_desc[i].offset; - int len = urb->iso_frame_desc[i].actual_length - 4; - - if (urb->iso_frame_desc[i].status) { - em28xx_isocdbg("data error: [%d] len=%d, status=%d", i, - urb->iso_frame_desc[i].actual_length, - urb->iso_frame_desc[i].status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; - } - if (urb->iso_frame_desc[i].actual_length <= 0) { - em28xx_isocdbg("packet %d is empty",i); - continue; - } - if (urb->iso_frame_desc[i].actual_length > - urb->iso_frame_desc[i].length) { - em28xx_isocdbg("packet bigger than packet size"); - continue; - } - /*new frame */ - if (buf[0] == 0x22 && buf[1] == 0x5a) { - em28xx_isocdbg("Video frame, length=%i!",len); - - if (em28xx_isoc_video(dev,f,&lock_flags,buf[2])) - break; - } else if (buf[0]==0x33 && buf[1]==0x95 && buf[2]==0x00) { - em28xx_isocdbg("VBI HEADER!!!"); - } - - /* actual copying */ - if ((*f)->state == F_GRABBING) { - em28xx_isoc_video_copy(dev,f,buf, len); - } - } - } - - for (i = 0; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - - urb->status = 0; - if ((status = usb_submit_urb(urb, GFP_ATOMIC))) { - em28xx_errdev("resubmit of urb failed (error=%i)\n", status); - dev->state |= DEV_MISCONFIGURED; - } - wake_up_interruptible(&dev->wait_frame); - return; -} - -/* - * em28xx_uninit_isoc() - * deallocates the buffers and urbs allocated during em28xx_init_iosc() - */ -void em28xx_uninit_isoc(struct em28xx *dev) -{ - int i; - - for (i = 0; i < EM28XX_NUM_BUFS; i++) { - if (dev->urb[i]) { - usb_kill_urb(dev->urb[i]); - if (dev->transfer_buffer[i]) { - usb_buffer_free(dev->udev, - dev->urb[i]->transfer_buffer_length, - dev->transfer_buffer[i], - dev->urb[i]->transfer_dma); - } - usb_free_urb(dev->urb[i]); - } - dev->urb[i] = NULL; - dev->transfer_buffer[i] = NULL; - } - em28xx_capture_start(dev, 0); -} - -/* - * em28xx_init_isoc() - * allocates transfer buffers and submits the urbs for isoc transfer - */ -int em28xx_init_isoc(struct em28xx *dev) -{ - /* change interface to 3 which allows the biggest packet sizes */ - int i, errCode; - int sb_size; - - em28xx_set_alternate(dev); - sb_size = EM28XX_NUM_PACKETS * dev->max_pkt_size; - - /* reset streaming vars */ - dev->frame_current = NULL; - dev->frame_count = 0; - - /* allocate urbs */ - for (i = 0; i < EM28XX_NUM_BUFS; i++) { - struct urb *urb; - int j; - /* allocate transfer buffer */ - urb = usb_alloc_urb(EM28XX_NUM_PACKETS, GFP_KERNEL); - if (!urb){ - em28xx_errdev("cannot alloc urb %i\n", i); - em28xx_uninit_isoc(dev); - return -ENOMEM; - } - dev->transfer_buffer[i] = usb_buffer_alloc(dev->udev, sb_size, - GFP_KERNEL, - &urb->transfer_dma); - if (!dev->transfer_buffer[i]) { - em28xx_errdev - ("unable to allocate %i bytes for transfer buffer %i\n", - sb_size, i); - em28xx_uninit_isoc(dev); - usb_free_urb(urb); - return -ENOMEM; - } - memset(dev->transfer_buffer[i], 0, sb_size); - urb->dev = dev->udev; - urb->context = dev; - urb->pipe = usb_rcvisocpipe(dev->udev, 0x82); - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - urb->interval = 1; - urb->transfer_buffer = dev->transfer_buffer[i]; - urb->complete = em28xx_isocIrq; - urb->number_of_packets = EM28XX_NUM_PACKETS; - urb->transfer_buffer_length = sb_size; - for (j = 0; j < EM28XX_NUM_PACKETS; j++) { - urb->iso_frame_desc[j].offset = j * dev->max_pkt_size; - urb->iso_frame_desc[j].length = dev->max_pkt_size; - } - dev->urb[i] = urb; - } - - /* submit urbs */ - em28xx_coredbg("Submitting %d urbs of %d packets (%d each)\n", - EM28XX_NUM_BUFS, EM28XX_NUM_PACKETS, dev->max_pkt_size); - for (i = 0; i < EM28XX_NUM_BUFS; i++) { - errCode = usb_submit_urb(dev->urb[i], GFP_KERNEL); - if (errCode) { - em28xx_errdev("submit of urb %i failed (error=%i)\n", i, - errCode); - em28xx_uninit_isoc(dev); - return errCode; - } - } - - return 0; -} - int em28xx_set_alternate(struct em28xx *dev) { int errCode, prev_alt = dev->alt; diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index c7f074c6f22..182058ac843 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -54,6 +54,21 @@ printk(KERN_INFO "%s %s :"fmt, \ dev->name, __func__ , ##arg); } while (0) +static unsigned int isoc_debug; +module_param(isoc_debug,int,0644); +MODULE_PARM_DESC(isoc_debug,"enable debug messages [isoc transfers]"); + +#define em28xx_isocdbg(fmt, arg...) do {\ + if (isoc_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __FUNCTION__ , ##arg); } while (0) + +#define BUFFER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ + +/* Limits minimum and default number of buffers */ +#define EM28XX_MIN_BUF 4 +#define EM28XX_DEF_BUF 8 + MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); @@ -107,6 +122,665 @@ static struct v4l2_queryctrl em28xx_qctrl[] = { static struct usb_driver em28xx_usb_driver; +/* ------------------------------------------------------------------ + DMA and thread functions + ------------------------------------------------------------------*/ + +/* + * Announces that a buffer were filled and request the next + */ +static void inline buffer_filled (struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf) +{ + /* Nobody is waiting something to be done, just return */ + if (!waitqueue_active(&buf->vb.done)) { + mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT); + + printk(KERN_ERR "em28xx: buffer underrun at %ld\n", + jiffies); + + return; + } + + /* Advice that buffer was filled */ + em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +/* + * Identify the buffer header type and properly handles + */ +static void em28xx_copy_video(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) +{ + void *fieldstart, *startwrite, *startread; + int linesdone, currlinedone, offset, lencopy,remain; + + if(dev->frame_size != buf->vb.size){ + em28xx_errdev("size %i and buf.length %lu are different!\n", + dev->frame_size, buf->vb.size); + return; + } + + if (dma_q->pos + len > buf->vb.size) + len = buf->vb.size - dma_q->pos; + + if (outp[0] != 0x88 && outp[0] != 0x22) { + em28xx_isocdbg("frame is not complete\n"); + len += 4; + } else + p +=4; + + startread = p; + remain = len; + + /* Interlaces frame */ + if (buf->top_field) + fieldstart = outp; + else + fieldstart = outp + dev->bytesperline; + + linesdone = dma_q->pos / dev->bytesperline; + currlinedone = dma_q->pos % dev->bytesperline; + offset = linesdone * dev->bytesperline * 2 + currlinedone; + startwrite = fieldstart + offset; + lencopy = dev->bytesperline - currlinedone; + lencopy = lencopy > remain ? remain : lencopy; + + if (__copy_to_user(startwrite, startread, lencopy) != 0) + em28xx_errdev("copy_to_user failed.\n"); + + remain -= lencopy; + + while (remain > 0) { + startwrite += lencopy + dev->bytesperline; + startread += lencopy; + if (dev->bytesperline > remain) + lencopy = remain; + else + lencopy = dev->bytesperline; + + if (__copy_to_user(startwrite, startread, lencopy) != 0) + em28xx_errdev("copy_to_user failed.\n"); + + remain -= lencopy; + } + + dma_q->pos += len; +} + +static void inline print_err_status (struct em28xx *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch(status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet<0) { + em28xx_isocdbg("URB status %d [%s].\n", status, errmsg); + } else { + em28xx_isocdbg("URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +/* + * video-buf generic routine to get the next available buffer + */ +static int inline get_next_buf (struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer **buf) +{ + struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + + if (list_empty(&dma_q->active)) { + em28xx_isocdbg("No active queue to serve\n"); + return 0; + } + + *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); + + return 1; +} + +/* + * Controls the isoc copy of each urb packet + */ +static inline int em28xx_isoc_copy(struct urb *urb, struct em28xx_buffer **buf) +{ + struct em28xx_dmaqueue *dma_q = urb->context; + struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + unsigned char *outp = videobuf_to_vmalloc (&(*buf)->vb); + int i, len = 0, rc = 1; + char *p; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status<0) { + print_err_status (dev,-1,urb->status); + if (urb->status == -ENOENT) + return 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status<0) { + print_err_status (dev,i,status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + len=urb->iso_frame_desc[i].actual_length - 4; + + if (urb->iso_frame_desc[i].actual_length <= 0) { + em28xx_isocdbg("packet %d is empty",i); + continue; + } + if (urb->iso_frame_desc[i].actual_length > + dev->max_pkt_size) { + em28xx_isocdbg("packet bigger than packet size"); + continue; + } + + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + + /* FIXME: incomplete buffer checks where removed to make + logic simpler. Impacts of those changes should be evaluated + */ + if (outp[0] == 0x22 && outp[1] == 0x5a) { + em28xx_isocdbg("Video frame, length=%i, %s", len, + (outp[2] == 1)? "top" : "botton"); + + if (outp[2] == 1) { + if ((*buf)->receiving) + buffer_filled (dev, dma_q, *buf); + + (*buf)->top_field = 1; + } else { + (*buf)->top_field = 0; + } + (*buf)->receiving = 1; + dma_q->pos = 0; + } else if (outp[0]==0x33 && outp[1]==0x95 && outp[2]==0x00) { + em28xx_isocdbg("VBI HEADER!!!"); + } + + em28xx_copy_video(dev, dma_q, *buf, p, outp, len); + + /* FIXME: Should add vbi copy */ + } + return rc; +} + +/* ------------------------------------------------------------------ + URB control + ------------------------------------------------------------------*/ + +/* + * IRQ callback, called by URB callback + */ +static void em28xx_irq_callback(struct urb *urb) +{ + struct em28xx_buffer *buf; + struct em28xx_dmaqueue *dma_q = urb->context; + struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + int rc,i; + unsigned long flags; + + spin_lock_irqsave(&dev->slock,flags); + + buf=dev->isoc_ctl.buf; + + if (!buf) { + rc=get_next_buf (dma_q, &buf); + if (rc<=0) + goto ret; + } + + /* Copy data from URB */ + rc=em28xx_isoc_copy(urb, &buf); + + dev->isoc_ctl.buf=buf; +ret: + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + urb->status = 0; + + if ((urb->status = usb_submit_urb(urb, GFP_ATOMIC))) { + em28xx_err("urb resubmit failed (error=%i)\n", + urb->status); + } + + if (rc >= 0) + mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT); + spin_unlock_irqrestore(&dev->slock,flags); +} + +/* + * Stop and Deallocate URBs + */ +static void em28xx_uninit_isoc(struct em28xx *dev) +{ + struct urb *urb; + int i; + + dev->isoc_ctl.nfields=-1; + dev->isoc_ctl.buf=NULL; + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb=dev->isoc_ctl.urb[i]; + if (urb) { + usb_kill_urb(urb); + usb_unlink_urb(urb); + if (dev->isoc_ctl.transfer_buffer[i]) { + usb_buffer_free(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; + } + + kfree (dev->isoc_ctl.urb); + kfree (dev->isoc_ctl.transfer_buffer); + dev->isoc_ctl.urb=NULL; + dev->isoc_ctl.transfer_buffer=NULL; + + dev->isoc_ctl.num_bufs=0; + + em28xx_capture_start(dev, 0); +} + +/* + * Stop video thread - FIXME: Can be easily removed + */ +static void em28xx_stop_thread(struct em28xx_dmaqueue *dma_q) +{ + struct em28xx *dev= container_of(dma_q, struct em28xx, vidq); + + em28xx_uninit_isoc(dev); +} + +/* + * Allocate URBs and start IRQ + */ +static int em28xx_prepare_isoc(struct em28xx *dev, int max_packets, + int num_bufs) +{ + struct em28xx_dmaqueue *dma_q = &dev->vidq; + int i; + int sb_size, pipe; + struct urb *urb; + int j, k; + + /* De-allocates all pending stuff */ + em28xx_uninit_isoc(dev); + + dev->isoc_ctl.num_bufs = num_bufs; + + dev->isoc_ctl.urb = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + em28xx_errdev("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->isoc_ctl.transfer_buffer = kmalloc(sizeof(void *)*num_bufs, + GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + em28xx_errdev("cannot allocate memory for usbtransfer\n"); + kfree(dev->isoc_ctl.urb); + return -ENOMEM; + } + + dev->isoc_ctl.max_pkt_size = dev->max_pkt_size; + + sb_size = max_packets * dev->isoc_ctl.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); + if (!urb) { + em28xx_err("cannot alloc isoc_ctl.urb %i\n", i); + em28xx_uninit_isoc(dev); + usb_free_urb(urb); + return -ENOMEM; + } + dev->isoc_ctl.urb[i] = urb; + + dev->isoc_ctl.transfer_buffer[i] = usb_buffer_alloc(dev->udev, + sb_size, GFP_KERNEL, &urb->transfer_dma); + if (!dev->isoc_ctl.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); + return -ENOMEM; + } + memset(dev->isoc_ctl.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, 0x82); + usb_fill_int_urb(urb, dev->udev, pipe, + dev->isoc_ctl.transfer_buffer[i], sb_size, + em28xx_irq_callback, dma_q, 1); + + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP; + + k = 0; + for (j = 0; j < max_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; + } + } + + return 0; +} + +static int em28xx_start_thread( struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf) +{ + struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + int i,rc; + + init_waitqueue_head(&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); + if (rc) { + em28xx_err("submit of urb %i failed (error=%i)\n", i, + rc); + em28xx_uninit_isoc(dev); + return rc; + } + } + + if (rc<0) + return rc; + + return 0; +} + +static int restart_video_queue(struct em28xx_dmaqueue *dma_q) +{ + struct em28xx *dev= container_of(dma_q,struct em28xx,vidq); + + struct em28xx_buffer *buf, *prev; + struct list_head *item; + + em28xx_videodbg("%s dma_q=0x%08lx\n", + __FUNCTION__,(unsigned long)dma_q); + + if (!list_empty(&dma_q->active)) { + buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); + em28xx_videodbg("restart_queue [%p/%d]: restart dma\n", + buf, buf->vb.i); + em28xx_stop_thread(dma_q); + em28xx_start_thread(dma_q, buf); + + /* cancel all outstanding capture / vbi requests */ + list_for_each(item,&dma_q->active) { + buf = list_entry(item, struct em28xx_buffer, vb.queue); + + list_del(&buf->vb.queue); + buf->vb.state = VIDEOBUF_ERROR; + wake_up(&buf->vb.done); + } + mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT); + + return 0; + } + + prev = NULL; + for (;;) { + if (list_empty(&dma_q->queued)) + return 0; + buf = list_entry(dma_q->queued.next, struct em28xx_buffer, vb.queue); + if (NULL == prev) { + list_del(&buf->vb.queue); + list_add_tail(&buf->vb.queue,&dma_q->active); + + em28xx_videodbg("Restarting video dma\n"); + em28xx_stop_thread(dma_q); + em28xx_start_thread(dma_q, buf); + + buf->vb.state = VIDEOBUF_ACTIVE; + mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT); + em28xx_videodbg("[%p/%d] restart_queue -" + " first active\n", buf, buf->vb.i); + + } else if (prev->vb.width == buf->vb.width && + prev->vb.height == buf->vb.height && + prev->fmt == buf->fmt) { + list_del(&buf->vb.queue); + list_add_tail(&buf->vb.queue,&dma_q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + em28xx_videodbg("[%p/%d] restart_queue -" + " move to active\n",buf,buf->vb.i); + } else { + return 0; + } + prev = buf; + } +} + +static void em28xx_vid_timeout(unsigned long data) +{ + struct em28xx *dev = (struct em28xx*)data; + struct em28xx_dmaqueue *vidq = &dev->vidq; + struct em28xx_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&dev->slock,flags); + while (!list_empty(&vidq->active)) { + buf = list_entry(vidq->active.next, struct em28xx_buffer, + vb.queue); + list_del(&buf->vb.queue); + buf->vb.state = VIDEOBUF_ERROR; + wake_up(&buf->vb.done); + em28xx_videodbg("em28xx/0: [%p/%d] timeout\n", + buf, buf->vb.i); + } + + restart_video_queue(vidq); + spin_unlock_irqrestore(&dev->slock,flags); +} + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static int +buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +{ + struct em28xx_fh *fh = vq->priv_data; + + *size = 16 * fh->width * fh->height >> 3; + if (0 == *count) + *count = EM28XX_DEF_BUF; + + if (*count < EM28XX_MIN_BUF) { + *count=EM28XX_MIN_BUF; + } + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) +{ + if (in_interrupt()) + BUG(); + + videobuf_waiton(&buf->vb, 0, 0); + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct em28xx_fh *fh = vq->priv_data; + struct em28xx_buffer *buf = container_of(vb,struct em28xx_buffer,vb); + struct em28xx *dev = fh->dev; + int rc = 0, urb_init = 0; + const int urbsize = EM28XX_NUM_PACKETS * dev->max_pkt_size; + + BUG_ON(NULL == fh->fmt); + + /* FIXME: It assumes depth = 16 */ + /* The only currently supported format is 16 bits/pixel */ + buf->vb.size = 16 * fh->width * fh->height >> 3; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + if (buf->fmt != fh->fmt || + buf->vb.width != fh->width || + buf->vb.height != fh->height || + buf->vb.field != field) { + buf->fmt = fh->fmt; + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + buf->vb.state = VIDEOBUF_NEEDS_INIT; + } + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + if (0 != (rc = videobuf_iolock(vq,&buf->vb,NULL))) + goto fail; + urb_init=1; + } + + + if (!dev->isoc_ctl.num_bufs) + urb_init=1; + + if (urb_init) { + rc = em28xx_prepare_isoc(dev, urbsize, EM28XX_NUM_BUFS); + if (rc<0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq,buf); + return rc; +} + +static void +buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + struct em28xx_dmaqueue *vidq = &dev->vidq; + struct em28xx_buffer *prev; + + if (!list_empty(&vidq->queued)) { + list_add_tail(&buf->vb.queue,&vidq->queued); + buf->vb.state = VIDEOBUF_QUEUED; + em28xx_videodbg("[%p/%d] buffer_queue - append to queued\n", + buf, buf->vb.i); + } else if (list_empty(&vidq->active)) { + list_add_tail(&buf->vb.queue,&vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); + em28xx_videodbg("[%p/%d] buffer_queue - first active\n", + buf, buf->vb.i); + em28xx_start_thread(vidq, buf); + } else { + prev = list_entry(vidq->active.prev, struct em28xx_buffer, + vb.queue); + if (prev->vb.width == buf->vb.width && + prev->vb.height == buf->vb.height && + prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue,&vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + em28xx_videodbg("[%p/%d] buffer_queue -" + " append to active\n", buf, buf->vb.i); + } else { + list_add_tail(&buf->vb.queue,&vidq->queued); + buf->vb.state = VIDEOBUF_QUEUED; + em28xx_videodbg("[%p/%d] buffer_queue - first queued\n", + buf, buf->vb.i); + } + } +} + +static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb,struct em28xx_buffer,vb); + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = (struct em28xx*)fh->dev; + struct em28xx_dmaqueue *vidq = &dev->vidq; + + em28xx_stop_th |