xref: /openbmc/linux/drivers/media/usb/pvrusb2/pvrusb2-io.c (revision 0c0d06cac63ee327ceaab4b5ffe2206574ab86bd)
1*0c0d06caSMauro Carvalho Chehab /*
2*0c0d06caSMauro Carvalho Chehab  *
3*0c0d06caSMauro Carvalho Chehab  *
4*0c0d06caSMauro Carvalho Chehab  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
5*0c0d06caSMauro Carvalho Chehab  *
6*0c0d06caSMauro Carvalho Chehab  *  This program is free software; you can redistribute it and/or modify
7*0c0d06caSMauro Carvalho Chehab  *  it under the terms of the GNU General Public License as published by
8*0c0d06caSMauro Carvalho Chehab  *  the Free Software Foundation; either version 2 of the License
9*0c0d06caSMauro Carvalho Chehab  *
10*0c0d06caSMauro Carvalho Chehab  *  This program is distributed in the hope that it will be useful,
11*0c0d06caSMauro Carvalho Chehab  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12*0c0d06caSMauro Carvalho Chehab  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*0c0d06caSMauro Carvalho Chehab  *  GNU General Public License for more details.
14*0c0d06caSMauro Carvalho Chehab  *
15*0c0d06caSMauro Carvalho Chehab  *  You should have received a copy of the GNU General Public License
16*0c0d06caSMauro Carvalho Chehab  *  along with this program; if not, write to the Free Software
17*0c0d06caSMauro Carvalho Chehab  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18*0c0d06caSMauro Carvalho Chehab  *
19*0c0d06caSMauro Carvalho Chehab  */
20*0c0d06caSMauro Carvalho Chehab 
21*0c0d06caSMauro Carvalho Chehab #include "pvrusb2-io.h"
22*0c0d06caSMauro Carvalho Chehab #include "pvrusb2-debug.h"
23*0c0d06caSMauro Carvalho Chehab #include <linux/errno.h>
24*0c0d06caSMauro Carvalho Chehab #include <linux/string.h>
25*0c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
26*0c0d06caSMauro Carvalho Chehab #include <linux/mutex.h>
27*0c0d06caSMauro Carvalho Chehab 
28*0c0d06caSMauro Carvalho Chehab static const char *pvr2_buffer_state_decode(enum pvr2_buffer_state);
29*0c0d06caSMauro Carvalho Chehab 
30*0c0d06caSMauro Carvalho Chehab #define BUFFER_SIG 0x47653271
31*0c0d06caSMauro Carvalho Chehab 
32*0c0d06caSMauro Carvalho Chehab // #define SANITY_CHECK_BUFFERS
33*0c0d06caSMauro Carvalho Chehab 
34*0c0d06caSMauro Carvalho Chehab 
35*0c0d06caSMauro Carvalho Chehab #ifdef SANITY_CHECK_BUFFERS
36*0c0d06caSMauro Carvalho Chehab #define BUFFER_CHECK(bp) do { \
37*0c0d06caSMauro Carvalho Chehab 	if ((bp)->signature != BUFFER_SIG) { \
38*0c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS, \
39*0c0d06caSMauro Carvalho Chehab 		"Buffer %p is bad at %s:%d", \
40*0c0d06caSMauro Carvalho Chehab 		(bp),__FILE__,__LINE__); \
41*0c0d06caSMauro Carvalho Chehab 		pvr2_buffer_describe(bp,"BadSig"); \
42*0c0d06caSMauro Carvalho Chehab 		BUG(); \
43*0c0d06caSMauro Carvalho Chehab 	} \
44*0c0d06caSMauro Carvalho Chehab } while (0)
45*0c0d06caSMauro Carvalho Chehab #else
46*0c0d06caSMauro Carvalho Chehab #define BUFFER_CHECK(bp) do {} while(0)
47*0c0d06caSMauro Carvalho Chehab #endif
48*0c0d06caSMauro Carvalho Chehab 
49*0c0d06caSMauro Carvalho Chehab struct pvr2_stream {
50*0c0d06caSMauro Carvalho Chehab 	/* Buffers queued for reading */
51*0c0d06caSMauro Carvalho Chehab 	struct list_head queued_list;
52*0c0d06caSMauro Carvalho Chehab 	unsigned int q_count;
53*0c0d06caSMauro Carvalho Chehab 	unsigned int q_bcount;
54*0c0d06caSMauro Carvalho Chehab 	/* Buffers with retrieved data */
55*0c0d06caSMauro Carvalho Chehab 	struct list_head ready_list;
56*0c0d06caSMauro Carvalho Chehab 	unsigned int r_count;
57*0c0d06caSMauro Carvalho Chehab 	unsigned int r_bcount;
58*0c0d06caSMauro Carvalho Chehab 	/* Buffers available for use */
59*0c0d06caSMauro Carvalho Chehab 	struct list_head idle_list;
60*0c0d06caSMauro Carvalho Chehab 	unsigned int i_count;
61*0c0d06caSMauro Carvalho Chehab 	unsigned int i_bcount;
62*0c0d06caSMauro Carvalho Chehab 	/* Pointers to all buffers */
63*0c0d06caSMauro Carvalho Chehab 	struct pvr2_buffer **buffers;
64*0c0d06caSMauro Carvalho Chehab 	/* Array size of buffers */
65*0c0d06caSMauro Carvalho Chehab 	unsigned int buffer_slot_count;
66*0c0d06caSMauro Carvalho Chehab 	/* Total buffers actually in circulation */
67*0c0d06caSMauro Carvalho Chehab 	unsigned int buffer_total_count;
68*0c0d06caSMauro Carvalho Chehab 	/* Designed number of buffers to be in circulation */
69*0c0d06caSMauro Carvalho Chehab 	unsigned int buffer_target_count;
70*0c0d06caSMauro Carvalho Chehab 	/* Executed when ready list become non-empty */
71*0c0d06caSMauro Carvalho Chehab 	pvr2_stream_callback callback_func;
72*0c0d06caSMauro Carvalho Chehab 	void *callback_data;
73*0c0d06caSMauro Carvalho Chehab 	/* Context for transfer endpoint */
74*0c0d06caSMauro Carvalho Chehab 	struct usb_device *dev;
75*0c0d06caSMauro Carvalho Chehab 	int endpoint;
76*0c0d06caSMauro Carvalho Chehab 	/* Overhead for mutex enforcement */
77*0c0d06caSMauro Carvalho Chehab 	spinlock_t list_lock;
78*0c0d06caSMauro Carvalho Chehab 	struct mutex mutex;
79*0c0d06caSMauro Carvalho Chehab 	/* Tracking state for tolerating errors */
80*0c0d06caSMauro Carvalho Chehab 	unsigned int fail_count;
81*0c0d06caSMauro Carvalho Chehab 	unsigned int fail_tolerance;
82*0c0d06caSMauro Carvalho Chehab 
83*0c0d06caSMauro Carvalho Chehab 	unsigned int buffers_processed;
84*0c0d06caSMauro Carvalho Chehab 	unsigned int buffers_failed;
85*0c0d06caSMauro Carvalho Chehab 	unsigned int bytes_processed;
86*0c0d06caSMauro Carvalho Chehab };
87*0c0d06caSMauro Carvalho Chehab 
88*0c0d06caSMauro Carvalho Chehab struct pvr2_buffer {
89*0c0d06caSMauro Carvalho Chehab 	int id;
90*0c0d06caSMauro Carvalho Chehab 	int signature;
91*0c0d06caSMauro Carvalho Chehab 	enum pvr2_buffer_state state;
92*0c0d06caSMauro Carvalho Chehab 	void *ptr;               /* Pointer to storage area */
93*0c0d06caSMauro Carvalho Chehab 	unsigned int max_count;  /* Size of storage area */
94*0c0d06caSMauro Carvalho Chehab 	unsigned int used_count; /* Amount of valid data in storage area */
95*0c0d06caSMauro Carvalho Chehab 	int status;              /* Transfer result status */
96*0c0d06caSMauro Carvalho Chehab 	struct pvr2_stream *stream;
97*0c0d06caSMauro Carvalho Chehab 	struct list_head list_overhead;
98*0c0d06caSMauro Carvalho Chehab 	struct urb *purb;
99*0c0d06caSMauro Carvalho Chehab };
100*0c0d06caSMauro Carvalho Chehab 
101*0c0d06caSMauro Carvalho Chehab static const char *pvr2_buffer_state_decode(enum pvr2_buffer_state st)
102*0c0d06caSMauro Carvalho Chehab {
103*0c0d06caSMauro Carvalho Chehab 	switch (st) {
104*0c0d06caSMauro Carvalho Chehab 	case pvr2_buffer_state_none: return "none";
105*0c0d06caSMauro Carvalho Chehab 	case pvr2_buffer_state_idle: return "idle";
106*0c0d06caSMauro Carvalho Chehab 	case pvr2_buffer_state_queued: return "queued";
107*0c0d06caSMauro Carvalho Chehab 	case pvr2_buffer_state_ready: return "ready";
108*0c0d06caSMauro Carvalho Chehab 	}
109*0c0d06caSMauro Carvalho Chehab 	return "unknown";
110*0c0d06caSMauro Carvalho Chehab }
111*0c0d06caSMauro Carvalho Chehab 
112*0c0d06caSMauro Carvalho Chehab #ifdef SANITY_CHECK_BUFFERS
113*0c0d06caSMauro Carvalho Chehab static void pvr2_buffer_describe(struct pvr2_buffer *bp,const char *msg)
114*0c0d06caSMauro Carvalho Chehab {
115*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INFO,
116*0c0d06caSMauro Carvalho Chehab 		   "buffer%s%s %p state=%s id=%d status=%d"
117*0c0d06caSMauro Carvalho Chehab 		   " stream=%p purb=%p sig=0x%x",
118*0c0d06caSMauro Carvalho Chehab 		   (msg ? " " : ""),
119*0c0d06caSMauro Carvalho Chehab 		   (msg ? msg : ""),
120*0c0d06caSMauro Carvalho Chehab 		   bp,
121*0c0d06caSMauro Carvalho Chehab 		   (bp ? pvr2_buffer_state_decode(bp->state) : "(invalid)"),
122*0c0d06caSMauro Carvalho Chehab 		   (bp ? bp->id : 0),
123*0c0d06caSMauro Carvalho Chehab 		   (bp ? bp->status : 0),
124*0c0d06caSMauro Carvalho Chehab 		   (bp ? bp->stream : NULL),
125*0c0d06caSMauro Carvalho Chehab 		   (bp ? bp->purb : NULL),
126*0c0d06caSMauro Carvalho Chehab 		   (bp ? bp->signature : 0));
127*0c0d06caSMauro Carvalho Chehab }
128*0c0d06caSMauro Carvalho Chehab #endif  /*  SANITY_CHECK_BUFFERS  */
129*0c0d06caSMauro Carvalho Chehab 
130*0c0d06caSMauro Carvalho Chehab static void pvr2_buffer_remove(struct pvr2_buffer *bp)
131*0c0d06caSMauro Carvalho Chehab {
132*0c0d06caSMauro Carvalho Chehab 	unsigned int *cnt;
133*0c0d06caSMauro Carvalho Chehab 	unsigned int *bcnt;
134*0c0d06caSMauro Carvalho Chehab 	unsigned int ccnt;
135*0c0d06caSMauro Carvalho Chehab 	struct pvr2_stream *sp = bp->stream;
136*0c0d06caSMauro Carvalho Chehab 	switch (bp->state) {
137*0c0d06caSMauro Carvalho Chehab 	case pvr2_buffer_state_idle:
138*0c0d06caSMauro Carvalho Chehab 		cnt = &sp->i_count;
139*0c0d06caSMauro Carvalho Chehab 		bcnt = &sp->i_bcount;
140*0c0d06caSMauro Carvalho Chehab 		ccnt = bp->max_count;
141*0c0d06caSMauro Carvalho Chehab 		break;
142*0c0d06caSMauro Carvalho Chehab 	case pvr2_buffer_state_queued:
143*0c0d06caSMauro Carvalho Chehab 		cnt = &sp->q_count;
144*0c0d06caSMauro Carvalho Chehab 		bcnt = &sp->q_bcount;
145*0c0d06caSMauro Carvalho Chehab 		ccnt = bp->max_count;
146*0c0d06caSMauro Carvalho Chehab 		break;
147*0c0d06caSMauro Carvalho Chehab 	case pvr2_buffer_state_ready:
148*0c0d06caSMauro Carvalho Chehab 		cnt = &sp->r_count;
149*0c0d06caSMauro Carvalho Chehab 		bcnt = &sp->r_bcount;
150*0c0d06caSMauro Carvalho Chehab 		ccnt = bp->used_count;
151*0c0d06caSMauro Carvalho Chehab 		break;
152*0c0d06caSMauro Carvalho Chehab 	default:
153*0c0d06caSMauro Carvalho Chehab 		return;
154*0c0d06caSMauro Carvalho Chehab 	}
155*0c0d06caSMauro Carvalho Chehab 	list_del_init(&bp->list_overhead);
156*0c0d06caSMauro Carvalho Chehab 	(*cnt)--;
157*0c0d06caSMauro Carvalho Chehab 	(*bcnt) -= ccnt;
158*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
159*0c0d06caSMauro Carvalho Chehab 		   "/*---TRACE_FLOW---*/"
160*0c0d06caSMauro Carvalho Chehab 		   " bufferPool     %8s dec cap=%07d cnt=%02d",
161*0c0d06caSMauro Carvalho Chehab 		   pvr2_buffer_state_decode(bp->state),*bcnt,*cnt);
162*0c0d06caSMauro Carvalho Chehab 	bp->state = pvr2_buffer_state_none;
163*0c0d06caSMauro Carvalho Chehab }
164*0c0d06caSMauro Carvalho Chehab 
165*0c0d06caSMauro Carvalho Chehab static void pvr2_buffer_set_none(struct pvr2_buffer *bp)
166*0c0d06caSMauro Carvalho Chehab {
167*0c0d06caSMauro Carvalho Chehab 	unsigned long irq_flags;
168*0c0d06caSMauro Carvalho Chehab 	struct pvr2_stream *sp;
169*0c0d06caSMauro Carvalho Chehab 	BUFFER_CHECK(bp);
170*0c0d06caSMauro Carvalho Chehab 	sp = bp->stream;
171*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
172*0c0d06caSMauro Carvalho Chehab 		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s",
173*0c0d06caSMauro Carvalho Chehab 		   bp,
174*0c0d06caSMauro Carvalho Chehab 		   pvr2_buffer_state_decode(bp->state),
175*0c0d06caSMauro Carvalho Chehab 		   pvr2_buffer_state_decode(pvr2_buffer_state_none));
176*0c0d06caSMauro Carvalho Chehab 	spin_lock_irqsave(&sp->list_lock,irq_flags);
177*0c0d06caSMauro Carvalho Chehab 	pvr2_buffer_remove(bp);
178*0c0d06caSMauro Carvalho Chehab 	spin_unlock_irqrestore(&sp->list_lock,irq_flags);
179*0c0d06caSMauro Carvalho Chehab }
180*0c0d06caSMauro Carvalho Chehab 
181*0c0d06caSMauro Carvalho Chehab static int pvr2_buffer_set_ready(struct pvr2_buffer *bp)
182*0c0d06caSMauro Carvalho Chehab {
183*0c0d06caSMauro Carvalho Chehab 	int fl;
184*0c0d06caSMauro Carvalho Chehab 	unsigned long irq_flags;
185*0c0d06caSMauro Carvalho Chehab 	struct pvr2_stream *sp;
186*0c0d06caSMauro Carvalho Chehab 	BUFFER_CHECK(bp);
187*0c0d06caSMauro Carvalho Chehab 	sp = bp->stream;
188*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
189*0c0d06caSMauro Carvalho Chehab 		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s",
190*0c0d06caSMauro Carvalho Chehab 		   bp,
191*0c0d06caSMauro Carvalho Chehab 		   pvr2_buffer_state_decode(bp->state),
192*0c0d06caSMauro Carvalho Chehab 		   pvr2_buffer_state_decode(pvr2_buffer_state_ready));
193*0c0d06caSMauro Carvalho Chehab 	spin_lock_irqsave(&sp->list_lock,irq_flags);
194*0c0d06caSMauro Carvalho Chehab 	fl = (sp->r_count == 0);
195*0c0d06caSMauro Carvalho Chehab 	pvr2_buffer_remove(bp);
196*0c0d06caSMauro Carvalho Chehab 	list_add_tail(&bp->list_overhead,&sp->ready_list);
197*0c0d06caSMauro Carvalho Chehab 	bp->state = pvr2_buffer_state_ready;
198*0c0d06caSMauro Carvalho Chehab 	(sp->r_count)++;
199*0c0d06caSMauro Carvalho Chehab 	sp->r_bcount += bp->used_count;
200*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
201*0c0d06caSMauro Carvalho Chehab 		   "/*---TRACE_FLOW---*/"
202*0c0d06caSMauro Carvalho Chehab 		   " bufferPool     %8s inc cap=%07d cnt=%02d",
203*0c0d06caSMauro Carvalho Chehab 		   pvr2_buffer_state_decode(bp->state),
204*0c0d06caSMauro Carvalho Chehab 		   sp->r_bcount,sp->r_count);
205*0c0d06caSMauro Carvalho Chehab 	spin_unlock_irqrestore(&sp->list_lock,irq_flags);
206*0c0d06caSMauro Carvalho Chehab 	return fl;
207*0c0d06caSMauro Carvalho Chehab }
208*0c0d06caSMauro Carvalho Chehab 
209*0c0d06caSMauro Carvalho Chehab static void pvr2_buffer_set_idle(struct pvr2_buffer *bp)
210*0c0d06caSMauro Carvalho Chehab {
211*0c0d06caSMauro Carvalho Chehab 	unsigned long irq_flags;
212*0c0d06caSMauro Carvalho Chehab 	struct pvr2_stream *sp;
213*0c0d06caSMauro Carvalho Chehab 	BUFFER_CHECK(bp);
214*0c0d06caSMauro Carvalho Chehab 	sp = bp->stream;
215*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
216*0c0d06caSMauro Carvalho Chehab 		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s",
217*0c0d06caSMauro Carvalho Chehab 		   bp,
218*0c0d06caSMauro Carvalho Chehab 		   pvr2_buffer_state_decode(bp->state),
219*0c0d06caSMauro Carvalho Chehab 		   pvr2_buffer_state_decode(pvr2_buffer_state_idle));
220*0c0d06caSMauro Carvalho Chehab 	spin_lock_irqsave(&sp->list_lock,irq_flags);
221*0c0d06caSMauro Carvalho Chehab 	pvr2_buffer_remove(bp);
222*0c0d06caSMauro Carvalho Chehab 	list_add_tail(&bp->list_overhead,&sp->idle_list);
223*0c0d06caSMauro Carvalho Chehab 	bp->state = pvr2_buffer_state_idle;
224*0c0d06caSMauro Carvalho Chehab 	(sp->i_count)++;
225*0c0d06caSMauro Carvalho Chehab 	sp->i_bcount += bp->max_count;
226*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
227*0c0d06caSMauro Carvalho Chehab 		   "/*---TRACE_FLOW---*/"
228*0c0d06caSMauro Carvalho Chehab 		   " bufferPool     %8s inc cap=%07d cnt=%02d",
229*0c0d06caSMauro Carvalho Chehab 		   pvr2_buffer_state_decode(bp->state),
230*0c0d06caSMauro Carvalho Chehab 		   sp->i_bcount,sp->i_count);
231*0c0d06caSMauro Carvalho Chehab 	spin_unlock_irqrestore(&sp->list_lock,irq_flags);
232*0c0d06caSMauro Carvalho Chehab }
233*0c0d06caSMauro Carvalho Chehab 
234*0c0d06caSMauro Carvalho Chehab static void pvr2_buffer_set_queued(struct pvr2_buffer *bp)
235*0c0d06caSMauro Carvalho Chehab {
236*0c0d06caSMauro Carvalho Chehab 	unsigned long irq_flags;
237*0c0d06caSMauro Carvalho Chehab 	struct pvr2_stream *sp;
238*0c0d06caSMauro Carvalho Chehab 	BUFFER_CHECK(bp);
239*0c0d06caSMauro Carvalho Chehab 	sp = bp->stream;
240*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
241*0c0d06caSMauro Carvalho Chehab 		   "/*---TRACE_FLOW---*/ bufferState    %p %6s --> %6s",
242*0c0d06caSMauro Carvalho Chehab 		   bp,
243*0c0d06caSMauro Carvalho Chehab 		   pvr2_buffer_state_decode(bp->state),
244*0c0d06caSMauro Carvalho Chehab 		   pvr2_buffer_state_decode(pvr2_buffer_state_queued));
245*0c0d06caSMauro Carvalho Chehab 	spin_lock_irqsave(&sp->list_lock,irq_flags);
246*0c0d06caSMauro Carvalho Chehab 	pvr2_buffer_remove(bp);
247*0c0d06caSMauro Carvalho Chehab 	list_add_tail(&bp->list_overhead,&sp->queued_list);
248*0c0d06caSMauro Carvalho Chehab 	bp->state = pvr2_buffer_state_queued;
249*0c0d06caSMauro Carvalho Chehab 	(sp->q_count)++;
250*0c0d06caSMauro Carvalho Chehab 	sp->q_bcount += bp->max_count;
251*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
252*0c0d06caSMauro Carvalho Chehab 		   "/*---TRACE_FLOW---*/"
253*0c0d06caSMauro Carvalho Chehab 		   " bufferPool     %8s inc cap=%07d cnt=%02d",
254*0c0d06caSMauro Carvalho Chehab 		   pvr2_buffer_state_decode(bp->state),
255*0c0d06caSMauro Carvalho Chehab 		   sp->q_bcount,sp->q_count);
256*0c0d06caSMauro Carvalho Chehab 	spin_unlock_irqrestore(&sp->list_lock,irq_flags);
257*0c0d06caSMauro Carvalho Chehab }
258*0c0d06caSMauro Carvalho Chehab 
259*0c0d06caSMauro Carvalho Chehab static void pvr2_buffer_wipe(struct pvr2_buffer *bp)
260*0c0d06caSMauro Carvalho Chehab {
261*0c0d06caSMauro Carvalho Chehab 	if (bp->state == pvr2_buffer_state_queued) {
262*0c0d06caSMauro Carvalho Chehab 		usb_kill_urb(bp->purb);
263*0c0d06caSMauro Carvalho Chehab 	}
264*0c0d06caSMauro Carvalho Chehab }
265*0c0d06caSMauro Carvalho Chehab 
266*0c0d06caSMauro Carvalho Chehab static int pvr2_buffer_init(struct pvr2_buffer *bp,
267*0c0d06caSMauro Carvalho Chehab 			    struct pvr2_stream *sp,
268*0c0d06caSMauro Carvalho Chehab 			    unsigned int id)
269*0c0d06caSMauro Carvalho Chehab {
270*0c0d06caSMauro Carvalho Chehab 	memset(bp,0,sizeof(*bp));
271*0c0d06caSMauro Carvalho Chehab 	bp->signature = BUFFER_SIG;
272*0c0d06caSMauro Carvalho Chehab 	bp->id = id;
273*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_BUF_POOL,
274*0c0d06caSMauro Carvalho Chehab 		   "/*---TRACE_FLOW---*/ bufferInit     %p stream=%p",bp,sp);
275*0c0d06caSMauro Carvalho Chehab 	bp->stream = sp;
276*0c0d06caSMauro Carvalho Chehab 	bp->state = pvr2_buffer_state_none;
277*0c0d06caSMauro Carvalho Chehab 	INIT_LIST_HEAD(&bp->list_overhead);
278*0c0d06caSMauro Carvalho Chehab 	bp->purb = usb_alloc_urb(0,GFP_KERNEL);
279*0c0d06caSMauro Carvalho Chehab 	if (! bp->purb) return -ENOMEM;
280*0c0d06caSMauro Carvalho Chehab #ifdef SANITY_CHECK_BUFFERS
281*0c0d06caSMauro Carvalho Chehab 	pvr2_buffer_describe(bp,"create");
282*0c0d06caSMauro Carvalho Chehab #endif
283*0c0d06caSMauro Carvalho Chehab 	return 0;
284*0c0d06caSMauro Carvalho Chehab }
285*0c0d06caSMauro Carvalho Chehab 
286*0c0d06caSMauro Carvalho Chehab static void pvr2_buffer_done(struct pvr2_buffer *bp)
287*0c0d06caSMauro Carvalho Chehab {
288*0c0d06caSMauro Carvalho Chehab #ifdef SANITY_CHECK_BUFFERS
289*0c0d06caSMauro Carvalho Chehab 	pvr2_buffer_describe(bp,"delete");
290*0c0d06caSMauro Carvalho Chehab #endif
291*0c0d06caSMauro Carvalho Chehab 	pvr2_buffer_wipe(bp);
292*0c0d06caSMauro Carvalho Chehab 	pvr2_buffer_set_none(bp);
293*0c0d06caSMauro Carvalho Chehab 	bp->signature = 0;
294*0c0d06caSMauro Carvalho Chehab 	bp->stream = NULL;
295*0c0d06caSMauro Carvalho Chehab 	usb_free_urb(bp->purb);
296*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_BUF_POOL,"/*---TRACE_FLOW---*/"
297*0c0d06caSMauro Carvalho Chehab 		   " bufferDone     %p",bp);
298*0c0d06caSMauro Carvalho Chehab }
299*0c0d06caSMauro Carvalho Chehab 
300*0c0d06caSMauro Carvalho Chehab static int pvr2_stream_buffer_count(struct pvr2_stream *sp,unsigned int cnt)
301*0c0d06caSMauro Carvalho Chehab {
302*0c0d06caSMauro Carvalho Chehab 	int ret;
303*0c0d06caSMauro Carvalho Chehab 	unsigned int scnt;
304*0c0d06caSMauro Carvalho Chehab 
305*0c0d06caSMauro Carvalho Chehab 	/* Allocate buffers pointer array in multiples of 32 entries */
306*0c0d06caSMauro Carvalho Chehab 	if (cnt == sp->buffer_total_count) return 0;
307*0c0d06caSMauro Carvalho Chehab 
308*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_BUF_POOL,
309*0c0d06caSMauro Carvalho Chehab 		   "/*---TRACE_FLOW---*/ poolResize    "
310*0c0d06caSMauro Carvalho Chehab 		   " stream=%p cur=%d adj=%+d",
311*0c0d06caSMauro Carvalho Chehab 		   sp,
312*0c0d06caSMauro Carvalho Chehab 		   sp->buffer_total_count,
313*0c0d06caSMauro Carvalho Chehab 		   cnt-sp->buffer_total_count);
314*0c0d06caSMauro Carvalho Chehab 
315*0c0d06caSMauro Carvalho Chehab 	scnt = cnt & ~0x1f;
316*0c0d06caSMauro Carvalho Chehab 	if (cnt > scnt) scnt += 0x20;
317*0c0d06caSMauro Carvalho Chehab 
318*0c0d06caSMauro Carvalho Chehab 	if (cnt > sp->buffer_total_count) {
319*0c0d06caSMauro Carvalho Chehab 		if (scnt > sp->buffer_slot_count) {
320*0c0d06caSMauro Carvalho Chehab 			struct pvr2_buffer **nb;
321*0c0d06caSMauro Carvalho Chehab 			nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL);
322*0c0d06caSMauro Carvalho Chehab 			if (!nb) return -ENOMEM;
323*0c0d06caSMauro Carvalho Chehab 			if (sp->buffer_slot_count) {
324*0c0d06caSMauro Carvalho Chehab 				memcpy(nb,sp->buffers,
325*0c0d06caSMauro Carvalho Chehab 				       sp->buffer_slot_count * sizeof(*nb));
326*0c0d06caSMauro Carvalho Chehab 				kfree(sp->buffers);
327*0c0d06caSMauro Carvalho Chehab 			}
328*0c0d06caSMauro Carvalho Chehab 			sp->buffers = nb;
329*0c0d06caSMauro Carvalho Chehab 			sp->buffer_slot_count = scnt;
330*0c0d06caSMauro Carvalho Chehab 		}
331*0c0d06caSMauro Carvalho Chehab 		while (sp->buffer_total_count < cnt) {
332*0c0d06caSMauro Carvalho Chehab 			struct pvr2_buffer *bp;
333*0c0d06caSMauro Carvalho Chehab 			bp = kmalloc(sizeof(*bp),GFP_KERNEL);
334*0c0d06caSMauro Carvalho Chehab 			if (!bp) return -ENOMEM;
335*0c0d06caSMauro Carvalho Chehab 			ret = pvr2_buffer_init(bp,sp,sp->buffer_total_count);
336*0c0d06caSMauro Carvalho Chehab 			if (ret) {
337*0c0d06caSMauro Carvalho Chehab 				kfree(bp);
338*0c0d06caSMauro Carvalho Chehab 				return -ENOMEM;
339*0c0d06caSMauro Carvalho Chehab 			}
340*0c0d06caSMauro Carvalho Chehab 			sp->buffers[sp->buffer_total_count] = bp;
341*0c0d06caSMauro Carvalho Chehab 			(sp->buffer_total_count)++;
342*0c0d06caSMauro Carvalho Chehab 			pvr2_buffer_set_idle(bp);
343*0c0d06caSMauro Carvalho Chehab 		}
344*0c0d06caSMauro Carvalho Chehab 	} else {
345*0c0d06caSMauro Carvalho Chehab 		while (sp->buffer_total_count > cnt) {
346*0c0d06caSMauro Carvalho Chehab 			struct pvr2_buffer *bp;
347*0c0d06caSMauro Carvalho Chehab 			bp = sp->buffers[sp->buffer_total_count - 1];
348*0c0d06caSMauro Carvalho Chehab 			/* Paranoia */
349*0c0d06caSMauro Carvalho Chehab 			sp->buffers[sp->buffer_total_count - 1] = NULL;
350*0c0d06caSMauro Carvalho Chehab 			(sp->buffer_total_count)--;
351*0c0d06caSMauro Carvalho Chehab 			pvr2_buffer_done(bp);
352*0c0d06caSMauro Carvalho Chehab 			kfree(bp);
353*0c0d06caSMauro Carvalho Chehab 		}
354*0c0d06caSMauro Carvalho Chehab 		if (scnt < sp->buffer_slot_count) {
355*0c0d06caSMauro Carvalho Chehab 			struct pvr2_buffer **nb = NULL;
356*0c0d06caSMauro Carvalho Chehab 			if (scnt) {
357*0c0d06caSMauro Carvalho Chehab 				nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL);
358*0c0d06caSMauro Carvalho Chehab 				if (!nb) return -ENOMEM;
359*0c0d06caSMauro Carvalho Chehab 				memcpy(nb,sp->buffers,scnt * sizeof(*nb));
360*0c0d06caSMauro Carvalho Chehab 			}
361*0c0d06caSMauro Carvalho Chehab 			kfree(sp->buffers);
362*0c0d06caSMauro Carvalho Chehab 			sp->buffers = nb;
363*0c0d06caSMauro Carvalho Chehab 			sp->buffer_slot_count = scnt;
364*0c0d06caSMauro Carvalho Chehab 		}
365*0c0d06caSMauro Carvalho Chehab 	}
366*0c0d06caSMauro Carvalho Chehab 	return 0;
367*0c0d06caSMauro Carvalho Chehab }
368*0c0d06caSMauro Carvalho Chehab 
369*0c0d06caSMauro Carvalho Chehab static int pvr2_stream_achieve_buffer_count(struct pvr2_stream *sp)
370*0c0d06caSMauro Carvalho Chehab {
371*0c0d06caSMauro Carvalho Chehab 	struct pvr2_buffer *bp;
372*0c0d06caSMauro Carvalho Chehab 	unsigned int cnt;
373*0c0d06caSMauro Carvalho Chehab 
374*0c0d06caSMauro Carvalho Chehab 	if (sp->buffer_total_count == sp->buffer_target_count) return 0;
375*0c0d06caSMauro Carvalho Chehab 
376*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_BUF_POOL,
377*0c0d06caSMauro Carvalho Chehab 		   "/*---TRACE_FLOW---*/"
378*0c0d06caSMauro Carvalho Chehab 		   " poolCheck      stream=%p cur=%d tgt=%d",
379*0c0d06caSMauro Carvalho Chehab 		   sp,sp->buffer_total_count,sp->buffer_target_count);
380*0c0d06caSMauro Carvalho Chehab 
381*0c0d06caSMauro Carvalho Chehab 	if (sp->buffer_total_count < sp->buffer_target_count) {
382*0c0d06caSMauro Carvalho Chehab 		return pvr2_stream_buffer_count(sp,sp->buffer_target_count);
383*0c0d06caSMauro Carvalho Chehab 	}
384*0c0d06caSMauro Carvalho Chehab 
385*0c0d06caSMauro Carvalho Chehab 	cnt = 0;
386*0c0d06caSMauro Carvalho Chehab 	while ((sp->buffer_total_count - cnt) > sp->buffer_target_count) {
387*0c0d06caSMauro Carvalho Chehab 		bp = sp->buffers[sp->buffer_total_count - (cnt + 1)];
388*0c0d06caSMauro Carvalho Chehab 		if (bp->state != pvr2_buffer_state_idle) break;
389*0c0d06caSMauro Carvalho Chehab 		cnt++;
390*0c0d06caSMauro Carvalho Chehab 	}
391*0c0d06caSMauro Carvalho Chehab 	if (cnt) {
392*0c0d06caSMauro Carvalho Chehab 		pvr2_stream_buffer_count(sp,sp->buffer_total_count - cnt);
393*0c0d06caSMauro Carvalho Chehab 	}
394*0c0d06caSMauro Carvalho Chehab 
395*0c0d06caSMauro Carvalho Chehab 	return 0;
396*0c0d06caSMauro Carvalho Chehab }
397*0c0d06caSMauro Carvalho Chehab 
398*0c0d06caSMauro Carvalho Chehab static void pvr2_stream_internal_flush(struct pvr2_stream *sp)
399*0c0d06caSMauro Carvalho Chehab {
400*0c0d06caSMauro Carvalho Chehab 	struct list_head *lp;
401*0c0d06caSMauro Carvalho Chehab 	struct pvr2_buffer *bp1;
402*0c0d06caSMauro Carvalho Chehab 	while ((lp = sp->queued_list.next) != &sp->queued_list) {
403*0c0d06caSMauro Carvalho Chehab 		bp1 = list_entry(lp,struct pvr2_buffer,list_overhead);
404*0c0d06caSMauro Carvalho Chehab 		pvr2_buffer_wipe(bp1);
405*0c0d06caSMauro Carvalho Chehab 		/* At this point, we should be guaranteed that no
406*0c0d06caSMauro Carvalho Chehab 		   completion callback may happen on this buffer.  But it's
407*0c0d06caSMauro Carvalho Chehab 		   possible that it might have completed after we noticed
408*0c0d06caSMauro Carvalho Chehab 		   it but before we wiped it.  So double check its status
409*0c0d06caSMauro Carvalho Chehab 		   here first. */
410*0c0d06caSMauro Carvalho Chehab 		if (bp1->state != pvr2_buffer_state_queued) continue;
411*0c0d06caSMauro Carvalho Chehab 		pvr2_buffer_set_idle(bp1);
412*0c0d06caSMauro Carvalho Chehab 	}
413*0c0d06caSMauro Carvalho Chehab 	if (sp->buffer_total_count != sp->buffer_target_count) {
414*0c0d06caSMauro Carvalho Chehab 		pvr2_stream_achieve_buffer_count(sp);
415*0c0d06caSMauro Carvalho Chehab 	}
416*0c0d06caSMauro Carvalho Chehab }
417*0c0d06caSMauro Carvalho Chehab 
418*0c0d06caSMauro Carvalho Chehab static void pvr2_stream_init(struct pvr2_stream *sp)
419*0c0d06caSMauro Carvalho Chehab {
420*0c0d06caSMauro Carvalho Chehab 	spin_lock_init(&sp->list_lock);
421*0c0d06caSMauro Carvalho Chehab 	mutex_init(&sp->mutex);
422*0c0d06caSMauro Carvalho Chehab 	INIT_LIST_HEAD(&sp->queued_list);
423*0c0d06caSMauro Carvalho Chehab 	INIT_LIST_HEAD(&sp->ready_list);
424*0c0d06caSMauro Carvalho Chehab 	INIT_LIST_HEAD(&sp->idle_list);
425*0c0d06caSMauro Carvalho Chehab }
426*0c0d06caSMauro Carvalho Chehab 
427*0c0d06caSMauro Carvalho Chehab static void pvr2_stream_done(struct pvr2_stream *sp)
428*0c0d06caSMauro Carvalho Chehab {
429*0c0d06caSMauro Carvalho Chehab 	mutex_lock(&sp->mutex); do {
430*0c0d06caSMauro Carvalho Chehab 		pvr2_stream_internal_flush(sp);
431*0c0d06caSMauro Carvalho Chehab 		pvr2_stream_buffer_count(sp,0);
432*0c0d06caSMauro Carvalho Chehab 	} while (0); mutex_unlock(&sp->mutex);
433*0c0d06caSMauro Carvalho Chehab }
434*0c0d06caSMauro Carvalho Chehab 
435*0c0d06caSMauro Carvalho Chehab static void buffer_complete(struct urb *urb)
436*0c0d06caSMauro Carvalho Chehab {
437*0c0d06caSMauro Carvalho Chehab 	struct pvr2_buffer *bp = urb->context;
438*0c0d06caSMauro Carvalho Chehab 	struct pvr2_stream *sp;
439*0c0d06caSMauro Carvalho Chehab 	unsigned long irq_flags;
440*0c0d06caSMauro Carvalho Chehab 	BUFFER_CHECK(bp);
441*0c0d06caSMauro Carvalho Chehab 	sp = bp->stream;
442*0c0d06caSMauro Carvalho Chehab 	bp->used_count = 0;
443*0c0d06caSMauro Carvalho Chehab 	bp->status = 0;
444*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_BUF_FLOW,
445*0c0d06caSMauro Carvalho Chehab 		   "/*---TRACE_FLOW---*/ bufferComplete %p stat=%d cnt=%d",
446*0c0d06caSMauro Carvalho Chehab 		   bp,urb->status,urb->actual_length);
447*0c0d06caSMauro Carvalho Chehab 	spin_lock_irqsave(&sp->list_lock,irq_flags);
448*0c0d06caSMauro Carvalho Chehab 	if ((!(urb->status)) ||
449*0c0d06caSMauro Carvalho Chehab 	    (urb->status == -ENOENT) ||
450*0c0d06caSMauro Carvalho Chehab 	    (urb->status == -ECONNRESET) ||
451*0c0d06caSMauro Carvalho Chehab 	    (urb->status == -ESHUTDOWN)) {
452*0c0d06caSMauro Carvalho Chehab 		(sp->buffers_processed)++;
453*0c0d06caSMauro Carvalho Chehab 		sp->bytes_processed += urb->actual_length;
454*0c0d06caSMauro Carvalho Chehab 		bp->used_count = urb->actual_length;
455*0c0d06caSMauro Carvalho Chehab 		if (sp->fail_count) {
456*0c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_TOLERANCE,
457*0c0d06caSMauro Carvalho Chehab 				   "stream %p transfer ok"
458*0c0d06caSMauro Carvalho Chehab 				   " - fail count reset",sp);
459*0c0d06caSMauro Carvalho Chehab 			sp->fail_count = 0;
460*0c0d06caSMauro Carvalho Chehab 		}
461*0c0d06caSMauro Carvalho Chehab 	} else if (sp->fail_count < sp->fail_tolerance) {
462*0c0d06caSMauro Carvalho Chehab 		// We can tolerate this error, because we're below the
463*0c0d06caSMauro Carvalho Chehab 		// threshold...
464*0c0d06caSMauro Carvalho Chehab 		(sp->fail_count)++;
465*0c0d06caSMauro Carvalho Chehab 		(sp->buffers_failed)++;
466*0c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_TOLERANCE,
467*0c0d06caSMauro Carvalho Chehab 			   "stream %p ignoring error %d"
468*0c0d06caSMauro Carvalho Chehab 			   " - fail count increased to %u",
469*0c0d06caSMauro Carvalho Chehab 			   sp,urb->status,sp->fail_count);
470*0c0d06caSMauro Carvalho Chehab 	} else {
471*0c0d06caSMauro Carvalho Chehab 		(sp->buffers_failed)++;
472*0c0d06caSMauro Carvalho Chehab 		bp->status = urb->status;
473*0c0d06caSMauro Carvalho Chehab 	}
474*0c0d06caSMauro Carvalho Chehab 	spin_unlock_irqrestore(&sp->list_lock,irq_flags);
475*0c0d06caSMauro Carvalho Chehab 	pvr2_buffer_set_ready(bp);
476*0c0d06caSMauro Carvalho Chehab 	if (sp && sp->callback_func) {
477*0c0d06caSMauro Carvalho Chehab 		sp->callback_func(sp->callback_data);
478*0c0d06caSMauro Carvalho Chehab 	}
479*0c0d06caSMauro Carvalho Chehab }
480*0c0d06caSMauro Carvalho Chehab 
481*0c0d06caSMauro Carvalho Chehab struct pvr2_stream *pvr2_stream_create(void)
482*0c0d06caSMauro Carvalho Chehab {
483*0c0d06caSMauro Carvalho Chehab 	struct pvr2_stream *sp;
484*0c0d06caSMauro Carvalho Chehab 	sp = kzalloc(sizeof(*sp),GFP_KERNEL);
485*0c0d06caSMauro Carvalho Chehab 	if (!sp) return sp;
486*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_create: sp=%p",sp);
487*0c0d06caSMauro Carvalho Chehab 	pvr2_stream_init(sp);
488*0c0d06caSMauro Carvalho Chehab 	return sp;
489*0c0d06caSMauro Carvalho Chehab }
490*0c0d06caSMauro Carvalho Chehab 
491*0c0d06caSMauro Carvalho Chehab void pvr2_stream_destroy(struct pvr2_stream *sp)
492*0c0d06caSMauro Carvalho Chehab {
493*0c0d06caSMauro Carvalho Chehab 	if (!sp) return;
494*0c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_destroy: sp=%p",sp);
495*0c0d06caSMauro Carvalho Chehab 	pvr2_stream_done(sp);
496*0c0d06caSMauro Carvalho Chehab 	kfree(sp);
497*0c0d06caSMauro Carvalho Chehab }
498*0c0d06caSMauro Carvalho Chehab 
499*0c0d06caSMauro Carvalho Chehab void pvr2_stream_setup(struct pvr2_stream *sp,
500*0c0d06caSMauro Carvalho Chehab 		       struct usb_device *dev,
501*0c0d06caSMauro Carvalho Chehab 		       int endpoint,
502*0c0d06caSMauro Carvalho Chehab 		       unsigned int tolerance)
503*0c0d06caSMauro Carvalho Chehab {
504*0c0d06caSMauro Carvalho Chehab 	mutex_lock(&sp->mutex); do {
505*0c0d06caSMauro Carvalho Chehab 		pvr2_stream_internal_flush(sp);
506*0c0d06caSMauro Carvalho Chehab 		sp->dev = dev;
507*0c0d06caSMauro Carvalho Chehab 		sp->endpoint = endpoint;
508*0c0d06caSMauro Carvalho Chehab 		sp->fail_tolerance = tolerance;
509*0c0d06caSMauro Carvalho Chehab 	} while(0); mutex_unlock(&sp->mutex);
510*0c0d06caSMauro Carvalho Chehab }
511*0c0d06caSMauro Carvalho Chehab 
512*0c0d06caSMauro Carvalho Chehab void pvr2_stream_set_callback(struct pvr2_stream *sp,
513*0c0d06caSMauro Carvalho Chehab 			      pvr2_stream_callback func,
514*0c0d06caSMauro Carvalho Chehab 			      void *data)
515*0c0d06caSMauro Carvalho Chehab {
516*0c0d06caSMauro Carvalho Chehab 	unsigned long irq_flags;
517*0c0d06caSMauro Carvalho Chehab 	mutex_lock(&sp->mutex); do {
518*0c0d06caSMauro Carvalho Chehab 		spin_lock_irqsave(&sp->list_lock,irq_flags);
519*0c0d06caSMauro Carvalho Chehab 		sp->callback_data = data;
520*0c0d06caSMauro Carvalho Chehab 		sp->callback_func = func;
521*0c0d06caSMauro Carvalho Chehab 		spin_unlock_irqrestore(&sp->list_lock,irq_flags);
522*0c0d06caSMauro Carvalho Chehab 	} while(0); mutex_unlock(&sp->mutex);
523*0c0d06caSMauro Carvalho Chehab }
524*0c0d06caSMauro Carvalho Chehab 
525*0c0d06caSMauro Carvalho Chehab void pvr2_stream_get_stats(struct pvr2_stream *sp,
526*0c0d06caSMauro Carvalho Chehab 			   struct pvr2_stream_stats *stats,
527*0c0d06caSMauro Carvalho Chehab 			   int zero_counts)
528*0c0d06caSMauro Carvalho Chehab {
529*0c0d06caSMauro Carvalho Chehab 	unsigned long irq_flags;
530*0c0d06caSMauro Carvalho Chehab 	spin_lock_irqsave(&sp->list_lock,irq_flags);
531*0c0d06caSMauro Carvalho Chehab 	if (stats) {
532*0c0d06caSMauro Carvalho Chehab 		stats->buffers_in_queue = sp->q_count;
533*0c0d06caSMauro Carvalho Chehab 		stats->buffers_in_idle = sp->i_count;
534*0c0d06caSMauro Carvalho Chehab 		stats->buffers_in_ready = sp->r_count;
535*0c0d06caSMauro Carvalho Chehab 		stats->buffers_processed = sp->buffers_processed;
536*0c0d06caSMauro Carvalho Chehab 		stats->buffers_failed = sp->buffers_failed;
537*0c0d06caSMauro Carvalho Chehab 		stats->bytes_processed = sp->bytes_processed;
538*0c0d06caSMauro Carvalho Chehab 	}
539*0c0d06caSMauro Carvalho Chehab 	if (zero_counts) {
540*0c0d06caSMauro Carvalho Chehab 		sp->buffers_processed = 0;
541*0c0d06caSMauro Carvalho Chehab 		sp->buffers_failed = 0;
542*0c0d06caSMauro Carvalho Chehab 		sp->bytes_processed = 0;
543*0c0d06caSMauro Carvalho Chehab 	}
544*0c0d06caSMauro Carvalho Chehab 	spin_unlock_irqrestore(&sp->list_lock,irq_flags);
545*0c0d06caSMauro Carvalho Chehab }
546*0c0d06caSMauro Carvalho Chehab 
547*0c0d06caSMauro Carvalho Chehab /* Query / set the nominal buffer count */
548*0c0d06caSMauro Carvalho Chehab int pvr2_stream_get_buffer_count(struct pvr2_stream *sp)
549*0c0d06caSMauro Carvalho Chehab {
550*0c0d06caSMauro Carvalho Chehab 	return sp->buffer_target_count;
551*0c0d06caSMauro Carvalho Chehab }
552*0c0d06caSMauro Carvalho Chehab 
553*0c0d06caSMauro Carvalho Chehab int pvr2_stream_set_buffer_count(struct pvr2_stream *sp,unsigned int cnt)
554*0c0d06caSMauro Carvalho Chehab {
555*0c0d06caSMauro Carvalho Chehab 	int ret;
556*0c0d06caSMauro Carvalho Chehab 	if (sp->buffer_target_count == cnt) return 0;
557*0c0d06caSMauro Carvalho Chehab 	mutex_lock(&sp->mutex); do {
558*0c0d06caSMauro Carvalho Chehab 		sp->buffer_target_count = cnt;
559*0c0d06caSMauro Carvalho Chehab 		ret = pvr2_stream_achieve_buffer_count(sp);
560*0c0d06caSMauro Carvalho Chehab 	} while(0); mutex_unlock(&sp->mutex);
561*0c0d06caSMauro Carvalho Chehab 	return ret;
562*0c0d06caSMauro Carvalho Chehab }
563*0c0d06caSMauro Carvalho Chehab 
564*0c0d06caSMauro Carvalho Chehab struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *sp)
565*0c0d06caSMauro Carvalho Chehab {
566*0c0d06caSMauro Carvalho Chehab 	struct list_head *lp = sp->idle_list.next;
567*0c0d06caSMauro Carvalho Chehab 	if (lp == &sp->idle_list) return NULL;
568*0c0d06caSMauro Carvalho Chehab 	return list_entry(lp,struct pvr2_buffer,list_overhead);
569*0c0d06caSMauro Carvalho Chehab }
570*0c0d06caSMauro Carvalho Chehab 
571*0c0d06caSMauro Carvalho Chehab struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *sp)
572*0c0d06caSMauro Carvalho Chehab {
573*0c0d06caSMauro Carvalho Chehab 	struct list_head *lp = sp->ready_list.next;
574*0c0d06caSMauro Carvalho Chehab 	if (lp == &sp->ready_list) return NULL;
575*0c0d06caSMauro Carvalho Chehab 	return list_entry(lp,struct pvr2_buffer,list_overhead);
576*0c0d06caSMauro Carvalho Chehab }
577*0c0d06caSMauro Carvalho Chehab 
578*0c0d06caSMauro Carvalho Chehab struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id)
579*0c0d06caSMauro Carvalho Chehab {
580*0c0d06caSMauro Carvalho Chehab 	if (id < 0) return NULL;
581*0c0d06caSMauro Carvalho Chehab 	if (id >= sp->buffer_total_count) return NULL;
582*0c0d06caSMauro Carvalho Chehab 	return sp->buffers[id];
583*0c0d06caSMauro Carvalho Chehab }
584*0c0d06caSMauro Carvalho Chehab 
585*0c0d06caSMauro Carvalho Chehab int pvr2_stream_get_ready_count(struct pvr2_stream *sp)
586*0c0d06caSMauro Carvalho Chehab {
587*0c0d06caSMauro Carvalho Chehab 	return sp->r_count;
588*0c0d06caSMauro Carvalho Chehab }
589*0c0d06caSMauro Carvalho Chehab 
590*0c0d06caSMauro Carvalho Chehab void pvr2_stream_kill(struct pvr2_stream *sp)
591*0c0d06caSMauro Carvalho Chehab {
592*0c0d06caSMauro Carvalho Chehab 	struct pvr2_buffer *bp;
593*0c0d06caSMauro Carvalho Chehab 	mutex_lock(&sp->mutex); do {
594*0c0d06caSMauro Carvalho Chehab 		pvr2_stream_internal_flush(sp);
595*0c0d06caSMauro Carvalho Chehab 		while ((bp = pvr2_stream_get_ready_buffer(sp)) != NULL) {
596*0c0d06caSMauro Carvalho Chehab 			pvr2_buffer_set_idle(bp);
597*0c0d06caSMauro Carvalho Chehab 		}
598*0c0d06caSMauro Carvalho Chehab 		if (sp->buffer_total_count != sp->buffer_target_count) {
599*0c0d06caSMauro Carvalho Chehab 			pvr2_stream_achieve_buffer_count(sp);
600*0c0d06caSMauro Carvalho Chehab 		}
601*0c0d06caSMauro Carvalho Chehab 	} while(0); mutex_unlock(&sp->mutex);
602*0c0d06caSMauro Carvalho Chehab }
603*0c0d06caSMauro Carvalho Chehab 
604*0c0d06caSMauro Carvalho Chehab int pvr2_buffer_queue(struct pvr2_buffer *bp)
605*0c0d06caSMauro Carvalho Chehab {
606*0c0d06caSMauro Carvalho Chehab #undef SEED_BUFFER
607*0c0d06caSMauro Carvalho Chehab #ifdef SEED_BUFFER
608*0c0d06caSMauro Carvalho Chehab 	unsigned int idx;
609*0c0d06caSMauro Carvalho Chehab 	unsigned int val;
610*0c0d06caSMauro Carvalho Chehab #endif
611*0c0d06caSMauro Carvalho Chehab 	int ret = 0;
612*0c0d06caSMauro Carvalho Chehab 	struct pvr2_stream *sp;
613*0c0d06caSMauro Carvalho Chehab 	if (!bp) return -EINVAL;
614*0c0d06caSMauro Carvalho Chehab 	sp = bp->stream;
615*0c0d06caSMauro Carvalho Chehab 	mutex_lock(&sp->mutex); do {
616*0c0d06caSMauro Carvalho Chehab 		pvr2_buffer_wipe(bp);
617*0c0d06caSMauro Carvalho Chehab 		if (!sp->dev) {
618*0c0d06caSMauro Carvalho Chehab 			ret = -EIO;
619*0c0d06caSMauro Carvalho Chehab 			break;
620*0c0d06caSMauro Carvalho Chehab 		}
621*0c0d06caSMauro Carvalho Chehab 		pvr2_buffer_set_queued(bp);
622*0c0d06caSMauro Carvalho Chehab #ifdef SEED_BUFFER
623*0c0d06caSMauro Carvalho Chehab 		for (idx = 0; idx < (bp->max_count) / 4; idx++) {
624*0c0d06caSMauro Carvalho Chehab 			val = bp->id << 24;
625*0c0d06caSMauro Carvalho Chehab 			val |= idx;
626*0c0d06caSMauro Carvalho Chehab 			((unsigned int *)(bp->ptr))[idx] = val;
627*0c0d06caSMauro Carvalho Chehab 		}
628*0c0d06caSMauro Carvalho Chehab #endif
629*0c0d06caSMauro Carvalho Chehab 		bp->status = -EINPROGRESS;
630*0c0d06caSMauro Carvalho Chehab 		usb_fill_bulk_urb(bp->purb,      // struct urb *urb
631*0c0d06caSMauro Carvalho Chehab 				  sp->dev,       // struct usb_device *dev
632*0c0d06caSMauro Carvalho Chehab 				  // endpoint (below)
633*0c0d06caSMauro Carvalho Chehab 				  usb_rcvbulkpipe(sp->dev,sp->endpoint),
634*0c0d06caSMauro Carvalho Chehab 				  bp->ptr,       // void *transfer_buffer
635*0c0d06caSMauro Carvalho Chehab 				  bp->max_count, // int buffer_length
636*0c0d06caSMauro Carvalho Chehab 				  buffer_complete,
637*0c0d06caSMauro Carvalho Chehab 				  bp);
638*0c0d06caSMauro Carvalho Chehab 		usb_submit_urb(bp->purb,GFP_KERNEL);
639*0c0d06caSMauro Carvalho Chehab 	} while(0); mutex_unlock(&sp->mutex);
640*0c0d06caSMauro Carvalho Chehab 	return ret;
641*0c0d06caSMauro Carvalho Chehab }
642*0c0d06caSMauro Carvalho Chehab 
643*0c0d06caSMauro Carvalho Chehab int pvr2_buffer_set_buffer(struct pvr2_buffer *bp,void *ptr,unsigned int cnt)
644*0c0d06caSMauro Carvalho Chehab {
645*0c0d06caSMauro Carvalho Chehab 	int ret = 0;
646*0c0d06caSMauro Carvalho Chehab 	unsigned long irq_flags;
647*0c0d06caSMauro Carvalho Chehab 	struct pvr2_stream *sp;
648*0c0d06caSMauro Carvalho Chehab 	if (!bp) return -EINVAL;
649*0c0d06caSMauro Carvalho Chehab 	sp = bp->stream;
650*0c0d06caSMauro Carvalho Chehab 	mutex_lock(&sp->mutex); do {
651*0c0d06caSMauro Carvalho Chehab 		spin_lock_irqsave(&sp->list_lock,irq_flags);
652*0c0d06caSMauro Carvalho Chehab 		if (bp->state != pvr2_buffer_state_idle) {
653*0c0d06caSMauro Carvalho Chehab 			ret = -EPERM;
654*0c0d06caSMauro Carvalho Chehab 		} else {
655*0c0d06caSMauro Carvalho Chehab 			bp->ptr = ptr;
656*0c0d06caSMauro Carvalho Chehab 			bp->stream->i_bcount -= bp->max_count;
657*0c0d06caSMauro Carvalho Chehab 			bp->max_count = cnt;
658*0c0d06caSMauro Carvalho Chehab 			bp->stream->i_bcount += bp->max_count;
659*0c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_BUF_FLOW,
660*0c0d06caSMauro Carvalho Chehab 				   "/*---TRACE_FLOW---*/ bufferPool    "
661*0c0d06caSMauro Carvalho Chehab 				   " %8s cap cap=%07d cnt=%02d",
662*0c0d06caSMauro Carvalho Chehab 				   pvr2_buffer_state_decode(
663*0c0d06caSMauro Carvalho Chehab 					   pvr2_buffer_state_idle),
664*0c0d06caSMauro Carvalho Chehab 				   bp->stream->i_bcount,bp->stream->i_count);
665*0c0d06caSMauro Carvalho Chehab 		}
666*0c0d06caSMauro Carvalho Chehab 		spin_unlock_irqrestore(&sp->list_lock,irq_flags);
667*0c0d06caSMauro Carvalho Chehab 	} while(0); mutex_unlock(&sp->mutex);
668*0c0d06caSMauro Carvalho Chehab 	return ret;
669*0c0d06caSMauro Carvalho Chehab }
670*0c0d06caSMauro Carvalho Chehab 
671*0c0d06caSMauro Carvalho Chehab unsigned int pvr2_buffer_get_count(struct pvr2_buffer *bp)
672*0c0d06caSMauro Carvalho Chehab {
673*0c0d06caSMauro Carvalho Chehab 	return bp->used_count;
674*0c0d06caSMauro Carvalho Chehab }
675*0c0d06caSMauro Carvalho Chehab 
676*0c0d06caSMauro Carvalho Chehab int pvr2_buffer_get_status(struct pvr2_buffer *bp)
677*0c0d06caSMauro Carvalho Chehab {
678*0c0d06caSMauro Carvalho Chehab 	return bp->status;
679*0c0d06caSMauro Carvalho Chehab }
680*0c0d06caSMauro Carvalho Chehab 
681*0c0d06caSMauro Carvalho Chehab int pvr2_buffer_get_id(struct pvr2_buffer *bp)
682*0c0d06caSMauro Carvalho Chehab {
683*0c0d06caSMauro Carvalho Chehab 	return bp->id;
684*0c0d06caSMauro Carvalho Chehab }
685*0c0d06caSMauro Carvalho Chehab 
686*0c0d06caSMauro Carvalho Chehab 
687*0c0d06caSMauro Carvalho Chehab /*
688*0c0d06caSMauro Carvalho Chehab   Stuff for Emacs to see, in order to encourage consistent editing style:
689*0c0d06caSMauro Carvalho Chehab   *** Local Variables: ***
690*0c0d06caSMauro Carvalho Chehab   *** mode: c ***
691*0c0d06caSMauro Carvalho Chehab   *** fill-column: 75 ***
692*0c0d06caSMauro Carvalho Chehab   *** tab-width: 8 ***
693*0c0d06caSMauro Carvalho Chehab   *** c-basic-offset: 8 ***
694*0c0d06caSMauro Carvalho Chehab   *** End: ***
695*0c0d06caSMauro Carvalho Chehab   */
696