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