162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2005 Mike Isely <isely@pobox.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "pvrusb2-io.h" 862306a36Sopenharmony_ci#include "pvrusb2-debug.h" 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/string.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/mutex.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic const char *pvr2_buffer_state_decode(enum pvr2_buffer_state); 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define BUFFER_SIG 0x47653271 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci// #define SANITY_CHECK_BUFFERS 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#ifdef SANITY_CHECK_BUFFERS 2262306a36Sopenharmony_ci#define BUFFER_CHECK(bp) do { \ 2362306a36Sopenharmony_ci if ((bp)->signature != BUFFER_SIG) { \ 2462306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_ERROR_LEGS, \ 2562306a36Sopenharmony_ci "Buffer %p is bad at %s:%d", \ 2662306a36Sopenharmony_ci (bp), __FILE__, __LINE__); \ 2762306a36Sopenharmony_ci pvr2_buffer_describe(bp, "BadSig"); \ 2862306a36Sopenharmony_ci BUG(); \ 2962306a36Sopenharmony_ci } \ 3062306a36Sopenharmony_ci} while (0) 3162306a36Sopenharmony_ci#else 3262306a36Sopenharmony_ci#define BUFFER_CHECK(bp) do {} while (0) 3362306a36Sopenharmony_ci#endif 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct pvr2_stream { 3662306a36Sopenharmony_ci /* Buffers queued for reading */ 3762306a36Sopenharmony_ci struct list_head queued_list; 3862306a36Sopenharmony_ci unsigned int q_count; 3962306a36Sopenharmony_ci unsigned int q_bcount; 4062306a36Sopenharmony_ci /* Buffers with retrieved data */ 4162306a36Sopenharmony_ci struct list_head ready_list; 4262306a36Sopenharmony_ci unsigned int r_count; 4362306a36Sopenharmony_ci unsigned int r_bcount; 4462306a36Sopenharmony_ci /* Buffers available for use */ 4562306a36Sopenharmony_ci struct list_head idle_list; 4662306a36Sopenharmony_ci unsigned int i_count; 4762306a36Sopenharmony_ci unsigned int i_bcount; 4862306a36Sopenharmony_ci /* Pointers to all buffers */ 4962306a36Sopenharmony_ci struct pvr2_buffer **buffers; 5062306a36Sopenharmony_ci /* Array size of buffers */ 5162306a36Sopenharmony_ci unsigned int buffer_slot_count; 5262306a36Sopenharmony_ci /* Total buffers actually in circulation */ 5362306a36Sopenharmony_ci unsigned int buffer_total_count; 5462306a36Sopenharmony_ci /* Designed number of buffers to be in circulation */ 5562306a36Sopenharmony_ci unsigned int buffer_target_count; 5662306a36Sopenharmony_ci /* Executed when ready list become non-empty */ 5762306a36Sopenharmony_ci pvr2_stream_callback callback_func; 5862306a36Sopenharmony_ci void *callback_data; 5962306a36Sopenharmony_ci /* Context for transfer endpoint */ 6062306a36Sopenharmony_ci struct usb_device *dev; 6162306a36Sopenharmony_ci int endpoint; 6262306a36Sopenharmony_ci /* Overhead for mutex enforcement */ 6362306a36Sopenharmony_ci spinlock_t list_lock; 6462306a36Sopenharmony_ci struct mutex mutex; 6562306a36Sopenharmony_ci /* Tracking state for tolerating errors */ 6662306a36Sopenharmony_ci unsigned int fail_count; 6762306a36Sopenharmony_ci unsigned int fail_tolerance; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci unsigned int buffers_processed; 7062306a36Sopenharmony_ci unsigned int buffers_failed; 7162306a36Sopenharmony_ci unsigned int bytes_processed; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistruct pvr2_buffer { 7562306a36Sopenharmony_ci int id; 7662306a36Sopenharmony_ci int signature; 7762306a36Sopenharmony_ci enum pvr2_buffer_state state; 7862306a36Sopenharmony_ci void *ptr; /* Pointer to storage area */ 7962306a36Sopenharmony_ci unsigned int max_count; /* Size of storage area */ 8062306a36Sopenharmony_ci unsigned int used_count; /* Amount of valid data in storage area */ 8162306a36Sopenharmony_ci int status; /* Transfer result status */ 8262306a36Sopenharmony_ci struct pvr2_stream *stream; 8362306a36Sopenharmony_ci struct list_head list_overhead; 8462306a36Sopenharmony_ci struct urb *purb; 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic const char *pvr2_buffer_state_decode(enum pvr2_buffer_state st) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci switch (st) { 9062306a36Sopenharmony_ci case pvr2_buffer_state_none: return "none"; 9162306a36Sopenharmony_ci case pvr2_buffer_state_idle: return "idle"; 9262306a36Sopenharmony_ci case pvr2_buffer_state_queued: return "queued"; 9362306a36Sopenharmony_ci case pvr2_buffer_state_ready: return "ready"; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci return "unknown"; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#ifdef SANITY_CHECK_BUFFERS 9962306a36Sopenharmony_cistatic void pvr2_buffer_describe(struct pvr2_buffer *bp, const char *msg) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_INFO, 10262306a36Sopenharmony_ci "buffer%s%s %p state=%s id=%d status=%d stream=%p purb=%p sig=0x%x", 10362306a36Sopenharmony_ci (msg ? " " : ""), 10462306a36Sopenharmony_ci (msg ? msg : ""), 10562306a36Sopenharmony_ci bp, 10662306a36Sopenharmony_ci (bp ? pvr2_buffer_state_decode(bp->state) : "(invalid)"), 10762306a36Sopenharmony_ci (bp ? bp->id : 0), 10862306a36Sopenharmony_ci (bp ? bp->status : 0), 10962306a36Sopenharmony_ci (bp ? bp->stream : NULL), 11062306a36Sopenharmony_ci (bp ? bp->purb : NULL), 11162306a36Sopenharmony_ci (bp ? bp->signature : 0)); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci#endif /* SANITY_CHECK_BUFFERS */ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void pvr2_buffer_remove(struct pvr2_buffer *bp) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci unsigned int *cnt; 11862306a36Sopenharmony_ci unsigned int *bcnt; 11962306a36Sopenharmony_ci unsigned int ccnt; 12062306a36Sopenharmony_ci struct pvr2_stream *sp = bp->stream; 12162306a36Sopenharmony_ci switch (bp->state) { 12262306a36Sopenharmony_ci case pvr2_buffer_state_idle: 12362306a36Sopenharmony_ci cnt = &sp->i_count; 12462306a36Sopenharmony_ci bcnt = &sp->i_bcount; 12562306a36Sopenharmony_ci ccnt = bp->max_count; 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci case pvr2_buffer_state_queued: 12862306a36Sopenharmony_ci cnt = &sp->q_count; 12962306a36Sopenharmony_ci bcnt = &sp->q_bcount; 13062306a36Sopenharmony_ci ccnt = bp->max_count; 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci case pvr2_buffer_state_ready: 13362306a36Sopenharmony_ci cnt = &sp->r_count; 13462306a36Sopenharmony_ci bcnt = &sp->r_bcount; 13562306a36Sopenharmony_ci ccnt = bp->used_count; 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci default: 13862306a36Sopenharmony_ci return; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci list_del_init(&bp->list_overhead); 14162306a36Sopenharmony_ci (*cnt)--; 14262306a36Sopenharmony_ci (*bcnt) -= ccnt; 14362306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_BUF_FLOW, 14462306a36Sopenharmony_ci "/*---TRACE_FLOW---*/ bufferPool %8s dec cap=%07d cnt=%02d", 14562306a36Sopenharmony_ci pvr2_buffer_state_decode(bp->state), *bcnt, *cnt); 14662306a36Sopenharmony_ci bp->state = pvr2_buffer_state_none; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic void pvr2_buffer_set_none(struct pvr2_buffer *bp) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci unsigned long irq_flags; 15262306a36Sopenharmony_ci struct pvr2_stream *sp; 15362306a36Sopenharmony_ci BUFFER_CHECK(bp); 15462306a36Sopenharmony_ci sp = bp->stream; 15562306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_BUF_FLOW, 15662306a36Sopenharmony_ci "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", 15762306a36Sopenharmony_ci bp, 15862306a36Sopenharmony_ci pvr2_buffer_state_decode(bp->state), 15962306a36Sopenharmony_ci pvr2_buffer_state_decode(pvr2_buffer_state_none)); 16062306a36Sopenharmony_ci spin_lock_irqsave(&sp->list_lock, irq_flags); 16162306a36Sopenharmony_ci pvr2_buffer_remove(bp); 16262306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->list_lock, irq_flags); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int pvr2_buffer_set_ready(struct pvr2_buffer *bp) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci int fl; 16862306a36Sopenharmony_ci unsigned long irq_flags; 16962306a36Sopenharmony_ci struct pvr2_stream *sp; 17062306a36Sopenharmony_ci BUFFER_CHECK(bp); 17162306a36Sopenharmony_ci sp = bp->stream; 17262306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_BUF_FLOW, 17362306a36Sopenharmony_ci "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", 17462306a36Sopenharmony_ci bp, 17562306a36Sopenharmony_ci pvr2_buffer_state_decode(bp->state), 17662306a36Sopenharmony_ci pvr2_buffer_state_decode(pvr2_buffer_state_ready)); 17762306a36Sopenharmony_ci spin_lock_irqsave(&sp->list_lock, irq_flags); 17862306a36Sopenharmony_ci fl = (sp->r_count == 0); 17962306a36Sopenharmony_ci pvr2_buffer_remove(bp); 18062306a36Sopenharmony_ci list_add_tail(&bp->list_overhead, &sp->ready_list); 18162306a36Sopenharmony_ci bp->state = pvr2_buffer_state_ready; 18262306a36Sopenharmony_ci (sp->r_count)++; 18362306a36Sopenharmony_ci sp->r_bcount += bp->used_count; 18462306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_BUF_FLOW, 18562306a36Sopenharmony_ci "/*---TRACE_FLOW---*/ bufferPool %8s inc cap=%07d cnt=%02d", 18662306a36Sopenharmony_ci pvr2_buffer_state_decode(bp->state), 18762306a36Sopenharmony_ci sp->r_bcount, sp->r_count); 18862306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->list_lock, irq_flags); 18962306a36Sopenharmony_ci return fl; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void pvr2_buffer_set_idle(struct pvr2_buffer *bp) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci unsigned long irq_flags; 19562306a36Sopenharmony_ci struct pvr2_stream *sp; 19662306a36Sopenharmony_ci BUFFER_CHECK(bp); 19762306a36Sopenharmony_ci sp = bp->stream; 19862306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_BUF_FLOW, 19962306a36Sopenharmony_ci "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", 20062306a36Sopenharmony_ci bp, 20162306a36Sopenharmony_ci pvr2_buffer_state_decode(bp->state), 20262306a36Sopenharmony_ci pvr2_buffer_state_decode(pvr2_buffer_state_idle)); 20362306a36Sopenharmony_ci spin_lock_irqsave(&sp->list_lock, irq_flags); 20462306a36Sopenharmony_ci pvr2_buffer_remove(bp); 20562306a36Sopenharmony_ci list_add_tail(&bp->list_overhead, &sp->idle_list); 20662306a36Sopenharmony_ci bp->state = pvr2_buffer_state_idle; 20762306a36Sopenharmony_ci (sp->i_count)++; 20862306a36Sopenharmony_ci sp->i_bcount += bp->max_count; 20962306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_BUF_FLOW, 21062306a36Sopenharmony_ci "/*---TRACE_FLOW---*/ bufferPool %8s inc cap=%07d cnt=%02d", 21162306a36Sopenharmony_ci pvr2_buffer_state_decode(bp->state), 21262306a36Sopenharmony_ci sp->i_bcount, sp->i_count); 21362306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->list_lock, irq_flags); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic void pvr2_buffer_set_queued(struct pvr2_buffer *bp) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci unsigned long irq_flags; 21962306a36Sopenharmony_ci struct pvr2_stream *sp; 22062306a36Sopenharmony_ci BUFFER_CHECK(bp); 22162306a36Sopenharmony_ci sp = bp->stream; 22262306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_BUF_FLOW, 22362306a36Sopenharmony_ci "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s", 22462306a36Sopenharmony_ci bp, 22562306a36Sopenharmony_ci pvr2_buffer_state_decode(bp->state), 22662306a36Sopenharmony_ci pvr2_buffer_state_decode(pvr2_buffer_state_queued)); 22762306a36Sopenharmony_ci spin_lock_irqsave(&sp->list_lock, irq_flags); 22862306a36Sopenharmony_ci pvr2_buffer_remove(bp); 22962306a36Sopenharmony_ci list_add_tail(&bp->list_overhead, &sp->queued_list); 23062306a36Sopenharmony_ci bp->state = pvr2_buffer_state_queued; 23162306a36Sopenharmony_ci (sp->q_count)++; 23262306a36Sopenharmony_ci sp->q_bcount += bp->max_count; 23362306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_BUF_FLOW, 23462306a36Sopenharmony_ci "/*---TRACE_FLOW---*/ bufferPool %8s inc cap=%07d cnt=%02d", 23562306a36Sopenharmony_ci pvr2_buffer_state_decode(bp->state), 23662306a36Sopenharmony_ci sp->q_bcount, sp->q_count); 23762306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->list_lock, irq_flags); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic void pvr2_buffer_wipe(struct pvr2_buffer *bp) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci if (bp->state == pvr2_buffer_state_queued) { 24362306a36Sopenharmony_ci usb_kill_urb(bp->purb); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int pvr2_buffer_init(struct pvr2_buffer *bp, 24862306a36Sopenharmony_ci struct pvr2_stream *sp, 24962306a36Sopenharmony_ci unsigned int id) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci memset(bp, 0, sizeof(*bp)); 25262306a36Sopenharmony_ci bp->signature = BUFFER_SIG; 25362306a36Sopenharmony_ci bp->id = id; 25462306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_BUF_POOL, 25562306a36Sopenharmony_ci "/*---TRACE_FLOW---*/ bufferInit %p stream=%p", bp, sp); 25662306a36Sopenharmony_ci bp->stream = sp; 25762306a36Sopenharmony_ci bp->state = pvr2_buffer_state_none; 25862306a36Sopenharmony_ci INIT_LIST_HEAD(&bp->list_overhead); 25962306a36Sopenharmony_ci bp->purb = usb_alloc_urb(0, GFP_KERNEL); 26062306a36Sopenharmony_ci if (! bp->purb) return -ENOMEM; 26162306a36Sopenharmony_ci#ifdef SANITY_CHECK_BUFFERS 26262306a36Sopenharmony_ci pvr2_buffer_describe(bp, "create"); 26362306a36Sopenharmony_ci#endif 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic void pvr2_buffer_done(struct pvr2_buffer *bp) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci#ifdef SANITY_CHECK_BUFFERS 27062306a36Sopenharmony_ci pvr2_buffer_describe(bp, "delete"); 27162306a36Sopenharmony_ci#endif 27262306a36Sopenharmony_ci pvr2_buffer_wipe(bp); 27362306a36Sopenharmony_ci pvr2_buffer_set_none(bp); 27462306a36Sopenharmony_ci bp->signature = 0; 27562306a36Sopenharmony_ci bp->stream = NULL; 27662306a36Sopenharmony_ci usb_free_urb(bp->purb); 27762306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_BUF_POOL, "/*---TRACE_FLOW---*/ bufferDone %p", 27862306a36Sopenharmony_ci bp); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int pvr2_stream_buffer_count(struct pvr2_stream *sp, unsigned int cnt) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci int ret; 28462306a36Sopenharmony_ci unsigned int scnt; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* Allocate buffers pointer array in multiples of 32 entries */ 28762306a36Sopenharmony_ci if (cnt == sp->buffer_total_count) return 0; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_BUF_POOL, 29062306a36Sopenharmony_ci "/*---TRACE_FLOW---*/ poolResize stream=%p cur=%d adj=%+d", 29162306a36Sopenharmony_ci sp, 29262306a36Sopenharmony_ci sp->buffer_total_count, 29362306a36Sopenharmony_ci cnt-sp->buffer_total_count); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci scnt = cnt & ~0x1f; 29662306a36Sopenharmony_ci if (cnt > scnt) scnt += 0x20; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (cnt > sp->buffer_total_count) { 29962306a36Sopenharmony_ci if (scnt > sp->buffer_slot_count) { 30062306a36Sopenharmony_ci struct pvr2_buffer **nb; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci nb = kmalloc_array(scnt, sizeof(*nb), GFP_KERNEL); 30362306a36Sopenharmony_ci if (!nb) return -ENOMEM; 30462306a36Sopenharmony_ci if (sp->buffer_slot_count) { 30562306a36Sopenharmony_ci memcpy(nb, sp->buffers, 30662306a36Sopenharmony_ci sp->buffer_slot_count * sizeof(*nb)); 30762306a36Sopenharmony_ci kfree(sp->buffers); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci sp->buffers = nb; 31062306a36Sopenharmony_ci sp->buffer_slot_count = scnt; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci while (sp->buffer_total_count < cnt) { 31362306a36Sopenharmony_ci struct pvr2_buffer *bp; 31462306a36Sopenharmony_ci bp = kmalloc(sizeof(*bp), GFP_KERNEL); 31562306a36Sopenharmony_ci if (!bp) return -ENOMEM; 31662306a36Sopenharmony_ci ret = pvr2_buffer_init(bp, sp, sp->buffer_total_count); 31762306a36Sopenharmony_ci if (ret) { 31862306a36Sopenharmony_ci kfree(bp); 31962306a36Sopenharmony_ci return -ENOMEM; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci sp->buffers[sp->buffer_total_count] = bp; 32262306a36Sopenharmony_ci (sp->buffer_total_count)++; 32362306a36Sopenharmony_ci pvr2_buffer_set_idle(bp); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci } else { 32662306a36Sopenharmony_ci while (sp->buffer_total_count > cnt) { 32762306a36Sopenharmony_ci struct pvr2_buffer *bp; 32862306a36Sopenharmony_ci bp = sp->buffers[sp->buffer_total_count - 1]; 32962306a36Sopenharmony_ci /* Paranoia */ 33062306a36Sopenharmony_ci sp->buffers[sp->buffer_total_count - 1] = NULL; 33162306a36Sopenharmony_ci (sp->buffer_total_count)--; 33262306a36Sopenharmony_ci pvr2_buffer_done(bp); 33362306a36Sopenharmony_ci kfree(bp); 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci if (scnt < sp->buffer_slot_count) { 33662306a36Sopenharmony_ci struct pvr2_buffer **nb = NULL; 33762306a36Sopenharmony_ci if (scnt) { 33862306a36Sopenharmony_ci nb = kmemdup(sp->buffers, scnt * sizeof(*nb), 33962306a36Sopenharmony_ci GFP_KERNEL); 34062306a36Sopenharmony_ci if (!nb) return -ENOMEM; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci kfree(sp->buffers); 34362306a36Sopenharmony_ci sp->buffers = nb; 34462306a36Sopenharmony_ci sp->buffer_slot_count = scnt; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic int pvr2_stream_achieve_buffer_count(struct pvr2_stream *sp) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct pvr2_buffer *bp; 35362306a36Sopenharmony_ci unsigned int cnt; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (sp->buffer_total_count == sp->buffer_target_count) return 0; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_BUF_POOL, 35862306a36Sopenharmony_ci "/*---TRACE_FLOW---*/ poolCheck stream=%p cur=%d tgt=%d", 35962306a36Sopenharmony_ci sp, sp->buffer_total_count, sp->buffer_target_count); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (sp->buffer_total_count < sp->buffer_target_count) { 36262306a36Sopenharmony_ci return pvr2_stream_buffer_count(sp, sp->buffer_target_count); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci cnt = 0; 36662306a36Sopenharmony_ci while ((sp->buffer_total_count - cnt) > sp->buffer_target_count) { 36762306a36Sopenharmony_ci bp = sp->buffers[sp->buffer_total_count - (cnt + 1)]; 36862306a36Sopenharmony_ci if (bp->state != pvr2_buffer_state_idle) break; 36962306a36Sopenharmony_ci cnt++; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci if (cnt) { 37262306a36Sopenharmony_ci pvr2_stream_buffer_count(sp, sp->buffer_total_count - cnt); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return 0; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic void pvr2_stream_internal_flush(struct pvr2_stream *sp) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct list_head *lp; 38162306a36Sopenharmony_ci struct pvr2_buffer *bp1; 38262306a36Sopenharmony_ci while ((lp = sp->queued_list.next) != &sp->queued_list) { 38362306a36Sopenharmony_ci bp1 = list_entry(lp, struct pvr2_buffer, list_overhead); 38462306a36Sopenharmony_ci pvr2_buffer_wipe(bp1); 38562306a36Sopenharmony_ci /* At this point, we should be guaranteed that no 38662306a36Sopenharmony_ci completion callback may happen on this buffer. But it's 38762306a36Sopenharmony_ci possible that it might have completed after we noticed 38862306a36Sopenharmony_ci it but before we wiped it. So double check its status 38962306a36Sopenharmony_ci here first. */ 39062306a36Sopenharmony_ci if (bp1->state != pvr2_buffer_state_queued) continue; 39162306a36Sopenharmony_ci pvr2_buffer_set_idle(bp1); 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci if (sp->buffer_total_count != sp->buffer_target_count) { 39462306a36Sopenharmony_ci pvr2_stream_achieve_buffer_count(sp); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic void pvr2_stream_init(struct pvr2_stream *sp) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci spin_lock_init(&sp->list_lock); 40162306a36Sopenharmony_ci mutex_init(&sp->mutex); 40262306a36Sopenharmony_ci INIT_LIST_HEAD(&sp->queued_list); 40362306a36Sopenharmony_ci INIT_LIST_HEAD(&sp->ready_list); 40462306a36Sopenharmony_ci INIT_LIST_HEAD(&sp->idle_list); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic void pvr2_stream_done(struct pvr2_stream *sp) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci mutex_lock(&sp->mutex); do { 41062306a36Sopenharmony_ci pvr2_stream_internal_flush(sp); 41162306a36Sopenharmony_ci pvr2_stream_buffer_count(sp, 0); 41262306a36Sopenharmony_ci } while (0); mutex_unlock(&sp->mutex); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void buffer_complete(struct urb *urb) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct pvr2_buffer *bp = urb->context; 41862306a36Sopenharmony_ci struct pvr2_stream *sp; 41962306a36Sopenharmony_ci unsigned long irq_flags; 42062306a36Sopenharmony_ci BUFFER_CHECK(bp); 42162306a36Sopenharmony_ci sp = bp->stream; 42262306a36Sopenharmony_ci bp->used_count = 0; 42362306a36Sopenharmony_ci bp->status = 0; 42462306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_BUF_FLOW, 42562306a36Sopenharmony_ci "/*---TRACE_FLOW---*/ bufferComplete %p stat=%d cnt=%d", 42662306a36Sopenharmony_ci bp, urb->status, urb->actual_length); 42762306a36Sopenharmony_ci spin_lock_irqsave(&sp->list_lock, irq_flags); 42862306a36Sopenharmony_ci if ((!(urb->status)) || 42962306a36Sopenharmony_ci (urb->status == -ENOENT) || 43062306a36Sopenharmony_ci (urb->status == -ECONNRESET) || 43162306a36Sopenharmony_ci (urb->status == -ESHUTDOWN)) { 43262306a36Sopenharmony_ci (sp->buffers_processed)++; 43362306a36Sopenharmony_ci sp->bytes_processed += urb->actual_length; 43462306a36Sopenharmony_ci bp->used_count = urb->actual_length; 43562306a36Sopenharmony_ci if (sp->fail_count) { 43662306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_TOLERANCE, 43762306a36Sopenharmony_ci "stream %p transfer ok - fail count reset", 43862306a36Sopenharmony_ci sp); 43962306a36Sopenharmony_ci sp->fail_count = 0; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci } else if (sp->fail_count < sp->fail_tolerance) { 44262306a36Sopenharmony_ci // We can tolerate this error, because we're below the 44362306a36Sopenharmony_ci // threshold... 44462306a36Sopenharmony_ci (sp->fail_count)++; 44562306a36Sopenharmony_ci (sp->buffers_failed)++; 44662306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_TOLERANCE, 44762306a36Sopenharmony_ci "stream %p ignoring error %d - fail count increased to %u", 44862306a36Sopenharmony_ci sp, urb->status, sp->fail_count); 44962306a36Sopenharmony_ci } else { 45062306a36Sopenharmony_ci (sp->buffers_failed)++; 45162306a36Sopenharmony_ci bp->status = urb->status; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->list_lock, irq_flags); 45462306a36Sopenharmony_ci pvr2_buffer_set_ready(bp); 45562306a36Sopenharmony_ci if (sp->callback_func) { 45662306a36Sopenharmony_ci sp->callback_func(sp->callback_data); 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistruct pvr2_stream *pvr2_stream_create(void) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct pvr2_stream *sp; 46362306a36Sopenharmony_ci sp = kzalloc(sizeof(*sp), GFP_KERNEL); 46462306a36Sopenharmony_ci if (!sp) return sp; 46562306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_INIT, "pvr2_stream_create: sp=%p", sp); 46662306a36Sopenharmony_ci pvr2_stream_init(sp); 46762306a36Sopenharmony_ci return sp; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_civoid pvr2_stream_destroy(struct pvr2_stream *sp) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci if (!sp) return; 47362306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_INIT, "pvr2_stream_destroy: sp=%p", sp); 47462306a36Sopenharmony_ci pvr2_stream_done(sp); 47562306a36Sopenharmony_ci kfree(sp); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_civoid pvr2_stream_setup(struct pvr2_stream *sp, 47962306a36Sopenharmony_ci struct usb_device *dev, 48062306a36Sopenharmony_ci int endpoint, 48162306a36Sopenharmony_ci unsigned int tolerance) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci mutex_lock(&sp->mutex); do { 48462306a36Sopenharmony_ci pvr2_stream_internal_flush(sp); 48562306a36Sopenharmony_ci sp->dev = dev; 48662306a36Sopenharmony_ci sp->endpoint = endpoint; 48762306a36Sopenharmony_ci sp->fail_tolerance = tolerance; 48862306a36Sopenharmony_ci } while (0); mutex_unlock(&sp->mutex); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_civoid pvr2_stream_set_callback(struct pvr2_stream *sp, 49262306a36Sopenharmony_ci pvr2_stream_callback func, 49362306a36Sopenharmony_ci void *data) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci unsigned long irq_flags; 49662306a36Sopenharmony_ci mutex_lock(&sp->mutex); 49762306a36Sopenharmony_ci do { 49862306a36Sopenharmony_ci spin_lock_irqsave(&sp->list_lock, irq_flags); 49962306a36Sopenharmony_ci sp->callback_data = data; 50062306a36Sopenharmony_ci sp->callback_func = func; 50162306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->list_lock, irq_flags); 50262306a36Sopenharmony_ci } while (0); 50362306a36Sopenharmony_ci mutex_unlock(&sp->mutex); 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_civoid pvr2_stream_get_stats(struct pvr2_stream *sp, 50762306a36Sopenharmony_ci struct pvr2_stream_stats *stats, 50862306a36Sopenharmony_ci int zero_counts) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci unsigned long irq_flags; 51162306a36Sopenharmony_ci spin_lock_irqsave(&sp->list_lock, irq_flags); 51262306a36Sopenharmony_ci if (stats) { 51362306a36Sopenharmony_ci stats->buffers_in_queue = sp->q_count; 51462306a36Sopenharmony_ci stats->buffers_in_idle = sp->i_count; 51562306a36Sopenharmony_ci stats->buffers_in_ready = sp->r_count; 51662306a36Sopenharmony_ci stats->buffers_processed = sp->buffers_processed; 51762306a36Sopenharmony_ci stats->buffers_failed = sp->buffers_failed; 51862306a36Sopenharmony_ci stats->bytes_processed = sp->bytes_processed; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci if (zero_counts) { 52162306a36Sopenharmony_ci sp->buffers_processed = 0; 52262306a36Sopenharmony_ci sp->buffers_failed = 0; 52362306a36Sopenharmony_ci sp->bytes_processed = 0; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->list_lock, irq_flags); 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci/* Query / set the nominal buffer count */ 52962306a36Sopenharmony_ciint pvr2_stream_get_buffer_count(struct pvr2_stream *sp) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci return sp->buffer_target_count; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ciint pvr2_stream_set_buffer_count(struct pvr2_stream *sp, unsigned int cnt) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci int ret; 53762306a36Sopenharmony_ci if (sp->buffer_target_count == cnt) return 0; 53862306a36Sopenharmony_ci mutex_lock(&sp->mutex); 53962306a36Sopenharmony_ci do { 54062306a36Sopenharmony_ci sp->buffer_target_count = cnt; 54162306a36Sopenharmony_ci ret = pvr2_stream_achieve_buffer_count(sp); 54262306a36Sopenharmony_ci } while (0); 54362306a36Sopenharmony_ci mutex_unlock(&sp->mutex); 54462306a36Sopenharmony_ci return ret; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistruct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *sp) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct list_head *lp = sp->idle_list.next; 55062306a36Sopenharmony_ci if (lp == &sp->idle_list) return NULL; 55162306a36Sopenharmony_ci return list_entry(lp, struct pvr2_buffer, list_overhead); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistruct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *sp) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct list_head *lp = sp->ready_list.next; 55762306a36Sopenharmony_ci if (lp == &sp->ready_list) return NULL; 55862306a36Sopenharmony_ci return list_entry(lp, struct pvr2_buffer, list_overhead); 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistruct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp, int id) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci if (id < 0) return NULL; 56462306a36Sopenharmony_ci if (id >= sp->buffer_total_count) return NULL; 56562306a36Sopenharmony_ci return sp->buffers[id]; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ciint pvr2_stream_get_ready_count(struct pvr2_stream *sp) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci return sp->r_count; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_civoid pvr2_stream_kill(struct pvr2_stream *sp) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct pvr2_buffer *bp; 57662306a36Sopenharmony_ci mutex_lock(&sp->mutex); 57762306a36Sopenharmony_ci do { 57862306a36Sopenharmony_ci pvr2_stream_internal_flush(sp); 57962306a36Sopenharmony_ci while ((bp = pvr2_stream_get_ready_buffer(sp)) != NULL) { 58062306a36Sopenharmony_ci pvr2_buffer_set_idle(bp); 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci if (sp->buffer_total_count != sp->buffer_target_count) { 58362306a36Sopenharmony_ci pvr2_stream_achieve_buffer_count(sp); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci } while (0); 58662306a36Sopenharmony_ci mutex_unlock(&sp->mutex); 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ciint pvr2_buffer_queue(struct pvr2_buffer *bp) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci#undef SEED_BUFFER 59262306a36Sopenharmony_ci#ifdef SEED_BUFFER 59362306a36Sopenharmony_ci unsigned int idx; 59462306a36Sopenharmony_ci unsigned int val; 59562306a36Sopenharmony_ci#endif 59662306a36Sopenharmony_ci int ret = 0; 59762306a36Sopenharmony_ci struct pvr2_stream *sp; 59862306a36Sopenharmony_ci if (!bp) return -EINVAL; 59962306a36Sopenharmony_ci sp = bp->stream; 60062306a36Sopenharmony_ci mutex_lock(&sp->mutex); 60162306a36Sopenharmony_ci do { 60262306a36Sopenharmony_ci pvr2_buffer_wipe(bp); 60362306a36Sopenharmony_ci if (!sp->dev) { 60462306a36Sopenharmony_ci ret = -EIO; 60562306a36Sopenharmony_ci break; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci pvr2_buffer_set_queued(bp); 60862306a36Sopenharmony_ci#ifdef SEED_BUFFER 60962306a36Sopenharmony_ci for (idx = 0; idx < (bp->max_count) / 4; idx++) { 61062306a36Sopenharmony_ci val = bp->id << 24; 61162306a36Sopenharmony_ci val |= idx; 61262306a36Sopenharmony_ci ((unsigned int *)(bp->ptr))[idx] = val; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci#endif 61562306a36Sopenharmony_ci bp->status = -EINPROGRESS; 61662306a36Sopenharmony_ci usb_fill_bulk_urb(bp->purb, // struct urb *urb 61762306a36Sopenharmony_ci sp->dev, // struct usb_device *dev 61862306a36Sopenharmony_ci // endpoint (below) 61962306a36Sopenharmony_ci usb_rcvbulkpipe(sp->dev, sp->endpoint), 62062306a36Sopenharmony_ci bp->ptr, // void *transfer_buffer 62162306a36Sopenharmony_ci bp->max_count, // int buffer_length 62262306a36Sopenharmony_ci buffer_complete, 62362306a36Sopenharmony_ci bp); 62462306a36Sopenharmony_ci usb_submit_urb(bp->purb, GFP_KERNEL); 62562306a36Sopenharmony_ci } while (0); 62662306a36Sopenharmony_ci mutex_unlock(&sp->mutex); 62762306a36Sopenharmony_ci return ret; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ciint pvr2_buffer_set_buffer(struct pvr2_buffer *bp, void *ptr, unsigned int cnt) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci int ret = 0; 63362306a36Sopenharmony_ci unsigned long irq_flags; 63462306a36Sopenharmony_ci struct pvr2_stream *sp; 63562306a36Sopenharmony_ci if (!bp) return -EINVAL; 63662306a36Sopenharmony_ci sp = bp->stream; 63762306a36Sopenharmony_ci mutex_lock(&sp->mutex); 63862306a36Sopenharmony_ci do { 63962306a36Sopenharmony_ci spin_lock_irqsave(&sp->list_lock, irq_flags); 64062306a36Sopenharmony_ci if (bp->state != pvr2_buffer_state_idle) { 64162306a36Sopenharmony_ci ret = -EPERM; 64262306a36Sopenharmony_ci } else { 64362306a36Sopenharmony_ci bp->ptr = ptr; 64462306a36Sopenharmony_ci bp->stream->i_bcount -= bp->max_count; 64562306a36Sopenharmony_ci bp->max_count = cnt; 64662306a36Sopenharmony_ci bp->stream->i_bcount += bp->max_count; 64762306a36Sopenharmony_ci pvr2_trace(PVR2_TRACE_BUF_FLOW, 64862306a36Sopenharmony_ci "/*---TRACE_FLOW---*/ bufferPool %8s cap cap=%07d cnt=%02d", 64962306a36Sopenharmony_ci pvr2_buffer_state_decode( 65062306a36Sopenharmony_ci pvr2_buffer_state_idle), 65162306a36Sopenharmony_ci bp->stream->i_bcount, bp->stream->i_count); 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->list_lock, irq_flags); 65462306a36Sopenharmony_ci } while (0); 65562306a36Sopenharmony_ci mutex_unlock(&sp->mutex); 65662306a36Sopenharmony_ci return ret; 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ciunsigned int pvr2_buffer_get_count(struct pvr2_buffer *bp) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci return bp->used_count; 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ciint pvr2_buffer_get_status(struct pvr2_buffer *bp) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci return bp->status; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ciint pvr2_buffer_get_id(struct pvr2_buffer *bp) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci return bp->id; 67262306a36Sopenharmony_ci} 673