Path: blob/master/drivers/media/video/ivtv/ivtv-queue.c
17602 views
/*1buffer queues.2Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>3Copyright (C) 2004 Chris Kennedy <[email protected]>4Copyright (C) 2005-2007 Hans Verkuil <[email protected]>56This program is free software; you can redistribute it and/or modify7it under the terms of the GNU General Public License as published by8the Free Software Foundation; either version 2 of the License, or9(at your option) any later version.1011This program is distributed in the hope that it will be useful,12but WITHOUT ANY WARRANTY; without even the implied warranty of13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14GNU General Public License for more details.1516You should have received a copy of the GNU General Public License17along with this program; if not, write to the Free Software18Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA19*/2021#include "ivtv-driver.h"22#include "ivtv-queue.h"2324int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes)25{26if (s->buf_size - buf->bytesused < copybytes)27copybytes = s->buf_size - buf->bytesused;28if (copy_from_user(buf->buf + buf->bytesused, src, copybytes)) {29return -EFAULT;30}31buf->bytesused += copybytes;32return copybytes;33}3435void ivtv_buf_swap(struct ivtv_buffer *buf)36{37int i;3839for (i = 0; i < buf->bytesused; i += 4)40swab32s((u32 *)(buf->buf + i));41}4243void ivtv_queue_init(struct ivtv_queue *q)44{45INIT_LIST_HEAD(&q->list);46q->buffers = 0;47q->length = 0;48q->bytesused = 0;49}5051void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q)52{53unsigned long flags;5455/* clear the buffer if it is going to be enqueued to the free queue */56if (q == &s->q_free) {57buf->bytesused = 0;58buf->readpos = 0;59buf->b_flags = 0;60buf->dma_xfer_cnt = 0;61}62spin_lock_irqsave(&s->qlock, flags);63list_add_tail(&buf->list, &q->list);64q->buffers++;65q->length += s->buf_size;66q->bytesused += buf->bytesused - buf->readpos;67spin_unlock_irqrestore(&s->qlock, flags);68}6970struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q)71{72struct ivtv_buffer *buf = NULL;73unsigned long flags;7475spin_lock_irqsave(&s->qlock, flags);76if (!list_empty(&q->list)) {77buf = list_entry(q->list.next, struct ivtv_buffer, list);78list_del_init(q->list.next);79q->buffers--;80q->length -= s->buf_size;81q->bytesused -= buf->bytesused - buf->readpos;82}83spin_unlock_irqrestore(&s->qlock, flags);84return buf;85}8687static void ivtv_queue_move_buf(struct ivtv_stream *s, struct ivtv_queue *from,88struct ivtv_queue *to, int clear)89{90struct ivtv_buffer *buf = list_entry(from->list.next, struct ivtv_buffer, list);9192list_move_tail(from->list.next, &to->list);93from->buffers--;94from->length -= s->buf_size;95from->bytesused -= buf->bytesused - buf->readpos;96/* special handling for q_free */97if (clear)98buf->bytesused = buf->readpos = buf->b_flags = buf->dma_xfer_cnt = 0;99to->buffers++;100to->length += s->buf_size;101to->bytesused += buf->bytesused - buf->readpos;102}103104/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.105If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.106If 'steal' != NULL, then buffers may also taken from that queue if107needed, but only if 'from' is the free queue.108109The buffer is automatically cleared if it goes to the free queue. It is110also cleared if buffers need to be taken from the 'steal' queue and111the 'from' queue is the free queue.112113When 'from' is q_free, then needed_bytes is compared to the total114available buffer length, otherwise needed_bytes is compared to the115bytesused value. For the 'steal' queue the total available buffer116length is always used.117118-ENOMEM is returned if the buffers could not be obtained, 0 if all119buffers where obtained from the 'from' list and if non-zero then120the number of stolen buffers is returned. */121int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,122struct ivtv_queue *to, int needed_bytes)123{124unsigned long flags;125int rc = 0;126int from_free = from == &s->q_free;127int to_free = to == &s->q_free;128int bytes_available, bytes_steal;129130spin_lock_irqsave(&s->qlock, flags);131if (needed_bytes == 0) {132from_free = 1;133needed_bytes = from->length;134}135136bytes_available = from_free ? from->length : from->bytesused;137bytes_steal = (from_free && steal) ? steal->length : 0;138139if (bytes_available + bytes_steal < needed_bytes) {140spin_unlock_irqrestore(&s->qlock, flags);141return -ENOMEM;142}143while (bytes_available < needed_bytes) {144struct ivtv_buffer *buf = list_entry(steal->list.prev, struct ivtv_buffer, list);145u16 dma_xfer_cnt = buf->dma_xfer_cnt;146147/* move buffers from the tail of the 'steal' queue to the tail of the148'from' queue. Always copy all the buffers with the same dma_xfer_cnt149value, this ensures that you do not end up with partial frame data150if one frame is stored in multiple buffers. */151while (dma_xfer_cnt == buf->dma_xfer_cnt) {152list_move_tail(steal->list.prev, &from->list);153rc++;154steal->buffers--;155steal->length -= s->buf_size;156steal->bytesused -= buf->bytesused - buf->readpos;157buf->bytesused = buf->readpos = buf->b_flags = buf->dma_xfer_cnt = 0;158from->buffers++;159from->length += s->buf_size;160bytes_available += s->buf_size;161if (list_empty(&steal->list))162break;163buf = list_entry(steal->list.prev, struct ivtv_buffer, list);164}165}166if (from_free) {167u32 old_length = to->length;168169while (to->length - old_length < needed_bytes) {170ivtv_queue_move_buf(s, from, to, 1);171}172}173else {174u32 old_bytesused = to->bytesused;175176while (to->bytesused - old_bytesused < needed_bytes) {177ivtv_queue_move_buf(s, from, to, to_free);178}179}180spin_unlock_irqrestore(&s->qlock, flags);181return rc;182}183184void ivtv_flush_queues(struct ivtv_stream *s)185{186ivtv_queue_move(s, &s->q_io, NULL, &s->q_free, 0);187ivtv_queue_move(s, &s->q_full, NULL, &s->q_free, 0);188ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);189ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0);190}191192int ivtv_stream_alloc(struct ivtv_stream *s)193{194struct ivtv *itv = s->itv;195int SGsize = sizeof(struct ivtv_sg_host_element) * s->buffers;196int i;197198if (s->buffers == 0)199return 0;200201IVTV_DEBUG_INFO("Allocate %s%s stream: %d x %d buffers (%dkB total)\n",202s->dma != PCI_DMA_NONE ? "DMA " : "",203s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024);204205s->sg_pending = kzalloc(SGsize, GFP_KERNEL|__GFP_NOWARN);206if (s->sg_pending == NULL) {207IVTV_ERR("Could not allocate sg_pending for %s stream\n", s->name);208return -ENOMEM;209}210s->sg_pending_size = 0;211212s->sg_processing = kzalloc(SGsize, GFP_KERNEL|__GFP_NOWARN);213if (s->sg_processing == NULL) {214IVTV_ERR("Could not allocate sg_processing for %s stream\n", s->name);215kfree(s->sg_pending);216s->sg_pending = NULL;217return -ENOMEM;218}219s->sg_processing_size = 0;220221s->sg_dma = kzalloc(sizeof(struct ivtv_sg_element),222GFP_KERNEL|__GFP_NOWARN);223if (s->sg_dma == NULL) {224IVTV_ERR("Could not allocate sg_dma for %s stream\n", s->name);225kfree(s->sg_pending);226s->sg_pending = NULL;227kfree(s->sg_processing);228s->sg_processing = NULL;229return -ENOMEM;230}231if (ivtv_might_use_dma(s)) {232s->sg_handle = pci_map_single(itv->pdev, s->sg_dma,233sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);234ivtv_stream_sync_for_cpu(s);235}236237/* allocate stream buffers. Initially all buffers are in q_free. */238for (i = 0; i < s->buffers; i++) {239struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer),240GFP_KERNEL|__GFP_NOWARN);241242if (buf == NULL)243break;244buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL|__GFP_NOWARN);245if (buf->buf == NULL) {246kfree(buf);247break;248}249INIT_LIST_HEAD(&buf->list);250if (ivtv_might_use_dma(s)) {251buf->dma_handle = pci_map_single(s->itv->pdev,252buf->buf, s->buf_size + 256, s->dma);253ivtv_buf_sync_for_cpu(s, buf);254}255ivtv_enqueue(s, buf, &s->q_free);256}257if (i == s->buffers)258return 0;259IVTV_ERR("Couldn't allocate buffers for %s stream\n", s->name);260ivtv_stream_free(s);261return -ENOMEM;262}263264void ivtv_stream_free(struct ivtv_stream *s)265{266struct ivtv_buffer *buf;267268/* move all buffers to q_free */269ivtv_flush_queues(s);270271/* empty q_free */272while ((buf = ivtv_dequeue(s, &s->q_free))) {273if (ivtv_might_use_dma(s))274pci_unmap_single(s->itv->pdev, buf->dma_handle,275s->buf_size + 256, s->dma);276kfree(buf->buf);277kfree(buf);278}279280/* Free SG Array/Lists */281if (s->sg_dma != NULL) {282if (s->sg_handle != IVTV_DMA_UNMAPPED) {283pci_unmap_single(s->itv->pdev, s->sg_handle,284sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);285s->sg_handle = IVTV_DMA_UNMAPPED;286}287kfree(s->sg_pending);288kfree(s->sg_processing);289kfree(s->sg_dma);290s->sg_pending = NULL;291s->sg_processing = NULL;292s->sg_dma = NULL;293s->sg_pending_size = 0;294s->sg_processing_size = 0;295}296}297298299