 0c0d06cac6
			
		
	
	
	0c0d06cac6
	
	
	
		
			
			Rename all USB drivers with their own directory under drivers/media/video into drivers/media/usb and update the building system. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
		
			
				
	
	
		
			695 lines
		
	
	
	
		
			18 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			695 lines
		
	
	
	
		
			18 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *
 | |
|  *
 | |
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
 | |
|  *
 | |
|  *  This program is free software; you can redistribute it and/or modify
 | |
|  *  it under the terms of the GNU General Public License as published by
 | |
|  *  the Free Software Foundation; either version 2 of the License
 | |
|  *
 | |
|  *  This program is distributed in the hope that it will be useful,
 | |
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  *  GNU General Public License for more details.
 | |
|  *
 | |
|  *  You should have received a copy of the GNU General Public License
 | |
|  *  along with this program; if not, write to the Free Software
 | |
|  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "pvrusb2-io.h"
 | |
| #include "pvrusb2-debug.h"
 | |
| #include <linux/errno.h>
 | |
| #include <linux/string.h>
 | |
| #include <linux/slab.h>
 | |
| #include <linux/mutex.h>
 | |
| 
 | |
| static const char *pvr2_buffer_state_decode(enum pvr2_buffer_state);
 | |
| 
 | |
| #define BUFFER_SIG 0x47653271
 | |
| 
 | |
| // #define SANITY_CHECK_BUFFERS
 | |
| 
 | |
| 
 | |
| #ifdef SANITY_CHECK_BUFFERS
 | |
| #define BUFFER_CHECK(bp) do { \
 | |
| 	if ((bp)->signature != BUFFER_SIG) { \
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, \
 | |
| 		"Buffer %p is bad at %s:%d", \
 | |
| 		(bp),__FILE__,__LINE__); \
 | |
| 		pvr2_buffer_describe(bp,"BadSig"); \
 | |
| 		BUG(); \
 | |
| 	} \
 | |
| } while (0)
 | |
| #else
 | |
| #define BUFFER_CHECK(bp) do {} while(0)
 | |
| #endif
 | |
| 
 | |
| struct pvr2_stream {
 | |
| 	/* Buffers queued for reading */
 | |
| 	struct list_head queued_list;
 | |
| 	unsigned int q_count;
 | |
| 	unsigned int q_bcount;
 | |
| 	/* Buffers with retrieved data */
 | |
| 	struct list_head ready_list;
 | |
| 	unsigned int r_count;
 | |
| 	unsigned int r_bcount;
 | |
| 	/* Buffers available for use */
 | |
| 	struct list_head idle_list;
 | |
| 	unsigned int i_count;
 | |
| 	unsigned int i_bcount;
 | |
| 	/* Pointers to all buffers */
 | |
| 	struct pvr2_buffer **buffers;
 | |
| 	/* Array size of buffers */
 | |
| 	unsigned int buffer_slot_count;
 | |
| 	/* Total buffers actually in circulation */
 | |
| 	unsigned int buffer_total_count;
 | |
| 	/* Designed number of buffers to be in circulation */
 | |
| 	unsigned int buffer_target_count;
 | |
| 	/* Executed when ready list become non-empty */
 | |
| 	pvr2_stream_callback callback_func;
 | |
| 	void *callback_data;
 | |
| 	/* Context for transfer endpoint */
 | |
| 	struct usb_device *dev;
 | |
| 	int endpoint;
 | |
| 	/* Overhead for mutex enforcement */
 | |
| 	spinlock_t list_lock;
 | |
| 	struct mutex mutex;
 | |
| 	/* Tracking state for tolerating errors */
 | |
| 	unsigned int fail_count;
 | |
| 	unsigned int fail_tolerance;
 | |
| 
 | |
| 	unsigned int buffers_processed;
 | |
| 	unsigned int buffers_failed;
 | |
| 	unsigned int bytes_processed;
 | |
| };
 | |
| 
 | |
| struct pvr2_buffer {
 | |
| 	int id;
 | |
| 	int signature;
 | |
| 	enum pvr2_buffer_state state;
 | |
| 	void *ptr;               /* Pointer to storage area */
 | |
| 	unsigned int max_count;  /* Size of storage area */
 | |
| 	unsigned int used_count; /* Amount of valid data in storage area */
 | |
| 	int status;              /* Transfer result status */
 | |
| 	struct pvr2_stream *stream;
 | |
| 	struct list_head list_overhead;
 | |
| 	struct urb *purb;
 | |
| };
 | |
| 
 | |
| static const char *pvr2_buffer_state_decode(enum pvr2_buffer_state st)
 | |
| {
 | |
| 	switch (st) {
 | |
| 	case pvr2_buffer_state_none: return "none";
 | |
| 	case pvr2_buffer_state_idle: return "idle";
 | |
| 	case pvr2_buffer_state_queued: return "queued";
 | |
| 	case pvr2_buffer_state_ready: return "ready";
 | |
| 	}
 | |
| 	return "unknown";
 | |
| }
 | |
| 
 | |
| #ifdef SANITY_CHECK_BUFFERS
 | |
| static void pvr2_buffer_describe(struct pvr2_buffer *bp,const char *msg)
 | |
| {
 | |
| 	pvr2_trace(PVR2_TRACE_INFO,
 | |
| 		   "buffer%s%s %p state=%s id=%d status=%d"
 | |
| 		   " stream=%p purb=%p sig=0x%x",
 | |
| 		   (msg ? " " : ""),
 | |
| 		   (msg ? msg : ""),
 | |
| 		   bp,
 | |
| 		   (bp ? pvr2_buffer_state_decode(bp->state) : "(invalid)"),
 | |
| 		   (bp ? bp->id : 0),
 | |
| 		   (bp ? bp->status : 0),
 | |
| 		   (bp ? bp->stream : NULL),
 | |
| 		   (bp ? bp->purb : NULL),
 | |
| 		   (bp ? bp->signature : 0));
 | |
| }
 | |
| #endif  /*  SANITY_CHECK_BUFFERS  */
 | |
| 
 | |
| static void pvr2_buffer_remove(struct pvr2_buffer *bp)
 | |
| {
 | |
| 	unsigned int *cnt;
 | |
| 	unsigned int *bcnt;
 | |
| 	unsigned int ccnt;
 | |
| 	struct pvr2_stream *sp = bp->stream;
 | |
| 	switch (bp->state) {
 | |
| 	case pvr2_buffer_state_idle:
 | |
| 		cnt = &sp->i_count;
 | |
| 		bcnt = &sp->i_bcount;
 | |
| 		ccnt = bp->max_count;
 | |
| 		break;
 | |
| 	case pvr2_buffer_state_queued:
 | |
| 		cnt = &sp->q_count;
 | |
| 		bcnt = &sp->q_bcount;
 | |
| 		ccnt = bp->max_count;
 | |
| 		break;
 | |
| 	case pvr2_buffer_state_ready:
 | |
| 		cnt = &sp->r_count;
 | |
| 		bcnt = &sp->r_bcount;
 | |
| 		ccnt = bp->used_count;
 | |
| 		break;
 | |
| 	default:
 | |
| 		return;
 | |
| 	}
 | |
| 	list_del_init(&bp->list_overhead);
 | |
| 	(*cnt)--;
 | |
| 	(*bcnt) -= ccnt;
 | |
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
 | |
| 		   "/*---TRACE_FLOW---*/"
 | |
| 		   " bufferPool     %8s dec cap=%07d cnt=%02d",
 | |
| 		   pvr2_buffer_state_decode(bp->state),*bcnt,*cnt);
 | |
| 	bp->state = pvr2_buffer_state_none;
 | |
| }
 | |
| 
 | |
| static void pvr2_buffer_set_none(struct pvr2_buffer *bp)
 | |
| {
 | |
| 	unsigned long irq_flags;
 | |
| 	struct pvr2_stream *sp;
 | |
| 	BUFFER_CHECK(bp);
 | |
| 	sp = bp->stream;
 | |
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
 | |
| 		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s",
 | |
| 		   bp,
 | |
| 		   pvr2_buffer_state_decode(bp->state),
 | |
| 		   pvr2_buffer_state_decode(pvr2_buffer_state_none));
 | |
| 	spin_lock_irqsave(&sp->list_lock,irq_flags);
 | |
| 	pvr2_buffer_remove(bp);
 | |
| 	spin_unlock_irqrestore(&sp->list_lock,irq_flags);
 | |
| }
 | |
| 
 | |
| static int pvr2_buffer_set_ready(struct pvr2_buffer *bp)
 | |
| {
 | |
| 	int fl;
 | |
| 	unsigned long irq_flags;
 | |
| 	struct pvr2_stream *sp;
 | |
| 	BUFFER_CHECK(bp);
 | |
| 	sp = bp->stream;
 | |
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
 | |
| 		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s",
 | |
| 		   bp,
 | |
| 		   pvr2_buffer_state_decode(bp->state),
 | |
| 		   pvr2_buffer_state_decode(pvr2_buffer_state_ready));
 | |
| 	spin_lock_irqsave(&sp->list_lock,irq_flags);
 | |
| 	fl = (sp->r_count == 0);
 | |
| 	pvr2_buffer_remove(bp);
 | |
| 	list_add_tail(&bp->list_overhead,&sp->ready_list);
 | |
| 	bp->state = pvr2_buffer_state_ready;
 | |
| 	(sp->r_count)++;
 | |
| 	sp->r_bcount += bp->used_count;
 | |
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
 | |
| 		   "/*---TRACE_FLOW---*/"
 | |
| 		   " bufferPool     %8s inc cap=%07d cnt=%02d",
 | |
| 		   pvr2_buffer_state_decode(bp->state),
 | |
| 		   sp->r_bcount,sp->r_count);
 | |
| 	spin_unlock_irqrestore(&sp->list_lock,irq_flags);
 | |
| 	return fl;
 | |
| }
 | |
| 
 | |
| static void pvr2_buffer_set_idle(struct pvr2_buffer *bp)
 | |
| {
 | |
| 	unsigned long irq_flags;
 | |
| 	struct pvr2_stream *sp;
 | |
| 	BUFFER_CHECK(bp);
 | |
| 	sp = bp->stream;
 | |
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
 | |
| 		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s",
 | |
| 		   bp,
 | |
| 		   pvr2_buffer_state_decode(bp->state),
 | |
| 		   pvr2_buffer_state_decode(pvr2_buffer_state_idle));
 | |
| 	spin_lock_irqsave(&sp->list_lock,irq_flags);
 | |
| 	pvr2_buffer_remove(bp);
 | |
| 	list_add_tail(&bp->list_overhead,&sp->idle_list);
 | |
| 	bp->state = pvr2_buffer_state_idle;
 | |
| 	(sp->i_count)++;
 | |
| 	sp->i_bcount += bp->max_count;
 | |
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
 | |
| 		   "/*---TRACE_FLOW---*/"
 | |
| 		   " bufferPool     %8s inc cap=%07d cnt=%02d",
 | |
| 		   pvr2_buffer_state_decode(bp->state),
 | |
| 		   sp->i_bcount,sp->i_count);
 | |
| 	spin_unlock_irqrestore(&sp->list_lock,irq_flags);
 | |
| }
 | |
| 
 | |
| static void pvr2_buffer_set_queued(struct pvr2_buffer *bp)
 | |
| {
 | |
| 	unsigned long irq_flags;
 | |
| 	struct pvr2_stream *sp;
 | |
| 	BUFFER_CHECK(bp);
 | |
| 	sp = bp->stream;
 | |
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
 | |
| 		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s",
 | |
| 		   bp,
 | |
| 		   pvr2_buffer_state_decode(bp->state),
 | |
| 		   pvr2_buffer_state_decode(pvr2_buffer_state_queued));
 | |
| 	spin_lock_irqsave(&sp->list_lock,irq_flags);
 | |
| 	pvr2_buffer_remove(bp);
 | |
| 	list_add_tail(&bp->list_overhead,&sp->queued_list);
 | |
| 	bp->state = pvr2_buffer_state_queued;
 | |
| 	(sp->q_count)++;
 | |
| 	sp->q_bcount += bp->max_count;
 | |
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
 | |
| 		   "/*---TRACE_FLOW---*/"
 | |
| 		   " bufferPool     %8s inc cap=%07d cnt=%02d",
 | |
| 		   pvr2_buffer_state_decode(bp->state),
 | |
| 		   sp->q_bcount,sp->q_count);
 | |
| 	spin_unlock_irqrestore(&sp->list_lock,irq_flags);
 | |
| }
 | |
| 
 | |
| static void pvr2_buffer_wipe(struct pvr2_buffer *bp)
 | |
| {
 | |
| 	if (bp->state == pvr2_buffer_state_queued) {
 | |
| 		usb_kill_urb(bp->purb);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int pvr2_buffer_init(struct pvr2_buffer *bp,
 | |
| 			    struct pvr2_stream *sp,
 | |
| 			    unsigned int id)
 | |
| {
 | |
| 	memset(bp,0,sizeof(*bp));
 | |
| 	bp->signature = BUFFER_SIG;
 | |
| 	bp->id = id;
 | |
| 	pvr2_trace(PVR2_TRACE_BUF_POOL,
 | |
| 		   "/*---TRACE_FLOW---*/ bufferInit     %p stream=%p",bp,sp);
 | |
| 	bp->stream = sp;
 | |
| 	bp->state = pvr2_buffer_state_none;
 | |
| 	INIT_LIST_HEAD(&bp->list_overhead);
 | |
| 	bp->purb = usb_alloc_urb(0,GFP_KERNEL);
 | |
| 	if (! bp->purb) return -ENOMEM;
 | |
| #ifdef SANITY_CHECK_BUFFERS
 | |
| 	pvr2_buffer_describe(bp,"create");
 | |
| #endif
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void pvr2_buffer_done(struct pvr2_buffer *bp)
 | |
| {
 | |
| #ifdef SANITY_CHECK_BUFFERS
 | |
| 	pvr2_buffer_describe(bp,"delete");
 | |
| #endif
 | |
| 	pvr2_buffer_wipe(bp);
 | |
| 	pvr2_buffer_set_none(bp);
 | |
| 	bp->signature = 0;
 | |
| 	bp->stream = NULL;
 | |
| 	usb_free_urb(bp->purb);
 | |
| 	pvr2_trace(PVR2_TRACE_BUF_POOL,"/*---TRACE_FLOW---*/"
 | |
| 		   " bufferDone     %p",bp);
 | |
| }
 | |
| 
 | |
| static int pvr2_stream_buffer_count(struct pvr2_stream *sp,unsigned int cnt)
 | |
| {
 | |
| 	int ret;
 | |
| 	unsigned int scnt;
 | |
| 
 | |
| 	/* Allocate buffers pointer array in multiples of 32 entries */
 | |
| 	if (cnt == sp->buffer_total_count) return 0;
 | |
| 
 | |
| 	pvr2_trace(PVR2_TRACE_BUF_POOL,
 | |
| 		   "/*---TRACE_FLOW---*/ poolResize    "
 | |
| 		   " stream=%p cur=%d adj=%+d",
 | |
| 		   sp,
 | |
| 		   sp->buffer_total_count,
 | |
| 		   cnt-sp->buffer_total_count);
 | |
| 
 | |
| 	scnt = cnt & ~0x1f;
 | |
| 	if (cnt > scnt) scnt += 0x20;
 | |
| 
 | |
| 	if (cnt > sp->buffer_total_count) {
 | |
| 		if (scnt > sp->buffer_slot_count) {
 | |
| 			struct pvr2_buffer **nb;
 | |
| 			nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL);
 | |
| 			if (!nb) return -ENOMEM;
 | |
| 			if (sp->buffer_slot_count) {
 | |
| 				memcpy(nb,sp->buffers,
 | |
| 				       sp->buffer_slot_count * sizeof(*nb));
 | |
| 				kfree(sp->buffers);
 | |
| 			}
 | |
| 			sp->buffers = nb;
 | |
| 			sp->buffer_slot_count = scnt;
 | |
| 		}
 | |
| 		while (sp->buffer_total_count < cnt) {
 | |
| 			struct pvr2_buffer *bp;
 | |
| 			bp = kmalloc(sizeof(*bp),GFP_KERNEL);
 | |
| 			if (!bp) return -ENOMEM;
 | |
| 			ret = pvr2_buffer_init(bp,sp,sp->buffer_total_count);
 | |
| 			if (ret) {
 | |
| 				kfree(bp);
 | |
| 				return -ENOMEM;
 | |
| 			}
 | |
| 			sp->buffers[sp->buffer_total_count] = bp;
 | |
| 			(sp->buffer_total_count)++;
 | |
| 			pvr2_buffer_set_idle(bp);
 | |
| 		}
 | |
| 	} else {
 | |
| 		while (sp->buffer_total_count > cnt) {
 | |
| 			struct pvr2_buffer *bp;
 | |
| 			bp = sp->buffers[sp->buffer_total_count - 1];
 | |
| 			/* Paranoia */
 | |
| 			sp->buffers[sp->buffer_total_count - 1] = NULL;
 | |
| 			(sp->buffer_total_count)--;
 | |
| 			pvr2_buffer_done(bp);
 | |
| 			kfree(bp);
 | |
| 		}
 | |
| 		if (scnt < sp->buffer_slot_count) {
 | |
| 			struct pvr2_buffer **nb = NULL;
 | |
| 			if (scnt) {
 | |
| 				nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL);
 | |
| 				if (!nb) return -ENOMEM;
 | |
| 				memcpy(nb,sp->buffers,scnt * sizeof(*nb));
 | |
| 			}
 | |
| 			kfree(sp->buffers);
 | |
| 			sp->buffers = nb;
 | |
| 			sp->buffer_slot_count = scnt;
 | |
| 		}
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int pvr2_stream_achieve_buffer_count(struct pvr2_stream *sp)
 | |
| {
 | |
| 	struct pvr2_buffer *bp;
 | |
| 	unsigned int cnt;
 | |
| 
 | |
| 	if (sp->buffer_total_count == sp->buffer_target_count) return 0;
 | |
| 
 | |
| 	pvr2_trace(PVR2_TRACE_BUF_POOL,
 | |
| 		   "/*---TRACE_FLOW---*/"
 | |
| 		   " poolCheck      stream=%p cur=%d tgt=%d",
 | |
| 		   sp,sp->buffer_total_count,sp->buffer_target_count);
 | |
| 
 | |
| 	if (sp->buffer_total_count < sp->buffer_target_count) {
 | |
| 		return pvr2_stream_buffer_count(sp,sp->buffer_target_count);
 | |
| 	}
 | |
| 
 | |
| 	cnt = 0;
 | |
| 	while ((sp->buffer_total_count - cnt) > sp->buffer_target_count) {
 | |
| 		bp = sp->buffers[sp->buffer_total_count - (cnt + 1)];
 | |
| 		if (bp->state != pvr2_buffer_state_idle) break;
 | |
| 		cnt++;
 | |
| 	}
 | |
| 	if (cnt) {
 | |
| 		pvr2_stream_buffer_count(sp,sp->buffer_total_count - cnt);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void pvr2_stream_internal_flush(struct pvr2_stream *sp)
 | |
| {
 | |
| 	struct list_head *lp;
 | |
| 	struct pvr2_buffer *bp1;
 | |
| 	while ((lp = sp->queued_list.next) != &sp->queued_list) {
 | |
| 		bp1 = list_entry(lp,struct pvr2_buffer,list_overhead);
 | |
| 		pvr2_buffer_wipe(bp1);
 | |
| 		/* At this point, we should be guaranteed that no
 | |
| 		   completion callback may happen on this buffer.  But it's
 | |
| 		   possible that it might have completed after we noticed
 | |
| 		   it but before we wiped it.  So double check its status
 | |
| 		   here first. */
 | |
| 		if (bp1->state != pvr2_buffer_state_queued) continue;
 | |
| 		pvr2_buffer_set_idle(bp1);
 | |
| 	}
 | |
| 	if (sp->buffer_total_count != sp->buffer_target_count) {
 | |
| 		pvr2_stream_achieve_buffer_count(sp);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void pvr2_stream_init(struct pvr2_stream *sp)
 | |
| {
 | |
| 	spin_lock_init(&sp->list_lock);
 | |
| 	mutex_init(&sp->mutex);
 | |
| 	INIT_LIST_HEAD(&sp->queued_list);
 | |
| 	INIT_LIST_HEAD(&sp->ready_list);
 | |
| 	INIT_LIST_HEAD(&sp->idle_list);
 | |
| }
 | |
| 
 | |
| static void pvr2_stream_done(struct pvr2_stream *sp)
 | |
| {
 | |
| 	mutex_lock(&sp->mutex); do {
 | |
| 		pvr2_stream_internal_flush(sp);
 | |
| 		pvr2_stream_buffer_count(sp,0);
 | |
| 	} while (0); mutex_unlock(&sp->mutex);
 | |
| }
 | |
| 
 | |
| static void buffer_complete(struct urb *urb)
 | |
| {
 | |
| 	struct pvr2_buffer *bp = urb->context;
 | |
| 	struct pvr2_stream *sp;
 | |
| 	unsigned long irq_flags;
 | |
| 	BUFFER_CHECK(bp);
 | |
| 	sp = bp->stream;
 | |
| 	bp->used_count = 0;
 | |
| 	bp->status = 0;
 | |
| 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
 | |
| 		   "/*---TRACE_FLOW---*/ bufferComplete %p stat=%d cnt=%d",
 | |
| 		   bp,urb->status,urb->actual_length);
 | |
| 	spin_lock_irqsave(&sp->list_lock,irq_flags);
 | |
| 	if ((!(urb->status)) ||
 | |
| 	    (urb->status == -ENOENT) ||
 | |
| 	    (urb->status == -ECONNRESET) ||
 | |
| 	    (urb->status == -ESHUTDOWN)) {
 | |
| 		(sp->buffers_processed)++;
 | |
| 		sp->bytes_processed += urb->actual_length;
 | |
| 		bp->used_count = urb->actual_length;
 | |
| 		if (sp->fail_count) {
 | |
| 			pvr2_trace(PVR2_TRACE_TOLERANCE,
 | |
| 				   "stream %p transfer ok"
 | |
| 				   " - fail count reset",sp);
 | |
| 			sp->fail_count = 0;
 | |
| 		}
 | |
| 	} else if (sp->fail_count < sp->fail_tolerance) {
 | |
| 		// We can tolerate this error, because we're below the
 | |
| 		// threshold...
 | |
| 		(sp->fail_count)++;
 | |
| 		(sp->buffers_failed)++;
 | |
| 		pvr2_trace(PVR2_TRACE_TOLERANCE,
 | |
| 			   "stream %p ignoring error %d"
 | |
| 			   " - fail count increased to %u",
 | |
| 			   sp,urb->status,sp->fail_count);
 | |
| 	} else {
 | |
| 		(sp->buffers_failed)++;
 | |
| 		bp->status = urb->status;
 | |
| 	}
 | |
| 	spin_unlock_irqrestore(&sp->list_lock,irq_flags);
 | |
| 	pvr2_buffer_set_ready(bp);
 | |
| 	if (sp && sp->callback_func) {
 | |
| 		sp->callback_func(sp->callback_data);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| struct pvr2_stream *pvr2_stream_create(void)
 | |
| {
 | |
| 	struct pvr2_stream *sp;
 | |
| 	sp = kzalloc(sizeof(*sp),GFP_KERNEL);
 | |
| 	if (!sp) return sp;
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_create: sp=%p",sp);
 | |
| 	pvr2_stream_init(sp);
 | |
| 	return sp;
 | |
| }
 | |
| 
 | |
| void pvr2_stream_destroy(struct pvr2_stream *sp)
 | |
| {
 | |
| 	if (!sp) return;
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_destroy: sp=%p",sp);
 | |
| 	pvr2_stream_done(sp);
 | |
| 	kfree(sp);
 | |
| }
 | |
| 
 | |
| void pvr2_stream_setup(struct pvr2_stream *sp,
 | |
| 		       struct usb_device *dev,
 | |
| 		       int endpoint,
 | |
| 		       unsigned int tolerance)
 | |
| {
 | |
| 	mutex_lock(&sp->mutex); do {
 | |
| 		pvr2_stream_internal_flush(sp);
 | |
| 		sp->dev = dev;
 | |
| 		sp->endpoint = endpoint;
 | |
| 		sp->fail_tolerance = tolerance;
 | |
| 	} while(0); mutex_unlock(&sp->mutex);
 | |
| }
 | |
| 
 | |
| void pvr2_stream_set_callback(struct pvr2_stream *sp,
 | |
| 			      pvr2_stream_callback func,
 | |
| 			      void *data)
 | |
| {
 | |
| 	unsigned long irq_flags;
 | |
| 	mutex_lock(&sp->mutex); do {
 | |
| 		spin_lock_irqsave(&sp->list_lock,irq_flags);
 | |
| 		sp->callback_data = data;
 | |
| 		sp->callback_func = func;
 | |
| 		spin_unlock_irqrestore(&sp->list_lock,irq_flags);
 | |
| 	} while(0); mutex_unlock(&sp->mutex);
 | |
| }
 | |
| 
 | |
| void pvr2_stream_get_stats(struct pvr2_stream *sp,
 | |
| 			   struct pvr2_stream_stats *stats,
 | |
| 			   int zero_counts)
 | |
| {
 | |
| 	unsigned long irq_flags;
 | |
| 	spin_lock_irqsave(&sp->list_lock,irq_flags);
 | |
| 	if (stats) {
 | |
| 		stats->buffers_in_queue = sp->q_count;
 | |
| 		stats->buffers_in_idle = sp->i_count;
 | |
| 		stats->buffers_in_ready = sp->r_count;
 | |
| 		stats->buffers_processed = sp->buffers_processed;
 | |
| 		stats->buffers_failed = sp->buffers_failed;
 | |
| 		stats->bytes_processed = sp->bytes_processed;
 | |
| 	}
 | |
| 	if (zero_counts) {
 | |
| 		sp->buffers_processed = 0;
 | |
| 		sp->buffers_failed = 0;
 | |
| 		sp->bytes_processed = 0;
 | |
| 	}
 | |
| 	spin_unlock_irqrestore(&sp->list_lock,irq_flags);
 | |
| }
 | |
| 
 | |
| /* Query / set the nominal buffer count */
 | |
| int pvr2_stream_get_buffer_count(struct pvr2_stream *sp)
 | |
| {
 | |
| 	return sp->buffer_target_count;
 | |
| }
 | |
| 
 | |
| int pvr2_stream_set_buffer_count(struct pvr2_stream *sp,unsigned int cnt)
 | |
| {
 | |
| 	int ret;
 | |
| 	if (sp->buffer_target_count == cnt) return 0;
 | |
| 	mutex_lock(&sp->mutex); do {
 | |
| 		sp->buffer_target_count = cnt;
 | |
| 		ret = pvr2_stream_achieve_buffer_count(sp);
 | |
| 	} while(0); mutex_unlock(&sp->mutex);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *sp)
 | |
| {
 | |
| 	struct list_head *lp = sp->idle_list.next;
 | |
| 	if (lp == &sp->idle_list) return NULL;
 | |
| 	return list_entry(lp,struct pvr2_buffer,list_overhead);
 | |
| }
 | |
| 
 | |
| struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *sp)
 | |
| {
 | |
| 	struct list_head *lp = sp->ready_list.next;
 | |
| 	if (lp == &sp->ready_list) return NULL;
 | |
| 	return list_entry(lp,struct pvr2_buffer,list_overhead);
 | |
| }
 | |
| 
 | |
| struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id)
 | |
| {
 | |
| 	if (id < 0) return NULL;
 | |
| 	if (id >= sp->buffer_total_count) return NULL;
 | |
| 	return sp->buffers[id];
 | |
| }
 | |
| 
 | |
| int pvr2_stream_get_ready_count(struct pvr2_stream *sp)
 | |
| {
 | |
| 	return sp->r_count;
 | |
| }
 | |
| 
 | |
| void pvr2_stream_kill(struct pvr2_stream *sp)
 | |
| {
 | |
| 	struct pvr2_buffer *bp;
 | |
| 	mutex_lock(&sp->mutex); do {
 | |
| 		pvr2_stream_internal_flush(sp);
 | |
| 		while ((bp = pvr2_stream_get_ready_buffer(sp)) != NULL) {
 | |
| 			pvr2_buffer_set_idle(bp);
 | |
| 		}
 | |
| 		if (sp->buffer_total_count != sp->buffer_target_count) {
 | |
| 			pvr2_stream_achieve_buffer_count(sp);
 | |
| 		}
 | |
| 	} while(0); mutex_unlock(&sp->mutex);
 | |
| }
 | |
| 
 | |
| int pvr2_buffer_queue(struct pvr2_buffer *bp)
 | |
| {
 | |
| #undef SEED_BUFFER
 | |
| #ifdef SEED_BUFFER
 | |
| 	unsigned int idx;
 | |
| 	unsigned int val;
 | |
| #endif
 | |
| 	int ret = 0;
 | |
| 	struct pvr2_stream *sp;
 | |
| 	if (!bp) return -EINVAL;
 | |
| 	sp = bp->stream;
 | |
| 	mutex_lock(&sp->mutex); do {
 | |
| 		pvr2_buffer_wipe(bp);
 | |
| 		if (!sp->dev) {
 | |
| 			ret = -EIO;
 | |
| 			break;
 | |
| 		}
 | |
| 		pvr2_buffer_set_queued(bp);
 | |
| #ifdef SEED_BUFFER
 | |
| 		for (idx = 0; idx < (bp->max_count) / 4; idx++) {
 | |
| 			val = bp->id << 24;
 | |
| 			val |= idx;
 | |
| 			((unsigned int *)(bp->ptr))[idx] = val;
 | |
| 		}
 | |
| #endif
 | |
| 		bp->status = -EINPROGRESS;
 | |
| 		usb_fill_bulk_urb(bp->purb,      // struct urb *urb
 | |
| 				  sp->dev,       // struct usb_device *dev
 | |
| 				  // endpoint (below)
 | |
| 				  usb_rcvbulkpipe(sp->dev,sp->endpoint),
 | |
| 				  bp->ptr,       // void *transfer_buffer
 | |
| 				  bp->max_count, // int buffer_length
 | |
| 				  buffer_complete,
 | |
| 				  bp);
 | |
| 		usb_submit_urb(bp->purb,GFP_KERNEL);
 | |
| 	} while(0); mutex_unlock(&sp->mutex);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int pvr2_buffer_set_buffer(struct pvr2_buffer *bp,void *ptr,unsigned int cnt)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 	unsigned long irq_flags;
 | |
| 	struct pvr2_stream *sp;
 | |
| 	if (!bp) return -EINVAL;
 | |
| 	sp = bp->stream;
 | |
| 	mutex_lock(&sp->mutex); do {
 | |
| 		spin_lock_irqsave(&sp->list_lock,irq_flags);
 | |
| 		if (bp->state != pvr2_buffer_state_idle) {
 | |
| 			ret = -EPERM;
 | |
| 		} else {
 | |
| 			bp->ptr = ptr;
 | |
| 			bp->stream->i_bcount -= bp->max_count;
 | |
| 			bp->max_count = cnt;
 | |
| 			bp->stream->i_bcount += bp->max_count;
 | |
| 			pvr2_trace(PVR2_TRACE_BUF_FLOW,
 | |
| 				   "/*---TRACE_FLOW---*/ bufferPool    "
 | |
| 				   " %8s cap cap=%07d cnt=%02d",
 | |
| 				   pvr2_buffer_state_decode(
 | |
| 					   pvr2_buffer_state_idle),
 | |
| 				   bp->stream->i_bcount,bp->stream->i_count);
 | |
| 		}
 | |
| 		spin_unlock_irqrestore(&sp->list_lock,irq_flags);
 | |
| 	} while(0); mutex_unlock(&sp->mutex);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| unsigned int pvr2_buffer_get_count(struct pvr2_buffer *bp)
 | |
| {
 | |
| 	return bp->used_count;
 | |
| }
 | |
| 
 | |
| int pvr2_buffer_get_status(struct pvr2_buffer *bp)
 | |
| {
 | |
| 	return bp->status;
 | |
| }
 | |
| 
 | |
| int pvr2_buffer_get_id(struct pvr2_buffer *bp)
 | |
| {
 | |
| 	return bp->id;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Stuff for Emacs to see, in order to encourage consistent editing style:
 | |
|   *** Local Variables: ***
 | |
|   *** mode: c ***
 | |
|   *** fill-column: 75 ***
 | |
|   *** tab-width: 8 ***
 | |
|   *** c-basic-offset: 8 ***
 | |
|   *** End: ***
 | |
|   */
 |