18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "pvrusb2-io.h"
88c2ecf20Sopenharmony_ci#include "pvrusb2-debug.h"
98c2ecf20Sopenharmony_ci#include <linux/errno.h>
108c2ecf20Sopenharmony_ci#include <linux/string.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/mutex.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic const char *pvr2_buffer_state_decode(enum pvr2_buffer_state);
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define BUFFER_SIG 0x47653271
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci// #define SANITY_CHECK_BUFFERS
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#ifdef SANITY_CHECK_BUFFERS
228c2ecf20Sopenharmony_ci#define BUFFER_CHECK(bp) do { \
238c2ecf20Sopenharmony_ci	if ((bp)->signature != BUFFER_SIG) { \
248c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS, \
258c2ecf20Sopenharmony_ci		"Buffer %p is bad at %s:%d", \
268c2ecf20Sopenharmony_ci		(bp), __FILE__, __LINE__); \
278c2ecf20Sopenharmony_ci		pvr2_buffer_describe(bp, "BadSig"); \
288c2ecf20Sopenharmony_ci		BUG(); \
298c2ecf20Sopenharmony_ci	} \
308c2ecf20Sopenharmony_ci} while (0)
318c2ecf20Sopenharmony_ci#else
328c2ecf20Sopenharmony_ci#define BUFFER_CHECK(bp) do {} while (0)
338c2ecf20Sopenharmony_ci#endif
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct pvr2_stream {
368c2ecf20Sopenharmony_ci	/* Buffers queued for reading */
378c2ecf20Sopenharmony_ci	struct list_head queued_list;
388c2ecf20Sopenharmony_ci	unsigned int q_count;
398c2ecf20Sopenharmony_ci	unsigned int q_bcount;
408c2ecf20Sopenharmony_ci	/* Buffers with retrieved data */
418c2ecf20Sopenharmony_ci	struct list_head ready_list;
428c2ecf20Sopenharmony_ci	unsigned int r_count;
438c2ecf20Sopenharmony_ci	unsigned int r_bcount;
448c2ecf20Sopenharmony_ci	/* Buffers available for use */
458c2ecf20Sopenharmony_ci	struct list_head idle_list;
468c2ecf20Sopenharmony_ci	unsigned int i_count;
478c2ecf20Sopenharmony_ci	unsigned int i_bcount;
488c2ecf20Sopenharmony_ci	/* Pointers to all buffers */
498c2ecf20Sopenharmony_ci	struct pvr2_buffer **buffers;
508c2ecf20Sopenharmony_ci	/* Array size of buffers */
518c2ecf20Sopenharmony_ci	unsigned int buffer_slot_count;
528c2ecf20Sopenharmony_ci	/* Total buffers actually in circulation */
538c2ecf20Sopenharmony_ci	unsigned int buffer_total_count;
548c2ecf20Sopenharmony_ci	/* Designed number of buffers to be in circulation */
558c2ecf20Sopenharmony_ci	unsigned int buffer_target_count;
568c2ecf20Sopenharmony_ci	/* Executed when ready list become non-empty */
578c2ecf20Sopenharmony_ci	pvr2_stream_callback callback_func;
588c2ecf20Sopenharmony_ci	void *callback_data;
598c2ecf20Sopenharmony_ci	/* Context for transfer endpoint */
608c2ecf20Sopenharmony_ci	struct usb_device *dev;
618c2ecf20Sopenharmony_ci	int endpoint;
628c2ecf20Sopenharmony_ci	/* Overhead for mutex enforcement */
638c2ecf20Sopenharmony_ci	spinlock_t list_lock;
648c2ecf20Sopenharmony_ci	struct mutex mutex;
658c2ecf20Sopenharmony_ci	/* Tracking state for tolerating errors */
668c2ecf20Sopenharmony_ci	unsigned int fail_count;
678c2ecf20Sopenharmony_ci	unsigned int fail_tolerance;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	unsigned int buffers_processed;
708c2ecf20Sopenharmony_ci	unsigned int buffers_failed;
718c2ecf20Sopenharmony_ci	unsigned int bytes_processed;
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistruct pvr2_buffer {
758c2ecf20Sopenharmony_ci	int id;
768c2ecf20Sopenharmony_ci	int signature;
778c2ecf20Sopenharmony_ci	enum pvr2_buffer_state state;
788c2ecf20Sopenharmony_ci	void *ptr;               /* Pointer to storage area */
798c2ecf20Sopenharmony_ci	unsigned int max_count;  /* Size of storage area */
808c2ecf20Sopenharmony_ci	unsigned int used_count; /* Amount of valid data in storage area */
818c2ecf20Sopenharmony_ci	int status;              /* Transfer result status */
828c2ecf20Sopenharmony_ci	struct pvr2_stream *stream;
838c2ecf20Sopenharmony_ci	struct list_head list_overhead;
848c2ecf20Sopenharmony_ci	struct urb *purb;
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic const char *pvr2_buffer_state_decode(enum pvr2_buffer_state st)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	switch (st) {
908c2ecf20Sopenharmony_ci	case pvr2_buffer_state_none: return "none";
918c2ecf20Sopenharmony_ci	case pvr2_buffer_state_idle: return "idle";
928c2ecf20Sopenharmony_ci	case pvr2_buffer_state_queued: return "queued";
938c2ecf20Sopenharmony_ci	case pvr2_buffer_state_ready: return "ready";
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci	return "unknown";
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#ifdef SANITY_CHECK_BUFFERS
998c2ecf20Sopenharmony_cistatic void pvr2_buffer_describe(struct pvr2_buffer *bp, const char *msg)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INFO,
1028c2ecf20Sopenharmony_ci		   "buffer%s%s %p state=%s id=%d status=%d stream=%p purb=%p sig=0x%x",
1038c2ecf20Sopenharmony_ci		   (msg ? " " : ""),
1048c2ecf20Sopenharmony_ci		   (msg ? msg : ""),
1058c2ecf20Sopenharmony_ci		   bp,
1068c2ecf20Sopenharmony_ci		   (bp ? pvr2_buffer_state_decode(bp->state) : "(invalid)"),
1078c2ecf20Sopenharmony_ci		   (bp ? bp->id : 0),
1088c2ecf20Sopenharmony_ci		   (bp ? bp->status : 0),
1098c2ecf20Sopenharmony_ci		   (bp ? bp->stream : NULL),
1108c2ecf20Sopenharmony_ci		   (bp ? bp->purb : NULL),
1118c2ecf20Sopenharmony_ci		   (bp ? bp->signature : 0));
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci#endif  /*  SANITY_CHECK_BUFFERS  */
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic void pvr2_buffer_remove(struct pvr2_buffer *bp)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	unsigned int *cnt;
1188c2ecf20Sopenharmony_ci	unsigned int *bcnt;
1198c2ecf20Sopenharmony_ci	unsigned int ccnt;
1208c2ecf20Sopenharmony_ci	struct pvr2_stream *sp = bp->stream;
1218c2ecf20Sopenharmony_ci	switch (bp->state) {
1228c2ecf20Sopenharmony_ci	case pvr2_buffer_state_idle:
1238c2ecf20Sopenharmony_ci		cnt = &sp->i_count;
1248c2ecf20Sopenharmony_ci		bcnt = &sp->i_bcount;
1258c2ecf20Sopenharmony_ci		ccnt = bp->max_count;
1268c2ecf20Sopenharmony_ci		break;
1278c2ecf20Sopenharmony_ci	case pvr2_buffer_state_queued:
1288c2ecf20Sopenharmony_ci		cnt = &sp->q_count;
1298c2ecf20Sopenharmony_ci		bcnt = &sp->q_bcount;
1308c2ecf20Sopenharmony_ci		ccnt = bp->max_count;
1318c2ecf20Sopenharmony_ci		break;
1328c2ecf20Sopenharmony_ci	case pvr2_buffer_state_ready:
1338c2ecf20Sopenharmony_ci		cnt = &sp->r_count;
1348c2ecf20Sopenharmony_ci		bcnt = &sp->r_bcount;
1358c2ecf20Sopenharmony_ci		ccnt = bp->used_count;
1368c2ecf20Sopenharmony_ci		break;
1378c2ecf20Sopenharmony_ci	default:
1388c2ecf20Sopenharmony_ci		return;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci	list_del_init(&bp->list_overhead);
1418c2ecf20Sopenharmony_ci	(*cnt)--;
1428c2ecf20Sopenharmony_ci	(*bcnt) -= ccnt;
1438c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_BUF_FLOW,
1448c2ecf20Sopenharmony_ci		   "/*---TRACE_FLOW---*/ bufferPool	%8s dec cap=%07d cnt=%02d",
1458c2ecf20Sopenharmony_ci		   pvr2_buffer_state_decode(bp->state), *bcnt, *cnt);
1468c2ecf20Sopenharmony_ci	bp->state = pvr2_buffer_state_none;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic void pvr2_buffer_set_none(struct pvr2_buffer *bp)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	unsigned long irq_flags;
1528c2ecf20Sopenharmony_ci	struct pvr2_stream *sp;
1538c2ecf20Sopenharmony_ci	BUFFER_CHECK(bp);
1548c2ecf20Sopenharmony_ci	sp = bp->stream;
1558c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_BUF_FLOW,
1568c2ecf20Sopenharmony_ci		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s",
1578c2ecf20Sopenharmony_ci		   bp,
1588c2ecf20Sopenharmony_ci		   pvr2_buffer_state_decode(bp->state),
1598c2ecf20Sopenharmony_ci		   pvr2_buffer_state_decode(pvr2_buffer_state_none));
1608c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sp->list_lock, irq_flags);
1618c2ecf20Sopenharmony_ci	pvr2_buffer_remove(bp);
1628c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sp->list_lock, irq_flags);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic int pvr2_buffer_set_ready(struct pvr2_buffer *bp)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	int fl;
1688c2ecf20Sopenharmony_ci	unsigned long irq_flags;
1698c2ecf20Sopenharmony_ci	struct pvr2_stream *sp;
1708c2ecf20Sopenharmony_ci	BUFFER_CHECK(bp);
1718c2ecf20Sopenharmony_ci	sp = bp->stream;
1728c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_BUF_FLOW,
1738c2ecf20Sopenharmony_ci		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s",
1748c2ecf20Sopenharmony_ci		   bp,
1758c2ecf20Sopenharmony_ci		   pvr2_buffer_state_decode(bp->state),
1768c2ecf20Sopenharmony_ci		   pvr2_buffer_state_decode(pvr2_buffer_state_ready));
1778c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sp->list_lock, irq_flags);
1788c2ecf20Sopenharmony_ci	fl = (sp->r_count == 0);
1798c2ecf20Sopenharmony_ci	pvr2_buffer_remove(bp);
1808c2ecf20Sopenharmony_ci	list_add_tail(&bp->list_overhead, &sp->ready_list);
1818c2ecf20Sopenharmony_ci	bp->state = pvr2_buffer_state_ready;
1828c2ecf20Sopenharmony_ci	(sp->r_count)++;
1838c2ecf20Sopenharmony_ci	sp->r_bcount += bp->used_count;
1848c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_BUF_FLOW,
1858c2ecf20Sopenharmony_ci		   "/*---TRACE_FLOW---*/ bufferPool	%8s inc cap=%07d cnt=%02d",
1868c2ecf20Sopenharmony_ci		   pvr2_buffer_state_decode(bp->state),
1878c2ecf20Sopenharmony_ci		   sp->r_bcount, sp->r_count);
1888c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sp->list_lock, irq_flags);
1898c2ecf20Sopenharmony_ci	return fl;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic void pvr2_buffer_set_idle(struct pvr2_buffer *bp)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	unsigned long irq_flags;
1958c2ecf20Sopenharmony_ci	struct pvr2_stream *sp;
1968c2ecf20Sopenharmony_ci	BUFFER_CHECK(bp);
1978c2ecf20Sopenharmony_ci	sp = bp->stream;
1988c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_BUF_FLOW,
1998c2ecf20Sopenharmony_ci		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s",
2008c2ecf20Sopenharmony_ci		   bp,
2018c2ecf20Sopenharmony_ci		   pvr2_buffer_state_decode(bp->state),
2028c2ecf20Sopenharmony_ci		   pvr2_buffer_state_decode(pvr2_buffer_state_idle));
2038c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sp->list_lock, irq_flags);
2048c2ecf20Sopenharmony_ci	pvr2_buffer_remove(bp);
2058c2ecf20Sopenharmony_ci	list_add_tail(&bp->list_overhead, &sp->idle_list);
2068c2ecf20Sopenharmony_ci	bp->state = pvr2_buffer_state_idle;
2078c2ecf20Sopenharmony_ci	(sp->i_count)++;
2088c2ecf20Sopenharmony_ci	sp->i_bcount += bp->max_count;
2098c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_BUF_FLOW,
2108c2ecf20Sopenharmony_ci		   "/*---TRACE_FLOW---*/ bufferPool	%8s inc cap=%07d cnt=%02d",
2118c2ecf20Sopenharmony_ci		   pvr2_buffer_state_decode(bp->state),
2128c2ecf20Sopenharmony_ci		   sp->i_bcount, sp->i_count);
2138c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sp->list_lock, irq_flags);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic void pvr2_buffer_set_queued(struct pvr2_buffer *bp)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	unsigned long irq_flags;
2198c2ecf20Sopenharmony_ci	struct pvr2_stream *sp;
2208c2ecf20Sopenharmony_ci	BUFFER_CHECK(bp);
2218c2ecf20Sopenharmony_ci	sp = bp->stream;
2228c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_BUF_FLOW,
2238c2ecf20Sopenharmony_ci		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s",
2248c2ecf20Sopenharmony_ci		   bp,
2258c2ecf20Sopenharmony_ci		   pvr2_buffer_state_decode(bp->state),
2268c2ecf20Sopenharmony_ci		   pvr2_buffer_state_decode(pvr2_buffer_state_queued));
2278c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sp->list_lock, irq_flags);
2288c2ecf20Sopenharmony_ci	pvr2_buffer_remove(bp);
2298c2ecf20Sopenharmony_ci	list_add_tail(&bp->list_overhead, &sp->queued_list);
2308c2ecf20Sopenharmony_ci	bp->state = pvr2_buffer_state_queued;
2318c2ecf20Sopenharmony_ci	(sp->q_count)++;
2328c2ecf20Sopenharmony_ci	sp->q_bcount += bp->max_count;
2338c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_BUF_FLOW,
2348c2ecf20Sopenharmony_ci		   "/*---TRACE_FLOW---*/ bufferPool	%8s inc cap=%07d cnt=%02d",
2358c2ecf20Sopenharmony_ci		   pvr2_buffer_state_decode(bp->state),
2368c2ecf20Sopenharmony_ci		   sp->q_bcount, sp->q_count);
2378c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sp->list_lock, irq_flags);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic void pvr2_buffer_wipe(struct pvr2_buffer *bp)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	if (bp->state == pvr2_buffer_state_queued) {
2438c2ecf20Sopenharmony_ci		usb_kill_urb(bp->purb);
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic int pvr2_buffer_init(struct pvr2_buffer *bp,
2488c2ecf20Sopenharmony_ci			    struct pvr2_stream *sp,
2498c2ecf20Sopenharmony_ci			    unsigned int id)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	memset(bp, 0, sizeof(*bp));
2528c2ecf20Sopenharmony_ci	bp->signature = BUFFER_SIG;
2538c2ecf20Sopenharmony_ci	bp->id = id;
2548c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_BUF_POOL,
2558c2ecf20Sopenharmony_ci		   "/*---TRACE_FLOW---*/ bufferInit     %p stream=%p", bp, sp);
2568c2ecf20Sopenharmony_ci	bp->stream = sp;
2578c2ecf20Sopenharmony_ci	bp->state = pvr2_buffer_state_none;
2588c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&bp->list_overhead);
2598c2ecf20Sopenharmony_ci	bp->purb = usb_alloc_urb(0, GFP_KERNEL);
2608c2ecf20Sopenharmony_ci	if (! bp->purb) return -ENOMEM;
2618c2ecf20Sopenharmony_ci#ifdef SANITY_CHECK_BUFFERS
2628c2ecf20Sopenharmony_ci	pvr2_buffer_describe(bp, "create");
2638c2ecf20Sopenharmony_ci#endif
2648c2ecf20Sopenharmony_ci	return 0;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic void pvr2_buffer_done(struct pvr2_buffer *bp)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci#ifdef SANITY_CHECK_BUFFERS
2708c2ecf20Sopenharmony_ci	pvr2_buffer_describe(bp, "delete");
2718c2ecf20Sopenharmony_ci#endif
2728c2ecf20Sopenharmony_ci	pvr2_buffer_wipe(bp);
2738c2ecf20Sopenharmony_ci	pvr2_buffer_set_none(bp);
2748c2ecf20Sopenharmony_ci	bp->signature = 0;
2758c2ecf20Sopenharmony_ci	bp->stream = NULL;
2768c2ecf20Sopenharmony_ci	usb_free_urb(bp->purb);
2778c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_BUF_POOL, "/*---TRACE_FLOW---*/ bufferDone     %p",
2788c2ecf20Sopenharmony_ci		   bp);
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic int pvr2_stream_buffer_count(struct pvr2_stream *sp, unsigned int cnt)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	int ret;
2848c2ecf20Sopenharmony_ci	unsigned int scnt;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	/* Allocate buffers pointer array in multiples of 32 entries */
2878c2ecf20Sopenharmony_ci	if (cnt == sp->buffer_total_count) return 0;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_BUF_POOL,
2908c2ecf20Sopenharmony_ci		   "/*---TRACE_FLOW---*/ poolResize	stream=%p cur=%d adj=%+d",
2918c2ecf20Sopenharmony_ci		   sp,
2928c2ecf20Sopenharmony_ci		   sp->buffer_total_count,
2938c2ecf20Sopenharmony_ci		   cnt-sp->buffer_total_count);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	scnt = cnt & ~0x1f;
2968c2ecf20Sopenharmony_ci	if (cnt > scnt) scnt += 0x20;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (cnt > sp->buffer_total_count) {
2998c2ecf20Sopenharmony_ci		if (scnt > sp->buffer_slot_count) {
3008c2ecf20Sopenharmony_ci			struct pvr2_buffer **nb;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci			nb = kmalloc_array(scnt, sizeof(*nb), GFP_KERNEL);
3038c2ecf20Sopenharmony_ci			if (!nb) return -ENOMEM;
3048c2ecf20Sopenharmony_ci			if (sp->buffer_slot_count) {
3058c2ecf20Sopenharmony_ci				memcpy(nb, sp->buffers,
3068c2ecf20Sopenharmony_ci				       sp->buffer_slot_count * sizeof(*nb));
3078c2ecf20Sopenharmony_ci				kfree(sp->buffers);
3088c2ecf20Sopenharmony_ci			}
3098c2ecf20Sopenharmony_ci			sp->buffers = nb;
3108c2ecf20Sopenharmony_ci			sp->buffer_slot_count = scnt;
3118c2ecf20Sopenharmony_ci		}
3128c2ecf20Sopenharmony_ci		while (sp->buffer_total_count < cnt) {
3138c2ecf20Sopenharmony_ci			struct pvr2_buffer *bp;
3148c2ecf20Sopenharmony_ci			bp = kmalloc(sizeof(*bp), GFP_KERNEL);
3158c2ecf20Sopenharmony_ci			if (!bp) return -ENOMEM;
3168c2ecf20Sopenharmony_ci			ret = pvr2_buffer_init(bp, sp, sp->buffer_total_count);
3178c2ecf20Sopenharmony_ci			if (ret) {
3188c2ecf20Sopenharmony_ci				kfree(bp);
3198c2ecf20Sopenharmony_ci				return -ENOMEM;
3208c2ecf20Sopenharmony_ci			}
3218c2ecf20Sopenharmony_ci			sp->buffers[sp->buffer_total_count] = bp;
3228c2ecf20Sopenharmony_ci			(sp->buffer_total_count)++;
3238c2ecf20Sopenharmony_ci			pvr2_buffer_set_idle(bp);
3248c2ecf20Sopenharmony_ci		}
3258c2ecf20Sopenharmony_ci	} else {
3268c2ecf20Sopenharmony_ci		while (sp->buffer_total_count > cnt) {
3278c2ecf20Sopenharmony_ci			struct pvr2_buffer *bp;
3288c2ecf20Sopenharmony_ci			bp = sp->buffers[sp->buffer_total_count - 1];
3298c2ecf20Sopenharmony_ci			/* Paranoia */
3308c2ecf20Sopenharmony_ci			sp->buffers[sp->buffer_total_count - 1] = NULL;
3318c2ecf20Sopenharmony_ci			(sp->buffer_total_count)--;
3328c2ecf20Sopenharmony_ci			pvr2_buffer_done(bp);
3338c2ecf20Sopenharmony_ci			kfree(bp);
3348c2ecf20Sopenharmony_ci		}
3358c2ecf20Sopenharmony_ci		if (scnt < sp->buffer_slot_count) {
3368c2ecf20Sopenharmony_ci			struct pvr2_buffer **nb = NULL;
3378c2ecf20Sopenharmony_ci			if (scnt) {
3388c2ecf20Sopenharmony_ci				nb = kmemdup(sp->buffers, scnt * sizeof(*nb),
3398c2ecf20Sopenharmony_ci					     GFP_KERNEL);
3408c2ecf20Sopenharmony_ci				if (!nb) return -ENOMEM;
3418c2ecf20Sopenharmony_ci			}
3428c2ecf20Sopenharmony_ci			kfree(sp->buffers);
3438c2ecf20Sopenharmony_ci			sp->buffers = nb;
3448c2ecf20Sopenharmony_ci			sp->buffer_slot_count = scnt;
3458c2ecf20Sopenharmony_ci		}
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci	return 0;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic int pvr2_stream_achieve_buffer_count(struct pvr2_stream *sp)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct pvr2_buffer *bp;
3538c2ecf20Sopenharmony_ci	unsigned int cnt;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (sp->buffer_total_count == sp->buffer_target_count) return 0;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_BUF_POOL,
3588c2ecf20Sopenharmony_ci		   "/*---TRACE_FLOW---*/ poolCheck	stream=%p cur=%d tgt=%d",
3598c2ecf20Sopenharmony_ci		   sp, sp->buffer_total_count, sp->buffer_target_count);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (sp->buffer_total_count < sp->buffer_target_count) {
3628c2ecf20Sopenharmony_ci		return pvr2_stream_buffer_count(sp, sp->buffer_target_count);
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	cnt = 0;
3668c2ecf20Sopenharmony_ci	while ((sp->buffer_total_count - cnt) > sp->buffer_target_count) {
3678c2ecf20Sopenharmony_ci		bp = sp->buffers[sp->buffer_total_count - (cnt + 1)];
3688c2ecf20Sopenharmony_ci		if (bp->state != pvr2_buffer_state_idle) break;
3698c2ecf20Sopenharmony_ci		cnt++;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci	if (cnt) {
3728c2ecf20Sopenharmony_ci		pvr2_stream_buffer_count(sp, sp->buffer_total_count - cnt);
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	return 0;
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic void pvr2_stream_internal_flush(struct pvr2_stream *sp)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct list_head *lp;
3818c2ecf20Sopenharmony_ci	struct pvr2_buffer *bp1;
3828c2ecf20Sopenharmony_ci	while ((lp = sp->queued_list.next) != &sp->queued_list) {
3838c2ecf20Sopenharmony_ci		bp1 = list_entry(lp, struct pvr2_buffer, list_overhead);
3848c2ecf20Sopenharmony_ci		pvr2_buffer_wipe(bp1);
3858c2ecf20Sopenharmony_ci		/* At this point, we should be guaranteed that no
3868c2ecf20Sopenharmony_ci		   completion callback may happen on this buffer.  But it's
3878c2ecf20Sopenharmony_ci		   possible that it might have completed after we noticed
3888c2ecf20Sopenharmony_ci		   it but before we wiped it.  So double check its status
3898c2ecf20Sopenharmony_ci		   here first. */
3908c2ecf20Sopenharmony_ci		if (bp1->state != pvr2_buffer_state_queued) continue;
3918c2ecf20Sopenharmony_ci		pvr2_buffer_set_idle(bp1);
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci	if (sp->buffer_total_count != sp->buffer_target_count) {
3948c2ecf20Sopenharmony_ci		pvr2_stream_achieve_buffer_count(sp);
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic void pvr2_stream_init(struct pvr2_stream *sp)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	spin_lock_init(&sp->list_lock);
4018c2ecf20Sopenharmony_ci	mutex_init(&sp->mutex);
4028c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&sp->queued_list);
4038c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&sp->ready_list);
4048c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&sp->idle_list);
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic void pvr2_stream_done(struct pvr2_stream *sp)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	mutex_lock(&sp->mutex); do {
4108c2ecf20Sopenharmony_ci		pvr2_stream_internal_flush(sp);
4118c2ecf20Sopenharmony_ci		pvr2_stream_buffer_count(sp, 0);
4128c2ecf20Sopenharmony_ci	} while (0); mutex_unlock(&sp->mutex);
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic void buffer_complete(struct urb *urb)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct pvr2_buffer *bp = urb->context;
4188c2ecf20Sopenharmony_ci	struct pvr2_stream *sp;
4198c2ecf20Sopenharmony_ci	unsigned long irq_flags;
4208c2ecf20Sopenharmony_ci	BUFFER_CHECK(bp);
4218c2ecf20Sopenharmony_ci	sp = bp->stream;
4228c2ecf20Sopenharmony_ci	bp->used_count = 0;
4238c2ecf20Sopenharmony_ci	bp->status = 0;
4248c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_BUF_FLOW,
4258c2ecf20Sopenharmony_ci		   "/*---TRACE_FLOW---*/ bufferComplete %p stat=%d cnt=%d",
4268c2ecf20Sopenharmony_ci		   bp, urb->status, urb->actual_length);
4278c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sp->list_lock, irq_flags);
4288c2ecf20Sopenharmony_ci	if ((!(urb->status)) ||
4298c2ecf20Sopenharmony_ci	    (urb->status == -ENOENT) ||
4308c2ecf20Sopenharmony_ci	    (urb->status == -ECONNRESET) ||
4318c2ecf20Sopenharmony_ci	    (urb->status == -ESHUTDOWN)) {
4328c2ecf20Sopenharmony_ci		(sp->buffers_processed)++;
4338c2ecf20Sopenharmony_ci		sp->bytes_processed += urb->actual_length;
4348c2ecf20Sopenharmony_ci		bp->used_count = urb->actual_length;
4358c2ecf20Sopenharmony_ci		if (sp->fail_count) {
4368c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_TOLERANCE,
4378c2ecf20Sopenharmony_ci				   "stream %p transfer ok - fail count reset",
4388c2ecf20Sopenharmony_ci				   sp);
4398c2ecf20Sopenharmony_ci			sp->fail_count = 0;
4408c2ecf20Sopenharmony_ci		}
4418c2ecf20Sopenharmony_ci	} else if (sp->fail_count < sp->fail_tolerance) {
4428c2ecf20Sopenharmony_ci		// We can tolerate this error, because we're below the
4438c2ecf20Sopenharmony_ci		// threshold...
4448c2ecf20Sopenharmony_ci		(sp->fail_count)++;
4458c2ecf20Sopenharmony_ci		(sp->buffers_failed)++;
4468c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_TOLERANCE,
4478c2ecf20Sopenharmony_ci			   "stream %p ignoring error %d - fail count increased to %u",
4488c2ecf20Sopenharmony_ci			   sp, urb->status, sp->fail_count);
4498c2ecf20Sopenharmony_ci	} else {
4508c2ecf20Sopenharmony_ci		(sp->buffers_failed)++;
4518c2ecf20Sopenharmony_ci		bp->status = urb->status;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sp->list_lock, irq_flags);
4548c2ecf20Sopenharmony_ci	pvr2_buffer_set_ready(bp);
4558c2ecf20Sopenharmony_ci	if (sp->callback_func) {
4568c2ecf20Sopenharmony_ci		sp->callback_func(sp->callback_data);
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistruct pvr2_stream *pvr2_stream_create(void)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	struct pvr2_stream *sp;
4638c2ecf20Sopenharmony_ci	sp = kzalloc(sizeof(*sp), GFP_KERNEL);
4648c2ecf20Sopenharmony_ci	if (!sp) return sp;
4658c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT, "pvr2_stream_create: sp=%p", sp);
4668c2ecf20Sopenharmony_ci	pvr2_stream_init(sp);
4678c2ecf20Sopenharmony_ci	return sp;
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_civoid pvr2_stream_destroy(struct pvr2_stream *sp)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	if (!sp) return;
4738c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT, "pvr2_stream_destroy: sp=%p", sp);
4748c2ecf20Sopenharmony_ci	pvr2_stream_done(sp);
4758c2ecf20Sopenharmony_ci	kfree(sp);
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_civoid pvr2_stream_setup(struct pvr2_stream *sp,
4798c2ecf20Sopenharmony_ci		       struct usb_device *dev,
4808c2ecf20Sopenharmony_ci		       int endpoint,
4818c2ecf20Sopenharmony_ci		       unsigned int tolerance)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	mutex_lock(&sp->mutex); do {
4848c2ecf20Sopenharmony_ci		pvr2_stream_internal_flush(sp);
4858c2ecf20Sopenharmony_ci		sp->dev = dev;
4868c2ecf20Sopenharmony_ci		sp->endpoint = endpoint;
4878c2ecf20Sopenharmony_ci		sp->fail_tolerance = tolerance;
4888c2ecf20Sopenharmony_ci	} while (0); mutex_unlock(&sp->mutex);
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_civoid pvr2_stream_set_callback(struct pvr2_stream *sp,
4928c2ecf20Sopenharmony_ci			      pvr2_stream_callback func,
4938c2ecf20Sopenharmony_ci			      void *data)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	unsigned long irq_flags;
4968c2ecf20Sopenharmony_ci	mutex_lock(&sp->mutex);
4978c2ecf20Sopenharmony_ci	do {
4988c2ecf20Sopenharmony_ci		spin_lock_irqsave(&sp->list_lock, irq_flags);
4998c2ecf20Sopenharmony_ci		sp->callback_data = data;
5008c2ecf20Sopenharmony_ci		sp->callback_func = func;
5018c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&sp->list_lock, irq_flags);
5028c2ecf20Sopenharmony_ci	} while (0);
5038c2ecf20Sopenharmony_ci	mutex_unlock(&sp->mutex);
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_civoid pvr2_stream_get_stats(struct pvr2_stream *sp,
5078c2ecf20Sopenharmony_ci			   struct pvr2_stream_stats *stats,
5088c2ecf20Sopenharmony_ci			   int zero_counts)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	unsigned long irq_flags;
5118c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sp->list_lock, irq_flags);
5128c2ecf20Sopenharmony_ci	if (stats) {
5138c2ecf20Sopenharmony_ci		stats->buffers_in_queue = sp->q_count;
5148c2ecf20Sopenharmony_ci		stats->buffers_in_idle = sp->i_count;
5158c2ecf20Sopenharmony_ci		stats->buffers_in_ready = sp->r_count;
5168c2ecf20Sopenharmony_ci		stats->buffers_processed = sp->buffers_processed;
5178c2ecf20Sopenharmony_ci		stats->buffers_failed = sp->buffers_failed;
5188c2ecf20Sopenharmony_ci		stats->bytes_processed = sp->bytes_processed;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci	if (zero_counts) {
5218c2ecf20Sopenharmony_ci		sp->buffers_processed = 0;
5228c2ecf20Sopenharmony_ci		sp->buffers_failed = 0;
5238c2ecf20Sopenharmony_ci		sp->bytes_processed = 0;
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sp->list_lock, irq_flags);
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci/* Query / set the nominal buffer count */
5298c2ecf20Sopenharmony_ciint pvr2_stream_get_buffer_count(struct pvr2_stream *sp)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	return sp->buffer_target_count;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ciint pvr2_stream_set_buffer_count(struct pvr2_stream *sp, unsigned int cnt)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	int ret;
5378c2ecf20Sopenharmony_ci	if (sp->buffer_target_count == cnt) return 0;
5388c2ecf20Sopenharmony_ci	mutex_lock(&sp->mutex);
5398c2ecf20Sopenharmony_ci	do {
5408c2ecf20Sopenharmony_ci		sp->buffer_target_count = cnt;
5418c2ecf20Sopenharmony_ci		ret = pvr2_stream_achieve_buffer_count(sp);
5428c2ecf20Sopenharmony_ci	} while (0);
5438c2ecf20Sopenharmony_ci	mutex_unlock(&sp->mutex);
5448c2ecf20Sopenharmony_ci	return ret;
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistruct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *sp)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	struct list_head *lp = sp->idle_list.next;
5508c2ecf20Sopenharmony_ci	if (lp == &sp->idle_list) return NULL;
5518c2ecf20Sopenharmony_ci	return list_entry(lp, struct pvr2_buffer, list_overhead);
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cistruct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *sp)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	struct list_head *lp = sp->ready_list.next;
5578c2ecf20Sopenharmony_ci	if (lp == &sp->ready_list) return NULL;
5588c2ecf20Sopenharmony_ci	return list_entry(lp, struct pvr2_buffer, list_overhead);
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistruct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp, int id)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	if (id < 0) return NULL;
5648c2ecf20Sopenharmony_ci	if (id >= sp->buffer_total_count) return NULL;
5658c2ecf20Sopenharmony_ci	return sp->buffers[id];
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ciint pvr2_stream_get_ready_count(struct pvr2_stream *sp)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	return sp->r_count;
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_civoid pvr2_stream_kill(struct pvr2_stream *sp)
5748c2ecf20Sopenharmony_ci{
5758c2ecf20Sopenharmony_ci	struct pvr2_buffer *bp;
5768c2ecf20Sopenharmony_ci	mutex_lock(&sp->mutex);
5778c2ecf20Sopenharmony_ci	do {
5788c2ecf20Sopenharmony_ci		pvr2_stream_internal_flush(sp);
5798c2ecf20Sopenharmony_ci		while ((bp = pvr2_stream_get_ready_buffer(sp)) != NULL) {
5808c2ecf20Sopenharmony_ci			pvr2_buffer_set_idle(bp);
5818c2ecf20Sopenharmony_ci		}
5828c2ecf20Sopenharmony_ci		if (sp->buffer_total_count != sp->buffer_target_count) {
5838c2ecf20Sopenharmony_ci			pvr2_stream_achieve_buffer_count(sp);
5848c2ecf20Sopenharmony_ci		}
5858c2ecf20Sopenharmony_ci	} while (0);
5868c2ecf20Sopenharmony_ci	mutex_unlock(&sp->mutex);
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ciint pvr2_buffer_queue(struct pvr2_buffer *bp)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci#undef SEED_BUFFER
5928c2ecf20Sopenharmony_ci#ifdef SEED_BUFFER
5938c2ecf20Sopenharmony_ci	unsigned int idx;
5948c2ecf20Sopenharmony_ci	unsigned int val;
5958c2ecf20Sopenharmony_ci#endif
5968c2ecf20Sopenharmony_ci	int ret = 0;
5978c2ecf20Sopenharmony_ci	struct pvr2_stream *sp;
5988c2ecf20Sopenharmony_ci	if (!bp) return -EINVAL;
5998c2ecf20Sopenharmony_ci	sp = bp->stream;
6008c2ecf20Sopenharmony_ci	mutex_lock(&sp->mutex);
6018c2ecf20Sopenharmony_ci	do {
6028c2ecf20Sopenharmony_ci		pvr2_buffer_wipe(bp);
6038c2ecf20Sopenharmony_ci		if (!sp->dev) {
6048c2ecf20Sopenharmony_ci			ret = -EIO;
6058c2ecf20Sopenharmony_ci			break;
6068c2ecf20Sopenharmony_ci		}
6078c2ecf20Sopenharmony_ci		pvr2_buffer_set_queued(bp);
6088c2ecf20Sopenharmony_ci#ifdef SEED_BUFFER
6098c2ecf20Sopenharmony_ci		for (idx = 0; idx < (bp->max_count) / 4; idx++) {
6108c2ecf20Sopenharmony_ci			val = bp->id << 24;
6118c2ecf20Sopenharmony_ci			val |= idx;
6128c2ecf20Sopenharmony_ci			((unsigned int *)(bp->ptr))[idx] = val;
6138c2ecf20Sopenharmony_ci		}
6148c2ecf20Sopenharmony_ci#endif
6158c2ecf20Sopenharmony_ci		bp->status = -EINPROGRESS;
6168c2ecf20Sopenharmony_ci		usb_fill_bulk_urb(bp->purb,      // struct urb *urb
6178c2ecf20Sopenharmony_ci				  sp->dev,       // struct usb_device *dev
6188c2ecf20Sopenharmony_ci				  // endpoint (below)
6198c2ecf20Sopenharmony_ci				  usb_rcvbulkpipe(sp->dev, sp->endpoint),
6208c2ecf20Sopenharmony_ci				  bp->ptr,       // void *transfer_buffer
6218c2ecf20Sopenharmony_ci				  bp->max_count, // int buffer_length
6228c2ecf20Sopenharmony_ci				  buffer_complete,
6238c2ecf20Sopenharmony_ci				  bp);
6248c2ecf20Sopenharmony_ci		usb_submit_urb(bp->purb, GFP_KERNEL);
6258c2ecf20Sopenharmony_ci	} while (0);
6268c2ecf20Sopenharmony_ci	mutex_unlock(&sp->mutex);
6278c2ecf20Sopenharmony_ci	return ret;
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ciint pvr2_buffer_set_buffer(struct pvr2_buffer *bp, void *ptr, unsigned int cnt)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	int ret = 0;
6338c2ecf20Sopenharmony_ci	unsigned long irq_flags;
6348c2ecf20Sopenharmony_ci	struct pvr2_stream *sp;
6358c2ecf20Sopenharmony_ci	if (!bp) return -EINVAL;
6368c2ecf20Sopenharmony_ci	sp = bp->stream;
6378c2ecf20Sopenharmony_ci	mutex_lock(&sp->mutex);
6388c2ecf20Sopenharmony_ci	do {
6398c2ecf20Sopenharmony_ci		spin_lock_irqsave(&sp->list_lock, irq_flags);
6408c2ecf20Sopenharmony_ci		if (bp->state != pvr2_buffer_state_idle) {
6418c2ecf20Sopenharmony_ci			ret = -EPERM;
6428c2ecf20Sopenharmony_ci		} else {
6438c2ecf20Sopenharmony_ci			bp->ptr = ptr;
6448c2ecf20Sopenharmony_ci			bp->stream->i_bcount -= bp->max_count;
6458c2ecf20Sopenharmony_ci			bp->max_count = cnt;
6468c2ecf20Sopenharmony_ci			bp->stream->i_bcount += bp->max_count;
6478c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_BUF_FLOW,
6488c2ecf20Sopenharmony_ci				   "/*---TRACE_FLOW---*/ bufferPool	%8s cap cap=%07d cnt=%02d",
6498c2ecf20Sopenharmony_ci				   pvr2_buffer_state_decode(
6508c2ecf20Sopenharmony_ci					   pvr2_buffer_state_idle),
6518c2ecf20Sopenharmony_ci				   bp->stream->i_bcount, bp->stream->i_count);
6528c2ecf20Sopenharmony_ci		}
6538c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&sp->list_lock, irq_flags);
6548c2ecf20Sopenharmony_ci	} while (0);
6558c2ecf20Sopenharmony_ci	mutex_unlock(&sp->mutex);
6568c2ecf20Sopenharmony_ci	return ret;
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ciunsigned int pvr2_buffer_get_count(struct pvr2_buffer *bp)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	return bp->used_count;
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ciint pvr2_buffer_get_status(struct pvr2_buffer *bp)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	return bp->status;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ciint pvr2_buffer_get_id(struct pvr2_buffer *bp)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	return bp->id;
6728c2ecf20Sopenharmony_ci}
673