📄 pvrusb2-io.c
字号:
/* * * $Id$ * * 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)#endifstruct 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;};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_BUFFERSstatic 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -