xref: /plan9/sys/src/9/pc/usbohci.c (revision 906943f9f6b8411972abb5e3a03ed19f74be7ccc)
16a5dc222SDavid du Colombier /*
2*906943f9SDavid du Colombier  * USB Open Host Controller Interface (Ohci) driver
3*906943f9SDavid du Colombier  *
4*906943f9SDavid du Colombier  * BUGS:
5*906943f9SDavid du Colombier  * - Missing isochronous input streams.
6*906943f9SDavid du Colombier  * - Too many delays and ilocks.
7*906943f9SDavid du Colombier  * - bandwidth admission control must be done per-frame.
8*906943f9SDavid du Colombier  * - Buffering could be handled like in uhci, to avoid
9*906943f9SDavid du Colombier  * needed block allocation and avoid allocs for small Tds.
10*906943f9SDavid du Colombier  * - must warn of power overruns.
116a5dc222SDavid du Colombier  */
12*906943f9SDavid du Colombier 
136a5dc222SDavid du Colombier #include	"u.h"
146a5dc222SDavid du Colombier #include	"../port/lib.h"
156a5dc222SDavid du Colombier #include	"mem.h"
166a5dc222SDavid du Colombier #include	"dat.h"
176a5dc222SDavid du Colombier #include	"fns.h"
186a5dc222SDavid du Colombier #include	"io.h"
196a5dc222SDavid du Colombier #include	"../port/error.h"
206a5dc222SDavid du Colombier 
216a5dc222SDavid du Colombier #include	"usb.h"
226a5dc222SDavid du Colombier 
236a5dc222SDavid du Colombier typedef struct Ctlr Ctlr;
24*906943f9SDavid du Colombier typedef struct Qtree Qtree;
25*906943f9SDavid du Colombier typedef struct Epx Epx;
26*906943f9SDavid du Colombier typedef struct Td Td;
27*906943f9SDavid du Colombier typedef struct Hcca Hcca;
28*906943f9SDavid du Colombier typedef struct Ohci Ohci;
29*906943f9SDavid du Colombier typedef struct Qio Qio;
30*906943f9SDavid du Colombier typedef struct Ed Ed;
31*906943f9SDavid du Colombier typedef struct Ctlio Ctlio;
32*906943f9SDavid du Colombier typedef struct Isoio Isoio;
33*906943f9SDavid du Colombier typedef struct Tdpool Tdpool;
34*906943f9SDavid du Colombier typedef struct Edpool Edpool;
356a5dc222SDavid du Colombier 
36*906943f9SDavid du Colombier enum
376a5dc222SDavid du Colombier {
38*906943f9SDavid du Colombier 	Incr		= 64,		/* for Td and Ed pools */
396a5dc222SDavid du Colombier 
40*906943f9SDavid du Colombier 	Align		= 0x20,		/* OHCI only requires 0x10 */
41*906943f9SDavid du Colombier 					/* use always a power of 2 */
426a5dc222SDavid du Colombier 
43*906943f9SDavid du Colombier 	Ctltmout	= 2000,		/* timeout for a ctl. request (ms) */
44*906943f9SDavid du Colombier 	Bulktmout	= 2000,		/* timeout for a bulk xfer. (ms) */
45*906943f9SDavid du Colombier 	Isotmout	= 2000,		/* timeout for an iso. request (ms) */
46*906943f9SDavid du Colombier 	Abortdelay	= 1,		/* delay after cancelling Tds (ms) */
47*906943f9SDavid du Colombier 	Tdatomic		= 8,		/* max nb. of Tds per bulk I/O op. */
48*906943f9SDavid du Colombier 	Enabledelay	= 100,		/* waiting for a port to enable */
496a5dc222SDavid du Colombier 
506a5dc222SDavid du Colombier 
51*906943f9SDavid du Colombier 	/* Queue states (software) */
52*906943f9SDavid du Colombier 	Qidle		= 0,
53*906943f9SDavid du Colombier 	Qinstall,
54*906943f9SDavid du Colombier 	Qrun,
55*906943f9SDavid du Colombier 	Qdone,
56*906943f9SDavid du Colombier 	Qclose,
57*906943f9SDavid du Colombier 	Qfree,
586a5dc222SDavid du Colombier 
59*906943f9SDavid du Colombier 	/* Ed control bits */
60*906943f9SDavid du Colombier 	Edmpsmask	= 0x7ff,	/* max packet size */
61*906943f9SDavid du Colombier 	Edmpsshift	= 16,
62*906943f9SDavid du Colombier 	Edlow		= 1 << 13,	/* low speed */
63*906943f9SDavid du Colombier 	Edskip		= 1 << 14,	/* skip this ed */
64*906943f9SDavid du Colombier 	Ediso		= 1 << 15,	/* iso Tds used */
65*906943f9SDavid du Colombier 	Edtddir		= 0,		/* get dir from td */
66*906943f9SDavid du Colombier 	Edin		= 2 << 11,	/* direction in */
67*906943f9SDavid du Colombier 	Edout		= 1 << 11,	/* direction out */
68*906943f9SDavid du Colombier 	Eddirmask	= 3 << 11,	/* direction bits */
69*906943f9SDavid du Colombier 	Edhalt		= 1,		/* halted (in head ptr) */
70*906943f9SDavid du Colombier 	Edtoggle	= 2,		/* toggle (in head ptr) 1 == data1 */
716a5dc222SDavid du Colombier 
72*906943f9SDavid du Colombier 	/* Td control bits */
73*906943f9SDavid du Colombier 	Tdround		= 1<<18,	/* (rounding) short packets ok */
74*906943f9SDavid du Colombier 	Tdtoksetup	= 0<<19,	/* setup packet */
75*906943f9SDavid du Colombier 	Tdtokin		= 2<<19,	/* in packet */
76*906943f9SDavid du Colombier 	Tdtokout	= 1<<19,	/* out packet */
77*906943f9SDavid du Colombier 	Tdtokmask	= 3<<19,	/* in/out/setup bits */
78*906943f9SDavid du Colombier 	Tdnoioc		= 7<<21,	/* intr. cnt. value for no interrupt */
79*906943f9SDavid du Colombier 	Tdusetog	= 1<<25,	/* use toggle from Td (1) or Ed (0) */
80*906943f9SDavid du Colombier 	Tddata1		= 1<<24,	/* data toggle (1 == data1) */
81*906943f9SDavid du Colombier 	Tddata0		= 0<<24,
82*906943f9SDavid du Colombier 	Tdfcmask	= 7,		/* frame count (iso) */
83*906943f9SDavid du Colombier 	Tdfcshift	= 24,
84*906943f9SDavid du Colombier 	Tdsfmask	= 0xFFFF,	/* starting frame (iso) */
85*906943f9SDavid du Colombier 	Tderrmask	= 3,		/* error counter */
86*906943f9SDavid du Colombier 	Tderrshift	= 26,
87*906943f9SDavid du Colombier 	Tdccmask	= 0xf,		/* condition code (status) */
88*906943f9SDavid du Colombier 	Tdccshift	= 28,
89*906943f9SDavid du Colombier 	Tdiccmask	= 0xf,		/* condition code (iso, offsets) */
90*906943f9SDavid du Colombier 	Tdiccshift	= 12,
916a5dc222SDavid du Colombier 
92*906943f9SDavid du Colombier 	Ntdframes	= 0x10000,	/* # of different iso frame numbers */
936a5dc222SDavid du Colombier 
94*906943f9SDavid du Colombier 	/* Td errors (condition code) */
95*906943f9SDavid du Colombier 	Tdok		= 0,
96*906943f9SDavid du Colombier 	Tdcrc		= 1,
97*906943f9SDavid du Colombier 	Tdbitstuff	= 2,
98*906943f9SDavid du Colombier 	Tdbadtog	= 3,
99*906943f9SDavid du Colombier 	Tdstalled	= 4,
100*906943f9SDavid du Colombier 	Tdtmout		= 5,
101*906943f9SDavid du Colombier 	Tdpidchk	= 6,
102*906943f9SDavid du Colombier 	Tdbadpid	= 7,
103*906943f9SDavid du Colombier 	Tddataovr	= 8,
104*906943f9SDavid du Colombier 	Tddataund	= 9,
105*906943f9SDavid du Colombier 	Tdbufovr	= 0xC,
106*906943f9SDavid du Colombier 	Tdbufund	= 0xD,
107*906943f9SDavid du Colombier 	Tdnotacc	= 0xE,
1086a5dc222SDavid du Colombier 
109*906943f9SDavid du Colombier 	/* control register */
110*906943f9SDavid du Colombier 	Cple		= 0x04,		/* periodic list enable */
111*906943f9SDavid du Colombier 	Cie		= 0x08,		/* iso. list enable */
112*906943f9SDavid du Colombier 	Ccle		= 0x10,		/* ctl list enable */
113*906943f9SDavid du Colombier 	Cble		= 0x20,		/* bulk list enable */
114*906943f9SDavid du Colombier 	Cfsmask		= 3 << 6,	/* functional state... */
115*906943f9SDavid du Colombier 	Cfsreset	= 0 << 6,
116*906943f9SDavid du Colombier 	Cfsresume	= 1 << 6,
117*906943f9SDavid du Colombier 	Cfsoper		= 2 << 6,
118*906943f9SDavid du Colombier 	Cfssuspend	= 3 << 6,
1196a5dc222SDavid du Colombier 
120*906943f9SDavid du Colombier 	/* command status */
121*906943f9SDavid du Colombier 	Sblf =	1 << 2,			/* bulk list (load) flag */
122*906943f9SDavid du Colombier 	Sclf =	1 << 1,			/* control list (load) flag */
123*906943f9SDavid du Colombier 	Shcr =	1 << 0,			/* host controller reset */
1246a5dc222SDavid du Colombier 
125*906943f9SDavid du Colombier 	/* intr enable */
1266a5dc222SDavid du Colombier 	Mie =	1 << 31,
1276a5dc222SDavid du Colombier 	Oc =	1 << 30,
1286a5dc222SDavid du Colombier 	Rhsc =	1 << 6,
1296a5dc222SDavid du Colombier 	Fno =	1 << 5,
1306a5dc222SDavid du Colombier 	Ue =	1 << 4,
1316a5dc222SDavid du Colombier 	Rd =	1 << 3,
1326a5dc222SDavid du Colombier 	Sf =	1 << 2,
1336a5dc222SDavid du Colombier 	Wdh =	1 << 1,
1346a5dc222SDavid du Colombier 	So =	1 << 0,
135*906943f9SDavid du Colombier 
136*906943f9SDavid du Colombier 	Fmaxpktmask = 0x7fff,
137*906943f9SDavid du Colombier 	Fmaxpktshift = 16,
1386a5dc222SDavid du Colombier 	HcRhDescA_POTPGT_MASK =	0xff << 24,
1396a5dc222SDavid du Colombier 	HcRhDescA_POTPGT_SHIFT =	24,
140*906943f9SDavid du Colombier 
141*906943f9SDavid du Colombier 	/* Rh status */
1426a5dc222SDavid du Colombier 	Lps =	1 << 0,
1436a5dc222SDavid du Colombier 	Cgp =	1 << 0,
1446a5dc222SDavid du Colombier 	Oci =	1 << 1,
145*906943f9SDavid du Colombier 	Psm =	1 << 8,
146*906943f9SDavid du Colombier 	Nps =	1 << 9,
1476a5dc222SDavid du Colombier 	Drwe =	1 << 15,
1486a5dc222SDavid du Colombier 	Srwe =	1 << 15,
149*906943f9SDavid du Colombier 	Lpsc =	1 << 16,
1506a5dc222SDavid du Colombier 	Sgp =	1 << 16,
1516a5dc222SDavid du Colombier 	Ccic =	1 << 17,
1526a5dc222SDavid du Colombier 	Crwe =	1 << 31,
1536a5dc222SDavid du Colombier 
154*906943f9SDavid du Colombier 	/* port status */
155*906943f9SDavid du Colombier 	Ccs =	0x00001,	/* current connect status */
156*906943f9SDavid du Colombier 	Pes =	0x00002,	/* port enable status */
157*906943f9SDavid du Colombier 	Pss =	0x00004,	/* port suspend status */
158*906943f9SDavid du Colombier 	Poci =	0x00008,	/* over current indicator */
159*906943f9SDavid du Colombier 	Prs =	0x00010,	/* port reset status */
160*906943f9SDavid du Colombier 	Pps =	0x00100,	/* port power status */
161*906943f9SDavid du Colombier 	Lsda =	0x00200,	/* low speed device attached */
162*906943f9SDavid du Colombier 	Csc =	0x10000,	/* connect status change */
163*906943f9SDavid du Colombier 	Pesc =	0x20000,	/* enable status change */
164*906943f9SDavid du Colombier 	Pssc =	0x40000,	/* suspend status change */
165*906943f9SDavid du Colombier 	Ocic =	0x80000,	/* over current ind. change */
166*906943f9SDavid du Colombier 	Prsc =	0x100000,	/* reset status change */
167*906943f9SDavid du Colombier 
168*906943f9SDavid du Colombier 	/* port status write bits */
169*906943f9SDavid du Colombier 	Cpe =	0x001,		/* clear port enable */
170*906943f9SDavid du Colombier 	Spe =	0x002,		/* set port enable */
171*906943f9SDavid du Colombier 	Spr =	0x010,		/* set port reset */
172*906943f9SDavid du Colombier 	Spp =	0x100,		/* set port power */
173*906943f9SDavid du Colombier 	Cpp =	0x200,		/* clear port power */
174*906943f9SDavid du Colombier 
1756a5dc222SDavid du Colombier };
1766a5dc222SDavid du Colombier 
177*906943f9SDavid du Colombier /*
178*906943f9SDavid du Colombier  * Endpoint descriptor. (first 4 words used by hardware)
179*906943f9SDavid du Colombier  */
180*906943f9SDavid du Colombier struct Ed {
181*906943f9SDavid du Colombier 	ulong	ctrl;
182*906943f9SDavid du Colombier 	ulong	tail;		/* transfer descriptor */
183*906943f9SDavid du Colombier 	ulong	head;
184*906943f9SDavid du Colombier 	ulong	nexted;
185*906943f9SDavid du Colombier 
186*906943f9SDavid du Colombier 	Ed*	next;		/* sw; in free list or next in list */
187*906943f9SDavid du Colombier 	Td*	tds;		/* in use by current xfer; all for iso */
188*906943f9SDavid du Colombier 	Ep*	ep;		/* debug/align */
189*906943f9SDavid du Colombier 	Ed*	inext;		/* debug/align (dump interrupt eds). */
1906a5dc222SDavid du Colombier };
1916a5dc222SDavid du Colombier 
192*906943f9SDavid du Colombier /*
193*906943f9SDavid du Colombier  * Endpoint I/O state (software), per direction.
194*906943f9SDavid du Colombier  */
195*906943f9SDavid du Colombier struct Qio
196*906943f9SDavid du Colombier {
197*906943f9SDavid du Colombier 	QLock;			/* for the entire I/O process */
198*906943f9SDavid du Colombier 	Rendez;			/* wait for completion */
199*906943f9SDavid du Colombier 	Ed*	ed;		/* to place Tds on it */
200*906943f9SDavid du Colombier 	int	sched;		/* queue number (intr/iso) */
201*906943f9SDavid du Colombier 	int	toggle;		/* Tddata0/Tddata1 */
202*906943f9SDavid du Colombier 	ulong	usbid;		/* device/endpoint address */
203*906943f9SDavid du Colombier 	int	tok;		/* Tdsetup, Tdtokin, Tdtokout */
204*906943f9SDavid du Colombier 	long	iotime;		/* last I/O time; to hold interrupt polls */
205*906943f9SDavid du Colombier 	int	debug;		/* for the endpoint */
206*906943f9SDavid du Colombier 	char*	err;		/* error status */
207*906943f9SDavid du Colombier 	int	state;		/* Qidle -> Qinstall -> Qrun -> Qdone | Qclose */
208*906943f9SDavid du Colombier 	long	bw;		/* load (intr/iso) */
2096a5dc222SDavid du Colombier };
2106a5dc222SDavid du Colombier 
211*906943f9SDavid du Colombier struct Ctlio
212*906943f9SDavid du Colombier {
213*906943f9SDavid du Colombier 	Qio;			/* single Ed for all transfers */
214*906943f9SDavid du Colombier 	uchar*	data;		/* read from last ctl req. */
215*906943f9SDavid du Colombier 	int	ndata;		/* number of bytes read */
2166a5dc222SDavid du Colombier 
217*906943f9SDavid du Colombier };
2186a5dc222SDavid du Colombier 
219*906943f9SDavid du Colombier struct Isoio
220*906943f9SDavid du Colombier {
221*906943f9SDavid du Colombier 	Qio;
222*906943f9SDavid du Colombier 	int	nframes;	/* number of frames for a full second */
223*906943f9SDavid du Colombier 	Td*	atds;		/* Tds avail for further I/O */
224*906943f9SDavid du Colombier 	int	navail;		/* number of avail Tds */
225*906943f9SDavid du Colombier 	ulong	frno;		/* next frame number avail for I/O */
226*906943f9SDavid du Colombier 	ulong	left;		/* remainder after rounding Hz to samples/ms */
227*906943f9SDavid du Colombier 	int	nerrs;		/* consecutive errors on iso I/O */
228*906943f9SDavid du Colombier };
229*906943f9SDavid du Colombier 
230*906943f9SDavid du Colombier /*
231*906943f9SDavid du Colombier  * Transfer descriptor. Size must be multiple of 32
232*906943f9SDavid du Colombier  * First block is used by hardware (aligned to 32).
233*906943f9SDavid du Colombier  */
234*906943f9SDavid du Colombier struct Td
235*906943f9SDavid du Colombier {
236*906943f9SDavid du Colombier 	ulong	ctrl;
237*906943f9SDavid du Colombier 	ulong	cbp;
238*906943f9SDavid du Colombier 	ulong	nexttd;
239*906943f9SDavid du Colombier 	ulong	be;
240*906943f9SDavid du Colombier 	ushort	offsets[8];	/* used by Iso Tds only */
241*906943f9SDavid du Colombier 
242*906943f9SDavid du Colombier 	Td*	next;		/* in free or Ed tds list */
243*906943f9SDavid du Colombier 	Td*	anext;		/* in avail td list (iso) */
244*906943f9SDavid du Colombier 	Ep*	ep;		/* using this Td for I/O */
245*906943f9SDavid du Colombier 	Qio*	io;		/* using this Td for I/O */
246*906943f9SDavid du Colombier 	Block*	bp;		/* data for this Td */
247*906943f9SDavid du Colombier 	ulong	nbytes;		/* bytes in this Td */
248*906943f9SDavid du Colombier 	ulong	cbp0;		/* initial value for cbp */
249*906943f9SDavid du Colombier 	ulong	last;		/* true for last Td in Qio */
250*906943f9SDavid du Colombier };
251*906943f9SDavid du Colombier 
252*906943f9SDavid du Colombier /*
253*906943f9SDavid du Colombier  * Host controller communication area (hardware)
254*906943f9SDavid du Colombier  */
255*906943f9SDavid du Colombier struct Hcca
256*906943f9SDavid du Colombier {
257*906943f9SDavid du Colombier 	ulong	intrtable[32];
258*906943f9SDavid du Colombier 	ushort	framenumber;
259*906943f9SDavid du Colombier 	ushort	pad1;
260*906943f9SDavid du Colombier 	ulong	donehead;
261*906943f9SDavid du Colombier 	uchar	reserved[116];
262*906943f9SDavid du Colombier };
263*906943f9SDavid du Colombier 
264*906943f9SDavid du Colombier /*
265*906943f9SDavid du Colombier  * I/O registers
266*906943f9SDavid du Colombier  */
267*906943f9SDavid du Colombier struct Ohci
268*906943f9SDavid du Colombier {
269*906943f9SDavid du Colombier 	/* control and status group */
270*906943f9SDavid du Colombier 	ulong	revision;		/*00*/
271*906943f9SDavid du Colombier 	ulong	control;		/*04*/
272*906943f9SDavid du Colombier 	ulong	cmdsts;			/*08*/
273*906943f9SDavid du Colombier 	ulong	intrsts;			/*0c*/
274*906943f9SDavid du Colombier 	ulong	intrenable;		/*10*/
275*906943f9SDavid du Colombier 	ulong	intrdisable;		/*14*/
276*906943f9SDavid du Colombier 
277*906943f9SDavid du Colombier 	/* memory pointer group */
278*906943f9SDavid du Colombier 	ulong	hcca;			/*18*/
279*906943f9SDavid du Colombier 	ulong	periodcurred;		/*1c*/
280*906943f9SDavid du Colombier 	ulong	ctlheaded;		/*20*/
281*906943f9SDavid du Colombier 	ulong	ctlcurred;		/*24*/
282*906943f9SDavid du Colombier 	ulong	bulkheaded;		/*28*/
283*906943f9SDavid du Colombier 	ulong	bulkcurred;		/*2c*/
284*906943f9SDavid du Colombier 	ulong	donehead;		/*30*/
285*906943f9SDavid du Colombier 
286*906943f9SDavid du Colombier 	/* frame counter group */
287*906943f9SDavid du Colombier 	ulong	fminterval;		/*34*/
288*906943f9SDavid du Colombier 	ulong	fmremaining;		/*38*/
289*906943f9SDavid du Colombier 	ulong	fmnumber;		/*3c*/
290*906943f9SDavid du Colombier 	ulong	periodicstart;		/*40*/
291*906943f9SDavid du Colombier 	ulong	lsthreshold;		/*44*/
292*906943f9SDavid du Colombier 
293*906943f9SDavid du Colombier 	/* root hub group */
294*906943f9SDavid du Colombier 	ulong	rhdesca;		/*48*/
295*906943f9SDavid du Colombier 	ulong	rhdescb;		/*4c*/
296*906943f9SDavid du Colombier 	ulong	rhsts;			/*50*/
297*906943f9SDavid du Colombier 	ulong	rhportsts[15];		/*54*/
298*906943f9SDavid du Colombier 	ulong	pad25[20];		/*90*/
299*906943f9SDavid du Colombier 
300*906943f9SDavid du Colombier 	/* unknown */
301*906943f9SDavid du Colombier 	ulong	hostueaddr;		/*e0*/
302*906943f9SDavid du Colombier 	ulong	hostuests;		/*e4*/
303*906943f9SDavid du Colombier 	ulong	hosttimeoutctrl;		/*e8*/
304*906943f9SDavid du Colombier 	ulong	pad59;			/*ec*/
305*906943f9SDavid du Colombier 	ulong	pad60;			/*f0*/
306*906943f9SDavid du Colombier 	ulong	hostrevision;		/*f4*/
307*906943f9SDavid du Colombier 	ulong	pad62[2];
308*906943f9SDavid du Colombier 					/*100*/
309*906943f9SDavid du Colombier };
310*906943f9SDavid du Colombier 
311*906943f9SDavid du Colombier /*
312*906943f9SDavid du Colombier  * Endpoint tree (software)
313*906943f9SDavid du Colombier  */
314*906943f9SDavid du Colombier struct Qtree
315*906943f9SDavid du Colombier {
316*906943f9SDavid du Colombier 	int	nel;
317*906943f9SDavid du Colombier 	int	depth;
318*906943f9SDavid du Colombier 	ulong*	bw;
319*906943f9SDavid du Colombier 	Ed**	root;
320*906943f9SDavid du Colombier };
321*906943f9SDavid du Colombier 
322*906943f9SDavid du Colombier struct Tdpool
323*906943f9SDavid du Colombier {
324*906943f9SDavid du Colombier 	Lock;
325*906943f9SDavid du Colombier 	Td*	free;
326*906943f9SDavid du Colombier 	int	nalloc;
327*906943f9SDavid du Colombier 	int	ninuse;
328*906943f9SDavid du Colombier 	int	nfree;
329*906943f9SDavid du Colombier };
330*906943f9SDavid du Colombier 
331*906943f9SDavid du Colombier struct Edpool
332*906943f9SDavid du Colombier {
333*906943f9SDavid du Colombier 	Lock;
334*906943f9SDavid du Colombier 	Ed*	free;
335*906943f9SDavid du Colombier 	int	nalloc;
336*906943f9SDavid du Colombier 	int	ninuse;
337*906943f9SDavid du Colombier 	int	nfree;
338*906943f9SDavid du Colombier };
339*906943f9SDavid du Colombier 
340*906943f9SDavid du Colombier struct Ctlr
341*906943f9SDavid du Colombier {
342*906943f9SDavid du Colombier 	Lock;			/* for ilock; lists and basic ctlr I/O */
343*906943f9SDavid du Colombier 	QLock	resetl;		/* lock controller during USB reset */
344*906943f9SDavid du Colombier 	Pcidev*	pcidev;
345*906943f9SDavid du Colombier 	int	active;
346*906943f9SDavid du Colombier 	Ctlr*	next;
347*906943f9SDavid du Colombier 	int	nports;
348*906943f9SDavid du Colombier 
349*906943f9SDavid du Colombier 	Ohci*	ohci;		/* base I/O address */
350*906943f9SDavid du Colombier 	Hcca*	hcca;		/* intr/done Td lists (used by hardware) */
351*906943f9SDavid du Colombier 	int	overrun;	/* sched. overrun */
352*906943f9SDavid du Colombier 	Ed*	intrhd;		/* list of intr. eds in tree */
353*906943f9SDavid du Colombier 	Qtree*	tree;		/* tree for t Ep i/o */
354*906943f9SDavid du Colombier 	int	ntree;		/* number of dummy Eds in tree */
355*906943f9SDavid du Colombier };
356*906943f9SDavid du Colombier 
357*906943f9SDavid du Colombier #define dqprint		if(debug || io && io->debug)print
358*906943f9SDavid du Colombier #define ddqprint		if(debug>1 || (io && io->debug>1))print
359*906943f9SDavid du Colombier #define diprint		if(debug || iso && iso->debug)print
360*906943f9SDavid du Colombier #define ddiprint		if(debug>1 || (iso && iso->debug>1))print
361*906943f9SDavid du Colombier #define TRUNC(x, sz)	((x) & ((sz)-1))
362*906943f9SDavid du Colombier 
363*906943f9SDavid du Colombier static int ohciinterrupts[Nttypes];
364*906943f9SDavid du Colombier static char* iosname[] = { "idle", "install", "run", "done", "close", "FREE" };
365*906943f9SDavid du Colombier 
366*906943f9SDavid du Colombier static int debug;
367*906943f9SDavid du Colombier static Edpool edpool;
368*906943f9SDavid du Colombier static Tdpool tdpool;
369*906943f9SDavid du Colombier static Ctlr* ctlrs[Nhcis];
370*906943f9SDavid du Colombier 
3716a5dc222SDavid du Colombier static	char	EnotWritten[] = "usb write unfinished";
3726a5dc222SDavid du Colombier static	char	EnotRead[] = "usb read unfinished";
3736a5dc222SDavid du Colombier static	char	Eunderrun[] = "usb endpoint underrun";
3746a5dc222SDavid du Colombier 
3756a5dc222SDavid du Colombier static	QLock	usbhstate;	/* protects name space state */
3766a5dc222SDavid du Colombier 
377*906943f9SDavid du Colombier static int	schedendpt(Ctlr *ub, Ep *ep);
378*906943f9SDavid du Colombier static void	unschedendpt(Ctlr *ub, Ep *ep);
379*906943f9SDavid du Colombier static long	qtd(Ctlr*, Ep*, int, Block*, uchar*, uchar*, int, ulong);
3806a5dc222SDavid du Colombier 
381*906943f9SDavid du Colombier static char* errmsgs[] =
3826a5dc222SDavid du Colombier {
383*906943f9SDavid du Colombier [Tdcrc]		"crc error",
384*906943f9SDavid du Colombier [Tdbitstuff]	"bit stuffing error",
385*906943f9SDavid du Colombier [Tdbadtog]	"bad toggle",
386*906943f9SDavid du Colombier [Tdstalled]	Estalled,
387*906943f9SDavid du Colombier [Tdtmout]	"timeout error",
388*906943f9SDavid du Colombier [Tdpidchk]	"pid check error",
389*906943f9SDavid du Colombier [Tdbadpid]	"bad pid",
390*906943f9SDavid du Colombier [Tddataovr]	"data overrun",
391*906943f9SDavid du Colombier [Tddataund]	"data underrun",
392*906943f9SDavid du Colombier [Tdbufovr]	"buffer overrun",
393*906943f9SDavid du Colombier [Tdbufund]	"buffer underrun",
394*906943f9SDavid du Colombier [Tdnotacc]	"not accessed"
395*906943f9SDavid du Colombier };
3966a5dc222SDavid du Colombier 
397*906943f9SDavid du Colombier static void*
398*906943f9SDavid du Colombier pa2ptr(ulong pa)
3996a5dc222SDavid du Colombier {
400*906943f9SDavid du Colombier 	if(pa == 0)
4016a5dc222SDavid du Colombier 		return nil;
4026a5dc222SDavid du Colombier 	else
403*906943f9SDavid du Colombier 		return KADDR(pa);
4046a5dc222SDavid du Colombier }
4056a5dc222SDavid du Colombier 
406*906943f9SDavid du Colombier static ulong
407*906943f9SDavid du Colombier ptr2pa(void *p)
4086a5dc222SDavid du Colombier {
409*906943f9SDavid du Colombier 	if(p == nil)
410*906943f9SDavid du Colombier 		return 0;
411*906943f9SDavid du Colombier 	else
412*906943f9SDavid du Colombier 		return PADDR(p);
4136a5dc222SDavid du Colombier }
4146a5dc222SDavid du Colombier 
4156a5dc222SDavid du Colombier static void
4166a5dc222SDavid du Colombier waitSOF(Ctlr *ub)
4176a5dc222SDavid du Colombier {
418*906943f9SDavid du Colombier 	int frame = ub->hcca->framenumber & 0x3f;
4196a5dc222SDavid du Colombier 
4206a5dc222SDavid du Colombier 	do {
4216a5dc222SDavid du Colombier 		delay(2);
422*906943f9SDavid du Colombier 	} while(frame == (ub->hcca->framenumber & 0x3f));
423*906943f9SDavid du Colombier }
424*906943f9SDavid du Colombier 
425*906943f9SDavid du Colombier static char*
426*906943f9SDavid du Colombier errmsg(int err)
427*906943f9SDavid du Colombier {
428*906943f9SDavid du Colombier 
429*906943f9SDavid du Colombier 	if(err < nelem(errmsgs))
430*906943f9SDavid du Colombier 		return errmsgs[err];
431*906943f9SDavid du Colombier 	return nil;
432*906943f9SDavid du Colombier }
433*906943f9SDavid du Colombier 
434*906943f9SDavid du Colombier static Ed*
435*906943f9SDavid du Colombier ctlhd(Ctlr *ctlr)
436*906943f9SDavid du Colombier {
437*906943f9SDavid du Colombier 	return pa2ptr(ctlr->ohci->ctlheaded);
438*906943f9SDavid du Colombier }
439*906943f9SDavid du Colombier 
440*906943f9SDavid du Colombier static Ed*
441*906943f9SDavid du Colombier bulkhd(Ctlr *ctlr)
442*906943f9SDavid du Colombier {
443*906943f9SDavid du Colombier 	return pa2ptr(ctlr->ohci->bulkheaded);
4446a5dc222SDavid du Colombier }
4456a5dc222SDavid du Colombier 
4466a5dc222SDavid du Colombier static void
447*906943f9SDavid du Colombier edlinked(Ed *ed, Ed *next)
4486a5dc222SDavid du Colombier {
4496a5dc222SDavid du Colombier 	if(ed == nil)
450*906943f9SDavid du Colombier 		print("edlinked: nil ed: pc %#p\n", getcallerpc(&ed));
451*906943f9SDavid du Colombier 	ed->nexted = ptr2pa(next);
452*906943f9SDavid du Colombier 	ed->next = next;
4536a5dc222SDavid du Colombier }
4546a5dc222SDavid du Colombier 
4556a5dc222SDavid du Colombier static void
456*906943f9SDavid du Colombier setctlhd(Ctlr *ctlr, Ed *ed)
4576a5dc222SDavid du Colombier {
458*906943f9SDavid du Colombier 	ctlr->ohci->ctlheaded = ptr2pa(ed);
459*906943f9SDavid du Colombier 	if(ed != nil)
460*906943f9SDavid du Colombier 		ctlr->ohci->cmdsts |= Sclf;	/* reload it on next pass */
4616a5dc222SDavid du Colombier }
4626a5dc222SDavid du Colombier 
4636a5dc222SDavid du Colombier static void
464*906943f9SDavid du Colombier setbulkhd(Ctlr *ctlr, Ed *ed)
4656a5dc222SDavid du Colombier {
466*906943f9SDavid du Colombier 	ctlr->ohci->bulkheaded = ptr2pa(ed);
467*906943f9SDavid du Colombier 	if(ed != nil)
468*906943f9SDavid du Colombier 		ctlr->ohci->cmdsts |= Sblf;	/* reload it on next pass */
469*906943f9SDavid du Colombier }
4706a5dc222SDavid du Colombier 
471*906943f9SDavid du Colombier static void
472*906943f9SDavid du Colombier unlinkctl(Ctlr *ctlr, Ed *ed)
473*906943f9SDavid du Colombier {
474*906943f9SDavid du Colombier 	Ed *this, *prev, *next;
475*906943f9SDavid du Colombier 
476*906943f9SDavid du Colombier 	ctlr->ohci->control &= ~Ccle;
477*906943f9SDavid du Colombier 	waitSOF(ctlr);
478*906943f9SDavid du Colombier 	this = ctlhd(ctlr);
479*906943f9SDavid du Colombier 	ctlr->ohci->ctlcurred = 0;
4806a5dc222SDavid du Colombier 	prev = nil;
4816a5dc222SDavid du Colombier 	while(this != nil && this != ed){
4826a5dc222SDavid du Colombier 		prev = this;
483*906943f9SDavid du Colombier 		this = this->next;
4846a5dc222SDavid du Colombier 	}
4856a5dc222SDavid du Colombier 	if(this == nil){
486*906943f9SDavid du Colombier 		print("unlinkctl: not found\n");
4876a5dc222SDavid du Colombier 		return;
4886a5dc222SDavid du Colombier 	}
489*906943f9SDavid du Colombier 	next = this->next;
4906a5dc222SDavid du Colombier 	if(prev == nil)
491*906943f9SDavid du Colombier 		setctlhd(ctlr, next);
4926a5dc222SDavid du Colombier 	else
493*906943f9SDavid du Colombier 		edlinked(prev, next);
494*906943f9SDavid du Colombier 	ctlr->ohci->control |= Ccle;
495*906943f9SDavid du Colombier 	edlinked(ed, nil);		/* wipe out next field */
496*906943f9SDavid du Colombier 
4976a5dc222SDavid du Colombier }
4986a5dc222SDavid du Colombier 
4996a5dc222SDavid du Colombier static void
500*906943f9SDavid du Colombier unlinkbulk(Ctlr *ctlr, Ed *ed)
5016a5dc222SDavid du Colombier {
502*906943f9SDavid du Colombier 	Ed *this, *prev, *next;
5036a5dc222SDavid du Colombier 
504*906943f9SDavid du Colombier 	ctlr->ohci->control &= ~Cble;
505*906943f9SDavid du Colombier 	waitSOF(ctlr);
506*906943f9SDavid du Colombier 	this = bulkhd(ctlr);
507*906943f9SDavid du Colombier 	ctlr->ohci->bulkcurred = 0;
5086a5dc222SDavid du Colombier 	prev = nil;
5096a5dc222SDavid du Colombier 	while(this != nil && this != ed){
5106a5dc222SDavid du Colombier 		prev = this;
511*906943f9SDavid du Colombier 		this = this->next;
5126a5dc222SDavid du Colombier 	}
513*906943f9SDavid du Colombier 	if(this == nil){
514*906943f9SDavid du Colombier 		print("unlinkbulk: not found\n");
515*906943f9SDavid du Colombier 		return;
516*906943f9SDavid du Colombier 	}
517*906943f9SDavid du Colombier 	next = this->next;
5186a5dc222SDavid du Colombier 	if(prev == nil)
519*906943f9SDavid du Colombier 		setbulkhd(ctlr, next);
5206a5dc222SDavid du Colombier 	else
521*906943f9SDavid du Colombier 		edlinked(prev, next);
522*906943f9SDavid du Colombier 	ctlr->ohci->control |= Cble;
523*906943f9SDavid du Colombier 	edlinked(ed, nil);		/* wipe out next field */
5246a5dc222SDavid du Colombier }
5256a5dc222SDavid du Colombier 
5266a5dc222SDavid du Colombier static void
527*906943f9SDavid du Colombier edsetaddr(Ed *ed, ulong addr)
5286a5dc222SDavid du Colombier {
5296a5dc222SDavid du Colombier 	ulong ctrl;
5306a5dc222SDavid du Colombier 
531*906943f9SDavid du Colombier 	ctrl = ed->ctrl & ~((Epmax<<7)|Devmax);
532*906943f9SDavid du Colombier 	ctrl |= (addr & ((Epmax<<7)|Devmax));
533*906943f9SDavid du Colombier 	ed->ctrl = ctrl;
5346a5dc222SDavid du Colombier }
5356a5dc222SDavid du Colombier 
5366a5dc222SDavid du Colombier static void
537*906943f9SDavid du Colombier edsettog(Ed *ed, int c)
5386a5dc222SDavid du Colombier {
539*906943f9SDavid du Colombier 	if(c != 0)
540*906943f9SDavid du Colombier 		ed->head |= Edtoggle;
541*906943f9SDavid du Colombier 	else
542*906943f9SDavid du Colombier 		ed->head &= ~Edtoggle;
5436a5dc222SDavid du Colombier }
544*906943f9SDavid du Colombier 
545*906943f9SDavid du Colombier static int
546*906943f9SDavid du Colombier edtoggle(Ed *ed)
547*906943f9SDavid du Colombier {
548*906943f9SDavid du Colombier 	return ed->head & Edtoggle;
549*906943f9SDavid du Colombier }
550*906943f9SDavid du Colombier 
551*906943f9SDavid du Colombier static int
552*906943f9SDavid du Colombier edhalted(Ed *ed)
553*906943f9SDavid du Colombier {
554*906943f9SDavid du Colombier 	return ed->head & Edhalt;
555*906943f9SDavid du Colombier }
556*906943f9SDavid du Colombier 
557*906943f9SDavid du Colombier static int
558*906943f9SDavid du Colombier edmaxpkt(Ed *ed)
559*906943f9SDavid du Colombier {
560*906943f9SDavid du Colombier 	return (ed->ctrl >> Edmpsshift) & Edmpsmask;
5616a5dc222SDavid du Colombier }
5626a5dc222SDavid du Colombier 
5636a5dc222SDavid du Colombier static void
564*906943f9SDavid du Colombier edsetmaxpkt(Ed *ed, int m)
5656a5dc222SDavid du Colombier {
566*906943f9SDavid du Colombier 	ulong c;
5676a5dc222SDavid du Colombier 
568*906943f9SDavid du Colombier 	c = ed->ctrl & ~(Edmpsmask << Edmpsshift);
569*906943f9SDavid du Colombier 	ed->ctrl = c | ((m&Edmpsmask) << Edmpsshift);
5706a5dc222SDavid du Colombier }
571*906943f9SDavid du Colombier 
572*906943f9SDavid du Colombier static int
573*906943f9SDavid du Colombier tderrs(Td *td)
574*906943f9SDavid du Colombier {
575*906943f9SDavid du Colombier 	return (td->ctrl >> Tdccshift) & Tdccmask;
576*906943f9SDavid du Colombier }
577*906943f9SDavid du Colombier 
578*906943f9SDavid du Colombier static int
579*906943f9SDavid du Colombier tdtok(Td *td)
580*906943f9SDavid du Colombier {
581*906943f9SDavid du Colombier 	return (td->ctrl & Tdtokmask);
582*906943f9SDavid du Colombier }
583*906943f9SDavid du Colombier 
584*906943f9SDavid du Colombier static Td*
585*906943f9SDavid du Colombier tdalloc(void)
586*906943f9SDavid du Colombier {
587*906943f9SDavid du Colombier 	Td *td;
588*906943f9SDavid du Colombier 	Td *pool;
589*906943f9SDavid du Colombier 	int i;
590*906943f9SDavid du Colombier 
591*906943f9SDavid du Colombier 	lock(&tdpool);
592*906943f9SDavid du Colombier 	if(tdpool.free == nil){
593*906943f9SDavid du Colombier 		ddprint("ohci: tdalloc %d Tds\n", Incr);
594*906943f9SDavid du Colombier 		pool = xspanalloc(Incr*sizeof(Td), Align, 0);
595*906943f9SDavid du Colombier 		if(pool == nil)
596*906943f9SDavid du Colombier 			panic("tdalloc");
597*906943f9SDavid du Colombier 		for(i=Incr; --i>=0;){
598*906943f9SDavid du Colombier 			pool[i].next = tdpool.free;
599*906943f9SDavid du Colombier 			tdpool.free = &pool[i];
600*906943f9SDavid du Colombier 		}
601*906943f9SDavid du Colombier 		tdpool.nalloc += Incr;
602*906943f9SDavid du Colombier 		tdpool.nfree += Incr;
603*906943f9SDavid du Colombier 	}
604*906943f9SDavid du Colombier 	tdpool.ninuse++;
605*906943f9SDavid du Colombier 	tdpool.nfree--;
606*906943f9SDavid du Colombier 	td = tdpool.free;
607*906943f9SDavid du Colombier 	tdpool.free = td->next;
608*906943f9SDavid du Colombier 	memset(td, 0, sizeof(Td));
609*906943f9SDavid du Colombier 	unlock(&tdpool);
610*906943f9SDavid du Colombier 
611*906943f9SDavid du Colombier 	assert(((uintptr)td & 0xF) == 0);
612*906943f9SDavid du Colombier 	return td;
613*906943f9SDavid du Colombier }
614*906943f9SDavid du Colombier 
615*906943f9SDavid du Colombier static void
616*906943f9SDavid du Colombier tdfree(Td *td)
617*906943f9SDavid du Colombier {
618*906943f9SDavid du Colombier 	if(td == 0)
619*906943f9SDavid du Colombier 		return;
620*906943f9SDavid du Colombier 	freeb(td->bp);
621*906943f9SDavid du Colombier 	td->bp = nil;
622*906943f9SDavid du Colombier 	lock(&tdpool);
623*906943f9SDavid du Colombier 	if(td->nexttd == 0x77777777)
624*906943f9SDavid du Colombier 		panic("ohci: tdfree: double free");
625*906943f9SDavid du Colombier 	memset(td, 7, sizeof(Td));	/* poison */
626*906943f9SDavid du Colombier 	td->next = tdpool.free;
627*906943f9SDavid du Colombier 	tdpool.free = td;
628*906943f9SDavid du Colombier 	tdpool.ninuse--;
629*906943f9SDavid du Colombier 	tdpool.nfree++;
630*906943f9SDavid du Colombier 	unlock(&tdpool);
631*906943f9SDavid du Colombier }
632*906943f9SDavid du Colombier 
633*906943f9SDavid du Colombier static Ed*
634*906943f9SDavid du Colombier edalloc(void)
635*906943f9SDavid du Colombier {
636*906943f9SDavid du Colombier 	Ed *ed;
637*906943f9SDavid du Colombier 	Ed *pool;
638*906943f9SDavid du Colombier 	int i;
639*906943f9SDavid du Colombier 
640*906943f9SDavid du Colombier 	lock(&edpool);
641*906943f9SDavid du Colombier 	if(edpool.free == nil){
642*906943f9SDavid du Colombier 		ddprint("ohci: edalloc %d Eds\n", Incr);
643*906943f9SDavid du Colombier 		pool = xspanalloc(Incr*sizeof(Ed), Align, 0);
644*906943f9SDavid du Colombier 		if(pool == nil)
645*906943f9SDavid du Colombier 			panic("edalloc");
646*906943f9SDavid du Colombier 		for(i=Incr; --i>=0;){
647*906943f9SDavid du Colombier 			pool[i].next = edpool.free;
648*906943f9SDavid du Colombier 			edpool.free = &pool[i];
649*906943f9SDavid du Colombier 		}
650*906943f9SDavid du Colombier 		edpool.nalloc += Incr;
651*906943f9SDavid du Colombier 		edpool.nfree += Incr;
652*906943f9SDavid du Colombier 	}
653*906943f9SDavid du Colombier 	edpool.ninuse++;
654*906943f9SDavid du Colombier 	edpool.nfree--;
655*906943f9SDavid du Colombier 	ed = edpool.free;
656*906943f9SDavid du Colombier 	edpool.free = ed->next;
657*906943f9SDavid du Colombier 	memset(ed, 0, sizeof(Ed));
658*906943f9SDavid du Colombier 	unlock(&edpool);
659*906943f9SDavid du Colombier 
660*906943f9SDavid du Colombier 	return ed;
661*906943f9SDavid du Colombier }
662*906943f9SDavid du Colombier 
663*906943f9SDavid du Colombier static void
664*906943f9SDavid du Colombier edfree(Ed *ed)
665*906943f9SDavid du Colombier {
666*906943f9SDavid du Colombier 	Td *td, *next;
667*906943f9SDavid du Colombier 	int i;
668*906943f9SDavid du Colombier 
669*906943f9SDavid du Colombier 	if(ed == 0)
670*906943f9SDavid du Colombier 		return;
671*906943f9SDavid du Colombier 	i = 0;
672*906943f9SDavid du Colombier 	for(td = ed->tds; td != nil; td = next){
673*906943f9SDavid du Colombier 		next = td->next;
674*906943f9SDavid du Colombier 		tdfree(td);
675*906943f9SDavid du Colombier 		if(i++ > 2000){
676*906943f9SDavid du Colombier 			print("ohci: bug: ed with more than 2000 tds\n");
677*906943f9SDavid du Colombier 			break;
678*906943f9SDavid du Colombier 		}
679*906943f9SDavid du Colombier 	}
680*906943f9SDavid du Colombier 	lock(&edpool);
681*906943f9SDavid du Colombier 	if(ed->nexted == 0x99999999)
682*906943f9SDavid du Colombier 		panic("ohci: edfree: double free");
683*906943f9SDavid du Colombier 	memset(ed, 9, sizeof(Ed));	/* poison */
684*906943f9SDavid du Colombier 	ed->next = edpool.free;
685*906943f9SDavid du Colombier 	edpool.free = ed;
686*906943f9SDavid du Colombier 	edpool.ninuse--;
687*906943f9SDavid du Colombier 	edpool.nfree++;
688*906943f9SDavid du Colombier 	unlock(&edpool);
689*906943f9SDavid du Colombier 	ddprint("edfree: ed %#p\n", ed);
6906a5dc222SDavid du Colombier }
6916a5dc222SDavid du Colombier 
6926a5dc222SDavid du Colombier /*
6936a5dc222SDavid du Colombier  * return smallest power of 2 >= n
6946a5dc222SDavid du Colombier  */
6956a5dc222SDavid du Colombier static int
6966a5dc222SDavid du Colombier flog2(int n)
6976a5dc222SDavid du Colombier {
6986a5dc222SDavid du Colombier 	int i;
6996a5dc222SDavid du Colombier 
7006a5dc222SDavid du Colombier 	for(i = 0; (1 << i) < n; i++)
7016a5dc222SDavid du Colombier 		;
7026a5dc222SDavid du Colombier 	return i;
7036a5dc222SDavid du Colombier }
7046a5dc222SDavid du Colombier 
7056a5dc222SDavid du Colombier /*
7066a5dc222SDavid du Colombier  * return smallest power of 2 <= n
7076a5dc222SDavid du Colombier  */
7086a5dc222SDavid du Colombier static int
7096a5dc222SDavid du Colombier flog2lower(int n)
7106a5dc222SDavid du Colombier {
7116a5dc222SDavid du Colombier 	int i;
7126a5dc222SDavid du Colombier 
7136a5dc222SDavid du Colombier 	for(i = 0; (1 << (i + 1)) <= n; i++)
7146a5dc222SDavid du Colombier 		;
7156a5dc222SDavid du Colombier 	return i;
7166a5dc222SDavid du Colombier }
7176a5dc222SDavid du Colombier 
7186a5dc222SDavid du Colombier static int
719*906943f9SDavid du Colombier pickschedq(Qtree *qt, int pollival, ulong bw, ulong limit)
7206a5dc222SDavid du Colombier {
7216a5dc222SDavid du Colombier 	int i, j, d, upperb, q;
7226a5dc222SDavid du Colombier 	ulong best, worst, total;
7236a5dc222SDavid du Colombier 
724*906943f9SDavid du Colombier 	d = flog2lower(pollival);
7256a5dc222SDavid du Colombier 	if(d > qt->depth)
7266a5dc222SDavid du Colombier 		d = qt->depth;
7276a5dc222SDavid du Colombier 	q = -1;
7286a5dc222SDavid du Colombier 	worst = 0;
7296a5dc222SDavid du Colombier 	best = ~0;
7306a5dc222SDavid du Colombier 	upperb = (1 << (d+1)) - 1;
7316a5dc222SDavid du Colombier 	for(i = (1 << d) - 1; i < upperb; i++){
7326a5dc222SDavid du Colombier 		total = qt->bw[0];
7336a5dc222SDavid du Colombier 		for(j = i; j > 0; j = (j - 1) / 2)
7346a5dc222SDavid du Colombier 			total += qt->bw[j];
7356a5dc222SDavid du Colombier 		if(total < best){
7366a5dc222SDavid du Colombier 			best = total;
7376a5dc222SDavid du Colombier 			q = i;
7386a5dc222SDavid du Colombier 		}
7396a5dc222SDavid du Colombier 		if(total > worst)
7406a5dc222SDavid du Colombier 			worst = total;
7416a5dc222SDavid du Colombier 	}
7426a5dc222SDavid du Colombier 	if(worst + bw >= limit)
7436a5dc222SDavid du Colombier 		return -1;
7446a5dc222SDavid du Colombier 	return q;
7456a5dc222SDavid du Colombier }
7466a5dc222SDavid du Colombier 
7476a5dc222SDavid du Colombier static int
748*906943f9SDavid du Colombier schedq(Ctlr *ctlr, Qio *io, int pollival)
7496a5dc222SDavid du Colombier {
7506a5dc222SDavid du Colombier 	int q;
751*906943f9SDavid du Colombier 	Ed *ted;
7526a5dc222SDavid du Colombier 
753*906943f9SDavid du Colombier 	q = pickschedq(ctlr->tree, pollival, io->bw, ~0);
754*906943f9SDavid du Colombier 	ddqprint("ohci: sched %#p q %d, ival %d, bw %ld\n", io, q, pollival, io->bw);
7556a5dc222SDavid du Colombier 	if(q < 0){
756*906943f9SDavid du Colombier 		print("ohci: no room for ed\n");
7576a5dc222SDavid du Colombier 		return -1;
7586a5dc222SDavid du Colombier 	}
759*906943f9SDavid du Colombier 	ctlr->tree->bw[q] += io->bw;
760*906943f9SDavid du Colombier 	ted = ctlr->tree->root[q];
761*906943f9SDavid du Colombier 	io->sched = q;
762*906943f9SDavid du Colombier 	edlinked(io->ed, ted->next);
763*906943f9SDavid du Colombier 	edlinked(ted, io->ed);
764*906943f9SDavid du Colombier 	io->ed->inext = ctlr->intrhd;
765*906943f9SDavid du Colombier 	ctlr->intrhd = io->ed;
7666a5dc222SDavid du Colombier 	return 0;
7676a5dc222SDavid du Colombier }
7686a5dc222SDavid du Colombier 
7696a5dc222SDavid du Colombier static void
770*906943f9SDavid du Colombier unschedq(Ctlr *ctlr, Qio *qio)
7716a5dc222SDavid du Colombier {
7726a5dc222SDavid du Colombier 	int q;
773*906943f9SDavid du Colombier 	Ed *prev, *this, *next;
774*906943f9SDavid du Colombier 	Ed **l;
7756a5dc222SDavid du Colombier 
776*906943f9SDavid du Colombier 	q = qio->sched;
777*906943f9SDavid du Colombier 	if(q < 0)
7786a5dc222SDavid du Colombier 		return;
779*906943f9SDavid du Colombier 	ctlr->tree->bw[q] -= qio->bw;
7806a5dc222SDavid du Colombier 
781*906943f9SDavid du Colombier 	prev = ctlr->tree->root[q];
782*906943f9SDavid du Colombier 	this = prev->next;
783*906943f9SDavid du Colombier 	while(this != nil && this != qio->ed){
7846a5dc222SDavid du Colombier 		prev = this;
785*906943f9SDavid du Colombier 		this = this->next;
7866a5dc222SDavid du Colombier 	}
7876a5dc222SDavid du Colombier 	if(this == nil)
788*906943f9SDavid du Colombier 		print("ohci: unschedq %d: not found\n", q);
7896a5dc222SDavid du Colombier 	else{
790*906943f9SDavid du Colombier 		next = this->next;
791*906943f9SDavid du Colombier 		edlinked(prev, next);
7926a5dc222SDavid du Colombier 	}
793*906943f9SDavid du Colombier 	waitSOF(ctlr);
794*906943f9SDavid du Colombier 	for(l = &ctlr->intrhd; *l != nil; l = &(*l)->inext)
795*906943f9SDavid du Colombier 		if(*l == qio->ed){
796*906943f9SDavid du Colombier 			*l = (*l)->inext;
797*906943f9SDavid du Colombier 			return;
798*906943f9SDavid du Colombier 		}
799*906943f9SDavid du Colombier 	print("ohci: unschedq: ed %#p not found\n", qio->ed);
8006a5dc222SDavid du Colombier }
8016a5dc222SDavid du Colombier 
802*906943f9SDavid du Colombier static char*
803*906943f9SDavid du Colombier seprinttdtok(char *s, char *e, int tok)
8046a5dc222SDavid du Colombier {
805*906943f9SDavid du Colombier 	switch(tok){
806*906943f9SDavid du Colombier 	case Tdtoksetup:
807*906943f9SDavid du Colombier 		s = seprint(s, e, " setup");
808*906943f9SDavid du Colombier 		break;
809*906943f9SDavid du Colombier 	case Tdtokin:
810*906943f9SDavid du Colombier 		s = seprint(s, e, " in");
811*906943f9SDavid du Colombier 		break;
812*906943f9SDavid du Colombier 	case Tdtokout:
813*906943f9SDavid du Colombier 		s = seprint(s, e, " out");
814*906943f9SDavid du Colombier 		break;
8156a5dc222SDavid du Colombier 	}
816*906943f9SDavid du Colombier 	return s;
817*906943f9SDavid du Colombier }
818*906943f9SDavid du Colombier 
819*906943f9SDavid du Colombier 
820*906943f9SDavid du Colombier static char*
821*906943f9SDavid du Colombier seprinttd(char *s, char *e, Td *td, int iso)
822*906943f9SDavid du Colombier {
823*906943f9SDavid du Colombier 	int i;
824*906943f9SDavid du Colombier 	Block *bp;
825*906943f9SDavid du Colombier 
826*906943f9SDavid du Colombier 	if(td == nil)
827*906943f9SDavid du Colombier 		return seprint(s, e, "<nil td>\n");
828*906943f9SDavid du Colombier 	s = seprint(s, e, "%#p ep %#p ctrl %#p", td, td->ep, td->ctrl);
829*906943f9SDavid du Colombier 	s = seprint(s, e, " cc=%#ulx", (td->ctrl >> Tdccshift) & Tdccmask);
830*906943f9SDavid du Colombier 	if(iso == 0){
831*906943f9SDavid du Colombier 		if((td->ctrl & Tdround) != 0)
832*906943f9SDavid du Colombier 			s = seprint(s, e, " rnd");
833*906943f9SDavid du Colombier 		s = seprinttdtok(s, e, td->ctrl & Tdtokmask);
834*906943f9SDavid du Colombier 		if((td->ctrl & Tdusetog) != 0)
835*906943f9SDavid du Colombier 			s = seprint(s, e, " d%d", (td->ctrl & Tddata1) ? 1 : 0);
836*906943f9SDavid du Colombier 		else
837*906943f9SDavid du Colombier 			s = seprint(s, e, " d-");
838*906943f9SDavid du Colombier 		s = seprint(s, e, " ec=%uld", (td->ctrl >> Tderrshift) & Tderrmask);
839*906943f9SDavid du Colombier 	}else{
840*906943f9SDavid du Colombier 		s = seprint(s, e, " fc=%uld", (td->ctrl >> Tdfcshift) & Tdfcmask);
841*906943f9SDavid du Colombier 		s = seprint(s, e, " sf=%uld", td->ctrl & Tdsfmask);
842*906943f9SDavid du Colombier 	}
843*906943f9SDavid du Colombier 	s = seprint(s, e, " cbp0 %#p cbp %#p next %#p be %#p %s",
844*906943f9SDavid du Colombier 		td->cbp0, td->cbp, td->nexttd, td->be, td->last ? "last" : "");
845*906943f9SDavid du Colombier 	s = seprint(s, e, "\n\t\t%ld bytes", td->nbytes);
846*906943f9SDavid du Colombier 	if((bp = td->bp) != nil){
847*906943f9SDavid du Colombier 		s = seprint(s, e, " rp %#p wp %#p ", bp->rp, bp->wp);
848*906943f9SDavid du Colombier 		if(BLEN(bp) > 0)
849*906943f9SDavid du Colombier 			s = seprintdata(s, e, bp->rp, bp->wp - bp->rp);
850*906943f9SDavid du Colombier 	}
851*906943f9SDavid du Colombier 	if(iso == 0)
852*906943f9SDavid du Colombier 		return seprint(s, e, "\n");
853*906943f9SDavid du Colombier 	s = seprint(s, e, "\n\t\t");
854*906943f9SDavid du Colombier 	/* we use only offsets[0] */
855*906943f9SDavid du Colombier 	i = 0;
856*906943f9SDavid du Colombier 	s = seprint(s, e, "[%d] %#ux cc=%#ux sz=%ud\n", i, td->offsets[i],
857*906943f9SDavid du Colombier 		(td->offsets[i] >> Tdiccshift) & Tdiccmask,
858*906943f9SDavid du Colombier 		td->offsets[i] & 0x7FF);
859*906943f9SDavid du Colombier 	return s;
860*906943f9SDavid du Colombier }
861*906943f9SDavid du Colombier 
862*906943f9SDavid du Colombier static void
863*906943f9SDavid du Colombier dumptd(Td *td, char *p, int iso)
864*906943f9SDavid du Colombier {
865*906943f9SDavid du Colombier 	static char buf[512];	/* Too much */
866*906943f9SDavid du Colombier 	char *s;
867*906943f9SDavid du Colombier 
868*906943f9SDavid du Colombier 	s = seprint(buf, buf+sizeof(buf), "%s: ", p);
869*906943f9SDavid du Colombier 	s = seprinttd(s, buf+sizeof(buf), td, iso);
870*906943f9SDavid du Colombier 	if(s > buf && s[-1] != '\n')
871*906943f9SDavid du Colombier 		s[-1] = '\n';
872*906943f9SDavid du Colombier 	print("\t%s", buf);
873*906943f9SDavid du Colombier }
874*906943f9SDavid du Colombier 
875*906943f9SDavid du Colombier static void
876*906943f9SDavid du Colombier dumptds(Td *td, char *p, int iso)
877*906943f9SDavid du Colombier {
878*906943f9SDavid du Colombier 	int i;
879*906943f9SDavid du Colombier 
880*906943f9SDavid du Colombier 	for(i = 0; td != nil; td = td->next){
881*906943f9SDavid du Colombier 		dumptd(td, p, iso);
882*906943f9SDavid du Colombier 		if(td->last)
883*906943f9SDavid du Colombier 			break;
884*906943f9SDavid du Colombier 		if(tdtok(td) == Tdtokin && ++i > 2){
885*906943f9SDavid du Colombier 			print("\t\t...\n");
886*906943f9SDavid du Colombier 			break;
887*906943f9SDavid du Colombier 		}
888*906943f9SDavid du Colombier 	}
889*906943f9SDavid du Colombier }
890*906943f9SDavid du Colombier 
891*906943f9SDavid du Colombier static void
892*906943f9SDavid du Colombier dumped(Ed *ed)
893*906943f9SDavid du Colombier {
894*906943f9SDavid du Colombier 	char *buf;
895*906943f9SDavid du Colombier 	char *s;
896*906943f9SDavid du Colombier 	char *e;
897*906943f9SDavid du Colombier 
898*906943f9SDavid du Colombier 	if(ed == nil){
899*906943f9SDavid du Colombier 		print("<null ed>\n");
900*906943f9SDavid du Colombier 		return;
901*906943f9SDavid du Colombier 	}
902*906943f9SDavid du Colombier 	buf = malloc(512);
903*906943f9SDavid du Colombier 	/* no waserror; may want to use from interrupt context */
904*906943f9SDavid du Colombier 	if(buf == nil)
905*906943f9SDavid du Colombier 		return;
906*906943f9SDavid du Colombier 	e = buf+512;
907*906943f9SDavid du Colombier 	s = seprint(buf, e, "\ted %#p: ctrl %#p", ed, ed->ctrl);
908*906943f9SDavid du Colombier 	if((ed->ctrl & Edskip) != 0)
909*906943f9SDavid du Colombier 		s = seprint(s, e, " skip");
910*906943f9SDavid du Colombier 	if((ed->ctrl & Ediso) != 0)
911*906943f9SDavid du Colombier 		s = seprint(s, e, " iso");
912*906943f9SDavid du Colombier 	if((ed->ctrl & Edlow) != 0)
913*906943f9SDavid du Colombier 		s = seprint(s, e, " low");
914*906943f9SDavid du Colombier 	s = seprint(s, e, " d%d", (ed->head & Edtoggle) ? 1 : 0);
915*906943f9SDavid du Colombier 	if((ed->ctrl & Eddirmask) == Edin)
916*906943f9SDavid du Colombier 		s = seprint(s, e, " in");
917*906943f9SDavid du Colombier 	if((ed->ctrl & Eddirmask) == Edout)
918*906943f9SDavid du Colombier 		s = seprint(s, e, " out");
919*906943f9SDavid du Colombier 	if(edhalted(ed))
920*906943f9SDavid du Colombier 		s = seprint(s, e, " hlt");
921*906943f9SDavid du Colombier 	s = seprint(s, e, " ep%uld.%uld", (ed->ctrl>>7)&Epmax, ed->ctrl&0x7f);
922*906943f9SDavid du Colombier 	s = seprint(s, e, " maxpkt %uld", (ed->ctrl>>Edmpsshift)&Edmpsmask);
923*906943f9SDavid du Colombier 	seprint(s, e, " tail %#p head %#p next %#p\n",ed->tail,ed->head,ed->nexted);
924*906943f9SDavid du Colombier 	print("%s", buf);
925*906943f9SDavid du Colombier 	free(buf);
926*906943f9SDavid du Colombier 	if(ed->tds != nil && (ed->ctrl & Ediso) == 0)
927*906943f9SDavid du Colombier 		dumptds(ed->tds, "td", 0);
928*906943f9SDavid du Colombier }
929*906943f9SDavid du Colombier 
930*906943f9SDavid du Colombier static char*
931*906943f9SDavid du Colombier seprintio(char *s, char *e, Qio *io, char *pref)
932*906943f9SDavid du Colombier {
933*906943f9SDavid du Colombier 	s = seprint(s, e, "%s qio %#p ed %#p", pref, io, io->ed);
934*906943f9SDavid du Colombier 	s = seprint(s, e, " tog %d iot %ld err %s id %#ulx",
935*906943f9SDavid du Colombier 		io->toggle, io->iotime, io->err, io->usbid);
936*906943f9SDavid du Colombier 	s = seprinttdtok(s, e, io->tok);
937*906943f9SDavid du Colombier 	s = seprint(s, e, " %s\n", iosname[io->state]);
938*906943f9SDavid du Colombier 	return s;
939*906943f9SDavid du Colombier }
940*906943f9SDavid du Colombier 
941*906943f9SDavid du Colombier static char*
942*906943f9SDavid du Colombier seprintep(char* s, char* e, Ep *ep)
943*906943f9SDavid du Colombier {
944*906943f9SDavid du Colombier 	Isoio *iso;
945*906943f9SDavid du Colombier 	Qio *io;
946*906943f9SDavid du Colombier 	Ctlio *cio;
947*906943f9SDavid du Colombier 
948*906943f9SDavid du Colombier 	if(ep == nil)
949*906943f9SDavid du Colombier 		return seprint(s, e, "<nil ep>\n");
950*906943f9SDavid du Colombier 	if(ep->aux == nil)
951*906943f9SDavid du Colombier 		return seprint(s, e, "no mdep\n");
952*906943f9SDavid du Colombier 	switch(ep->ttype){
953*906943f9SDavid du Colombier 	case Tctl:
954*906943f9SDavid du Colombier 		cio = ep->aux;
955*906943f9SDavid du Colombier 		s = seprintio(s, e, cio, "c");
956*906943f9SDavid du Colombier 		s = seprint(s, e, "\trepl %d ndata %d\n", ep->rhrepl, cio->ndata);
957*906943f9SDavid du Colombier 		break;
958*906943f9SDavid du Colombier 	case Tbulk:
959*906943f9SDavid du Colombier 	case Tintr:
960*906943f9SDavid du Colombier 		io = ep->aux;
961*906943f9SDavid du Colombier 		if(ep->mode != OWRITE)
962*906943f9SDavid du Colombier 			s = seprintio(s, e, &io[OREAD], "r");
963*906943f9SDavid du Colombier 		if(ep->mode != OREAD)
964*906943f9SDavid du Colombier 			s = seprintio(s, e, &io[OWRITE], "w");
965*906943f9SDavid du Colombier 		break;
966*906943f9SDavid du Colombier 	case Tiso:
967*906943f9SDavid du Colombier 		iso = ep->aux;
968*906943f9SDavid du Colombier 		s = seprintio(s, e, iso, "w");
969*906943f9SDavid du Colombier 		s = seprint(s, e, "\tntds %d avail %d frno %uld left %uld next avail %#p\n",
970*906943f9SDavid du Colombier 			iso->nframes, iso->navail, iso->frno, iso->left, iso->atds);
971*906943f9SDavid du Colombier 		break;
972*906943f9SDavid du Colombier 	}
973*906943f9SDavid du Colombier 	return s;
974*906943f9SDavid du Colombier }
975*906943f9SDavid du Colombier 
976*906943f9SDavid du Colombier static char*
977*906943f9SDavid du Colombier seprintctl(char *s, char *se, ulong ctl)
978*906943f9SDavid du Colombier {
979*906943f9SDavid du Colombier 	s = seprint(s, se, "en=");
980*906943f9SDavid du Colombier 	if((ctl&Cple) != 0)
981*906943f9SDavid du Colombier 		s = seprint(s, se, "p");
982*906943f9SDavid du Colombier 	if((ctl&Cie) != 0)
983*906943f9SDavid du Colombier 		s = seprint(s, se, "i");
984*906943f9SDavid du Colombier 	if((ctl&Ccle) != 0)
985*906943f9SDavid du Colombier 		s = seprint(s, se, "c");
986*906943f9SDavid du Colombier 	if((ctl&Cble) != 0)
987*906943f9SDavid du Colombier 		s = seprint(s, se, "b");
988*906943f9SDavid du Colombier 	switch(ctl & Cfsmask){
989*906943f9SDavid du Colombier 	case Cfsreset:
990*906943f9SDavid du Colombier 		return seprint(s, se, " reset");
991*906943f9SDavid du Colombier 	case Cfsresume:
992*906943f9SDavid du Colombier 		return seprint(s, se, " resume");
993*906943f9SDavid du Colombier 	case Cfsoper:
994*906943f9SDavid du Colombier 		return seprint(s, se, " run");
995*906943f9SDavid du Colombier 	case Cfssuspend:
996*906943f9SDavid du Colombier 		return seprint(s, se, " suspend");
997*906943f9SDavid du Colombier 	default:
998*906943f9SDavid du Colombier 		return seprint(s, se, " ???");
999*906943f9SDavid du Colombier 	}
1000*906943f9SDavid du Colombier }
1001*906943f9SDavid du Colombier 
1002*906943f9SDavid du Colombier static void
1003*906943f9SDavid du Colombier dump(Hci *hp)
1004*906943f9SDavid du Colombier {
1005*906943f9SDavid du Colombier 	Ctlr *ctlr;
1006*906943f9SDavid du Colombier 	Ed *ed;
1007*906943f9SDavid du Colombier 	char cs[20];
1008*906943f9SDavid du Colombier 
1009*906943f9SDavid du Colombier 	ctlr = hp->aux;
1010*906943f9SDavid du Colombier 	ilock(ctlr);
1011*906943f9SDavid du Colombier 	seprintctl(cs, cs+sizeof(cs), ctlr->ohci->control);
1012*906943f9SDavid du Colombier 	print("ohci ctlr %#p: frno %#ux ctl %#lux %s sts %#lux intr %#lux\n",
1013*906943f9SDavid du Colombier 		ctlr, ctlr->hcca->framenumber, ctlr->ohci->control, cs,
1014*906943f9SDavid du Colombier 		ctlr->ohci->cmdsts, ctlr->ohci->intrsts);
1015*906943f9SDavid du Colombier 	print("ctlhd %#ulx cur %#ulx bulkhd %#ulx cur %#ulx done %#ulx\n",
1016*906943f9SDavid du Colombier 		ctlr->ohci->ctlheaded, ctlr->ohci->ctlcurred,
1017*906943f9SDavid du Colombier 		ctlr->ohci->bulkheaded, ctlr->ohci->bulkcurred,
1018*906943f9SDavid du Colombier 		ctlr->ohci->donehead);
1019*906943f9SDavid du Colombier 	if(ctlhd(ctlr) != nil)
1020*906943f9SDavid du Colombier 		print("[ctl]\n");
1021*906943f9SDavid du Colombier 	for(ed = ctlhd(ctlr); ed != nil; ed = ed->next)
1022*906943f9SDavid du Colombier 		dumped(ed);
1023*906943f9SDavid du Colombier 	if(bulkhd(ctlr) != nil)
1024*906943f9SDavid du Colombier 		print("[bulk]\n");
1025*906943f9SDavid du Colombier 	for(ed = bulkhd(ctlr); ed != nil; ed = ed->next)
1026*906943f9SDavid du Colombier 		dumped(ed);
1027*906943f9SDavid du Colombier 	if(ctlr->intrhd != nil)
1028*906943f9SDavid du Colombier 		print("[intr]\n");
1029*906943f9SDavid du Colombier 	for(ed = ctlr->intrhd; ed != nil; ed = ed->inext)
1030*906943f9SDavid du Colombier 		dumped(ed);
1031*906943f9SDavid du Colombier 	if(ctlr->tree->root[0]->next != nil)
1032*906943f9SDavid du Colombier 		print("[iso]");
1033*906943f9SDavid du Colombier 	for(ed = ctlr->tree->root[0]->next; ed != nil; ed = ed->next)
1034*906943f9SDavid du Colombier 		dumped(ed);
1035*906943f9SDavid du Colombier 	print("%d eds in tree\n", ctlr->ntree);
1036*906943f9SDavid du Colombier 	iunlock(ctlr);
1037*906943f9SDavid du Colombier 	lock(&tdpool);
1038*906943f9SDavid du Colombier 	print("%d tds allocated = %d in use + %d free\n",
1039*906943f9SDavid du Colombier 		tdpool.nalloc, tdpool.ninuse, tdpool.nfree);
1040*906943f9SDavid du Colombier 	unlock(&tdpool);
1041*906943f9SDavid du Colombier 	lock(&edpool);
1042*906943f9SDavid du Colombier 	print("%d eds allocated = %d in use + %d free\n",
1043*906943f9SDavid du Colombier 		edpool.nalloc, edpool.ninuse, edpool.nfree);
1044*906943f9SDavid du Colombier 	unlock(&edpool);
1045*906943f9SDavid du Colombier }
1046*906943f9SDavid du Colombier 
1047*906943f9SDavid du Colombier /*
1048*906943f9SDavid du Colombier  * Compute size for the next iso Td and setup its
1049*906943f9SDavid du Colombier  * descriptor for I/O according to the buffer size.
1050*906943f9SDavid du Colombier  */
1051*906943f9SDavid du Colombier static void
1052*906943f9SDavid du Colombier isodtdinit(Ep *ep, Isoio *iso, Td *td)
1053*906943f9SDavid du Colombier {
1054*906943f9SDavid du Colombier 	Block *bp;
1055*906943f9SDavid du Colombier 	long size;
1056*906943f9SDavid du Colombier 	int i;
1057*906943f9SDavid du Colombier 
1058*906943f9SDavid du Colombier 	bp = td->bp;
1059*906943f9SDavid du Colombier 	assert(bp != nil && BLEN(bp) == 0);
1060*906943f9SDavid du Colombier 	size = (ep->hz+iso->left) * ep->pollival / 1000;
1061*906943f9SDavid du Colombier 	iso->left = (ep->hz+iso->left) * ep->pollival % 1000;
1062*906943f9SDavid du Colombier 	size *= ep->samplesz;
1063*906943f9SDavid du Colombier 	if(size > ep->maxpkt){
1064*906943f9SDavid du Colombier 		print("ohci: ep%d.%d: size > maxpkt\n",
1065*906943f9SDavid du Colombier 			ep->dev->nb, ep->nb);
1066*906943f9SDavid du Colombier 		print("size = %uld max = %ld\n", size, ep->maxpkt);
1067*906943f9SDavid du Colombier 		size = ep->maxpkt;
1068*906943f9SDavid du Colombier 	}
1069*906943f9SDavid du Colombier 	td->nbytes = size;
1070*906943f9SDavid du Colombier 	memset(bp->wp, 0, size);	/* in case we don't fill it on time */
1071*906943f9SDavid du Colombier 	td->cbp0 = td->cbp = ptr2pa(bp->rp) & ~0xFFF;
1072*906943f9SDavid du Colombier 	td->ctrl = TRUNC(iso->frno, Ntdframes);
1073*906943f9SDavid du Colombier 	td->offsets[0] = (ptr2pa(bp->rp) & 0xFFF);
1074*906943f9SDavid du Colombier 	td->offsets[0] |= (Tdnotacc << Tdiccshift);
1075*906943f9SDavid du Colombier 	/* in case the controller checks out the offests... */
1076*906943f9SDavid du Colombier 	for(i = 1; i < nelem(td->offsets); i++)
1077*906943f9SDavid du Colombier 		td->offsets[i] = td->offsets[0];
1078*906943f9SDavid du Colombier 	td->be = ptr2pa(bp->rp + size - 1);
1079*906943f9SDavid du Colombier 	td->ctrl |= (0 << Tdfcshift);	/* frame count is 1 */
1080*906943f9SDavid du Colombier 
1081*906943f9SDavid du Colombier 	iso->frno = TRUNC(iso->frno + ep->pollival, Ntdframes);
1082*906943f9SDavid du Colombier }
1083*906943f9SDavid du Colombier 
1084*906943f9SDavid du Colombier /*
1085*906943f9SDavid du Colombier  * start I/O on the dummy td and setup a new dummy to fill up.
1086*906943f9SDavid du Colombier  */
1087*906943f9SDavid du Colombier static void
1088*906943f9SDavid du Colombier isoadvance(Ep *ep, Isoio *iso, Td *td)
1089*906943f9SDavid du Colombier {
1090*906943f9SDavid du Colombier 	Td *dtd;
1091*906943f9SDavid du Colombier 
1092*906943f9SDavid du Colombier 	dtd = iso->atds;
1093*906943f9SDavid du Colombier 	iso->atds = dtd->anext;
1094*906943f9SDavid du Colombier 	iso->navail--;
1095*906943f9SDavid du Colombier 	dtd->anext = nil;
1096*906943f9SDavid du Colombier 	dtd->bp->wp = dtd->bp->rp;
1097*906943f9SDavid du Colombier 	dtd->nexttd = 0;
1098*906943f9SDavid du Colombier 	td->nexttd = ptr2pa(dtd);
1099*906943f9SDavid du Colombier 	isodtdinit(ep, iso, dtd);
1100*906943f9SDavid du Colombier 	iso->ed->tail = ptr2pa(dtd);
1101*906943f9SDavid du Colombier }
1102*906943f9SDavid du Colombier 
1103*906943f9SDavid du Colombier static int
1104*906943f9SDavid du Colombier isocanwrite(void *a)
1105*906943f9SDavid du Colombier {
1106*906943f9SDavid du Colombier 	Isoio *iso;
1107*906943f9SDavid du Colombier 
1108*906943f9SDavid du Colombier 	iso = a;
1109*906943f9SDavid du Colombier 	return iso->state == Qclose || iso->err != nil ||
1110*906943f9SDavid du Colombier 		iso->navail > iso->nframes / 2;
1111*906943f9SDavid du Colombier }
1112*906943f9SDavid du Colombier 
1113*906943f9SDavid du Colombier /*
1114*906943f9SDavid du Colombier  * Service a completed/failed Td from the done queue.
1115*906943f9SDavid du Colombier  * It may be of any transfer type.
1116*906943f9SDavid du Colombier  * The queue is not in completion order.
1117*906943f9SDavid du Colombier  * (It's actually in reverse completion order).
1118*906943f9SDavid du Colombier  *
1119*906943f9SDavid du Colombier  * When an error, a short packet, or a last Td is found
1120*906943f9SDavid du Colombier  * we awake the process waiting for the transfer.
1121*906943f9SDavid du Colombier  * Although later we will process other Tds completed
1122*906943f9SDavid du Colombier  * before, epio won't be able to touch the current Td
1123*906943f9SDavid du Colombier  * until interrupt returns and releases the lock on the
1124*906943f9SDavid du Colombier  * controller.
1125*906943f9SDavid du Colombier  */
1126*906943f9SDavid du Colombier static void
1127*906943f9SDavid du Colombier qhinterrupt(Ctlr *, Ep *ep, Qio *io, Td *td, int)
1128*906943f9SDavid du Colombier {
1129*906943f9SDavid du Colombier 	Block *bp;
1130*906943f9SDavid du Colombier 	int mode;
1131*906943f9SDavid du Colombier 	int err;
1132*906943f9SDavid du Colombier 	Ed *ed;
1133*906943f9SDavid du Colombier 
1134*906943f9SDavid du Colombier 	ed = io->ed;
1135*906943f9SDavid du Colombier 	if(io->state != Qrun)
1136*906943f9SDavid du Colombier 		return;
1137*906943f9SDavid du Colombier 	if(tdtok(td) == Tdtokin)
1138*906943f9SDavid du Colombier 		mode = OREAD;
1139*906943f9SDavid du Colombier 	else
1140*906943f9SDavid du Colombier 		mode = OWRITE;
1141*906943f9SDavid du Colombier 	bp = td->bp;
1142*906943f9SDavid du Colombier 	err = tderrs(td);
1143*906943f9SDavid du Colombier 
1144*906943f9SDavid du Colombier 	switch(err){
1145*906943f9SDavid du Colombier 	case Tddataovr:			/* Overrun is not an error */
1146*906943f9SDavid du Colombier 	case Tdok:
1147*906943f9SDavid du Colombier 		if(td->cbp != 0)
1148*906943f9SDavid du Colombier 			panic("ohci: full packet but cbp != 0");
1149*906943f9SDavid du Colombier 		break;
1150*906943f9SDavid du Colombier 	case Tddataund:
1151*906943f9SDavid du Colombier 		/* short input packets are ok */
1152*906943f9SDavid du Colombier 		if(mode == OREAD){
1153*906943f9SDavid du Colombier 			if(td->cbp == 0)
1154*906943f9SDavid du Colombier 				panic("ohci: short packet but cbp == 0");
1155*906943f9SDavid du Colombier 			if((td->cbp & ~0xFFF) == (td->cbp0 & ~0xFFF))
1156*906943f9SDavid du Colombier 				bp->wp = pa2ptr(td->cbp);
1157*906943f9SDavid du Colombier 			else
1158*906943f9SDavid du Colombier 				bp->wp = bp->rp + 0x1000 + (td->cbp&0xFFF);
1159*906943f9SDavid du Colombier 			if(bp->wp < bp->rp)
1160*906943f9SDavid du Colombier 				panic("ohci: wp < rp");
1161*906943f9SDavid du Colombier 			/*
1162*906943f9SDavid du Colombier 			 * It's ok. clear error and flag as last in xfer.
1163*906943f9SDavid du Colombier 			 * epio must ignore following Tds.
1164*906943f9SDavid du Colombier 			 */
1165*906943f9SDavid du Colombier 			td->last = 1;
1166*906943f9SDavid du Colombier 			td->ctrl &= ~(Tdccmask << Tdccshift);
1167*906943f9SDavid du Colombier 			break;
1168*906943f9SDavid du Colombier 		}
1169*906943f9SDavid du Colombier 		/* else fall; it's an error */
1170*906943f9SDavid du Colombier 	case Tdcrc:
1171*906943f9SDavid du Colombier 	case Tdbitstuff:
1172*906943f9SDavid du Colombier 	case Tdbadtog:
1173*906943f9SDavid du Colombier 	case Tdstalled:
1174*906943f9SDavid du Colombier 	case Tdtmout:
1175*906943f9SDavid du Colombier 	case Tdpidchk:
1176*906943f9SDavid du Colombier 	case Tdbadpid:
1177*906943f9SDavid du Colombier 		bp->wp = bp->rp;	/* no bytes in xfer. */
1178*906943f9SDavid du Colombier 		io->err = errmsg(err);
1179*906943f9SDavid du Colombier 		if(debug || ep->debug){
1180*906943f9SDavid du Colombier 			print("tdinterrupt: failed err %d (%s)\n", err, io->err);
1181*906943f9SDavid du Colombier 			dumptd(td, "failed", ed->ctrl & Ediso);
1182*906943f9SDavid du Colombier 		}
1183*906943f9SDavid du Colombier 		td->last = 1;
1184*906943f9SDavid du Colombier 		break;
1185*906943f9SDavid du Colombier 	default:
1186*906943f9SDavid du Colombier 		panic("ohci: td cc %ud unknown\n", err);
1187*906943f9SDavid du Colombier 	}
1188*906943f9SDavid du Colombier 
1189*906943f9SDavid du Colombier 	if(td->last != 0){
1190*906943f9SDavid du Colombier 		/*
1191*906943f9SDavid du Colombier 		 * clear td list and halt flag.
1192*906943f9SDavid du Colombier 		 */
1193*906943f9SDavid du Colombier 		ed->head = (ed->head & Edtoggle) | ed->tail;
1194*906943f9SDavid du Colombier 		ed->tds = pa2ptr(ed->tail);
1195*906943f9SDavid du Colombier 		io->state = Qdone;
1196*906943f9SDavid du Colombier 		wakeup(io);
1197*906943f9SDavid du Colombier 	}
1198*906943f9SDavid du Colombier }
1199*906943f9SDavid du Colombier 
1200*906943f9SDavid du Colombier /*
1201*906943f9SDavid du Colombier  * BUG: Iso input streams are not implemented.
1202*906943f9SDavid du Colombier  */
1203*906943f9SDavid du Colombier static void
1204*906943f9SDavid du Colombier isointerrupt(Ctlr *ctlr, Ep *ep, Qio *io, Td *td, int)
1205*906943f9SDavid du Colombier {
1206*906943f9SDavid du Colombier 	Isoio *iso;
1207*906943f9SDavid du Colombier 	Block *bp;
1208*906943f9SDavid du Colombier 	int err;
1209*906943f9SDavid du Colombier 	int isoerr;
1210*906943f9SDavid du Colombier 	Ed *ed;
1211*906943f9SDavid du Colombier 
1212*906943f9SDavid du Colombier 	iso = ep->aux;
1213*906943f9SDavid du Colombier 	ed = io->ed;
1214*906943f9SDavid du Colombier 	if(io->state == Qclose)
1215*906943f9SDavid du Colombier 		return;
1216*906943f9SDavid du Colombier 	bp = td->bp;
1217*906943f9SDavid du Colombier 	/*
1218*906943f9SDavid du Colombier 	 * When we get more than half the frames consecutive errors
1219*906943f9SDavid du Colombier 	 * we signal an actual error. Errors in the entire Td are
1220*906943f9SDavid du Colombier 	 * more serious and are always singaled.
1221*906943f9SDavid du Colombier 	 * Errors like overrun are not really errors. In fact, for
1222*906943f9SDavid du Colombier 	 * output, errors cannot be really detected. The driver will
1223*906943f9SDavid du Colombier 	 * hopefully notice I/O errors on input endpoints and detach the device.
1224*906943f9SDavid du Colombier 	 */
1225*906943f9SDavid du Colombier 	err = tderrs(td);
1226*906943f9SDavid du Colombier 	isoerr = (td->offsets[0] >> Tdiccshift) & Tdiccmask;
1227*906943f9SDavid du Colombier 	if(isoerr == Tdok || isoerr == Tdnotacc)
1228*906943f9SDavid du Colombier 		iso->nerrs = 0;
1229*906943f9SDavid du Colombier 	else if(iso->nerrs++ > iso->nframes/2)
1230*906943f9SDavid du Colombier 		err = Tdstalled;
1231*906943f9SDavid du Colombier 	if(err != Tdok && err != Tddataovr){
1232*906943f9SDavid du Colombier 		bp->wp = bp->rp;
1233*906943f9SDavid du Colombier 		io->err = errmsg(err);
1234*906943f9SDavid du Colombier 		if(debug || ep->debug){
1235*906943f9SDavid du Colombier 			print("ohci: isointerrupt: ep%d.%d: err %d (%s) frnum 0x%lux\n",
1236*906943f9SDavid du Colombier 				ep->dev->nb, ep->nb,
1237*906943f9SDavid du Colombier 				err, errmsg(err), ctlr->ohci->fmnumber);
1238*906943f9SDavid du Colombier 			dumptd(td, "failed", ed->ctrl & Ediso);
1239*906943f9SDavid du Colombier 		}
1240*906943f9SDavid du Colombier 	}
1241*906943f9SDavid du Colombier 	td->bp->wp = td->bp->rp;
1242*906943f9SDavid du Colombier 	td->nbytes = 0;
1243*906943f9SDavid du Colombier 	td->anext = iso->atds;
1244*906943f9SDavid du Colombier 	iso->atds = td;
1245*906943f9SDavid du Colombier 	iso->navail++;
1246*906943f9SDavid du Colombier 	/*
1247*906943f9SDavid du Colombier 	 * If almost all Tds are avail the user is not doing I/O at the
1248*906943f9SDavid du Colombier 	 * required rate. We put another Td in place to keep the polling rate.
1249*906943f9SDavid du Colombier 	 */
1250*906943f9SDavid du Colombier 	if(iso->err == nil && iso->navail > iso->nframes - 10)
1251*906943f9SDavid du Colombier 		isoadvance(ep, iso, pa2ptr(iso->ed->tail));
1252*906943f9SDavid du Colombier 	/*
1253*906943f9SDavid du Colombier 	 * If there's enough buffering futher I/O can be done.
1254*906943f9SDavid du Colombier 	 */
1255*906943f9SDavid du Colombier 	if(isocanwrite(iso))
1256*906943f9SDavid du Colombier 		wakeup(iso);
1257*906943f9SDavid du Colombier }
1258*906943f9SDavid du Colombier 
1259*906943f9SDavid du Colombier static void
1260*906943f9SDavid du Colombier interrupt(Ureg *, void *arg)
1261*906943f9SDavid du Colombier {
1262*906943f9SDavid du Colombier 	Td *td, *ntd;
1263*906943f9SDavid du Colombier 	Td *td0;
1264*906943f9SDavid du Colombier 	Hci *hp;
1265*906943f9SDavid du Colombier 	Ctlr *ctlr;
1266*906943f9SDavid du Colombier 	ulong status;
1267*906943f9SDavid du Colombier 	ulong curred;
1268*906943f9SDavid du Colombier 	int i;
1269*906943f9SDavid du Colombier 	int frno;
1270*906943f9SDavid du Colombier 
1271*906943f9SDavid du Colombier 	hp = arg;
1272*906943f9SDavid du Colombier 	ctlr = hp->aux;
1273*906943f9SDavid du Colombier 	ilock(ctlr);
1274*906943f9SDavid du Colombier 	status = ctlr->ohci->intrsts;
1275*906943f9SDavid du Colombier 	status &= ctlr->ohci->intrenable;
1276*906943f9SDavid du Colombier 	status &= Oc|Rhsc|Fno|Ue|Rd|Sf|Wdh|So;
1277*906943f9SDavid du Colombier 	frno = TRUNC(ctlr->ohci->fmnumber, Ntdframes);
1278*906943f9SDavid du Colombier 	if((status & Wdh) != 0){
1279*906943f9SDavid du Colombier 		/* lsb of donehead has bit to flag other intrs.  */
1280*906943f9SDavid du Colombier 		td = pa2ptr(ctlr->hcca->donehead & ~0xF);
1281*906943f9SDavid du Colombier 	}else
1282*906943f9SDavid du Colombier 		td = nil;
1283*906943f9SDavid du Colombier 	td0 = td;
1284*906943f9SDavid du Colombier 
1285*906943f9SDavid du Colombier 	for(i = 0; td != nil && i < 1024; i++){
1286*906943f9SDavid du Colombier 		if(0)ddprint("ohci tdinterrupt: td %#p\n", td);
1287*906943f9SDavid du Colombier 		ntd = pa2ptr(td->nexttd & ~0xF);
1288*906943f9SDavid du Colombier 		td->nexttd = 0;
1289*906943f9SDavid du Colombier 		if(td->ep == nil || td->io == nil)
1290*906943f9SDavid du Colombier 			panic("interrupt: ep %#x io %#x\n", td->ep, td->io);
1291*906943f9SDavid du Colombier 		ohciinterrupts[td->ep->ttype]++;
1292*906943f9SDavid du Colombier 		if(td->ep->ttype == Tiso)
1293*906943f9SDavid du Colombier 			isointerrupt(ctlr, td->ep, td->io, td, frno);
1294*906943f9SDavid du Colombier 		else
1295*906943f9SDavid du Colombier 			qhinterrupt(ctlr, td->ep, td->io, td, frno);
1296*906943f9SDavid du Colombier 		td = ntd;
1297*906943f9SDavid du Colombier 	}
1298*906943f9SDavid du Colombier 	if(i == 1024)
1299*906943f9SDavid du Colombier 		print("ohci: bug: more than 1024 done Tds?\n");
1300*906943f9SDavid du Colombier 
1301*906943f9SDavid du Colombier 	if(pa2ptr(ctlr->hcca->donehead & ~0xF) != td0)
1302*906943f9SDavid du Colombier 		print("ohci: bug: donehead changed before ack\n");
1303*906943f9SDavid du Colombier 	ctlr->hcca->donehead = 0;
1304*906943f9SDavid du Colombier 
1305*906943f9SDavid du Colombier 	ctlr->ohci->intrsts = status;
1306*906943f9SDavid du Colombier 	status &= ~Wdh;
1307*906943f9SDavid du Colombier 	status &= ~Sf;
1308*906943f9SDavid du Colombier 	if(status & So){
1309*906943f9SDavid du Colombier 		print("ohci: sched overrun: too much load\n");
1310*906943f9SDavid du Colombier 		ctlr->overrun++;
1311*906943f9SDavid du Colombier 		status &= ~So;
1312*906943f9SDavid du Colombier 	}
1313*906943f9SDavid du Colombier 	if((status & Ue) != 0){
1314*906943f9SDavid du Colombier 		curred = ctlr->ohci->periodcurred;
1315*906943f9SDavid du Colombier 		print("ohci: unrecoverable error frame 0x%.8lux ed 0x%.8lux, "
1316*906943f9SDavid du Colombier 			"ints %d %d %d %d\n",
1317*906943f9SDavid du Colombier 			ctlr->ohci->fmnumber, curred,
1318*906943f9SDavid du Colombier 			ohciinterrupts[Tctl], ohciinterrupts[Tintr],
1319*906943f9SDavid du Colombier 			ohciinterrupts[Tbulk], ohciinterrupts[Tiso]);
1320*906943f9SDavid du Colombier 		if(curred != 0)
1321*906943f9SDavid du Colombier 			dumped(pa2ptr(curred));
1322*906943f9SDavid du Colombier 		status &= ~Ue;
1323*906943f9SDavid du Colombier 	}
1324*906943f9SDavid du Colombier 	if(status != 0)
1325*906943f9SDavid du Colombier 		print("ohci interrupt: unhandled sts 0x%.8lux\n", status);
1326*906943f9SDavid du Colombier 	iunlock(ctlr);
1327*906943f9SDavid du Colombier }
1328*906943f9SDavid du Colombier 
1329*906943f9SDavid du Colombier /*
1330*906943f9SDavid du Colombier  * The old dummy Td is used to implement the new Td.
1331*906943f9SDavid du Colombier  * A new dummy is linked at the end of the old one and
1332*906943f9SDavid du Colombier  * returned, to link further Tds if needed.
1333*906943f9SDavid du Colombier  */
1334*906943f9SDavid du Colombier static Td*
1335*906943f9SDavid du Colombier epgettd(Ep *ep, Qio *io, Td **dtdp, int flags, void *a, int count)
1336*906943f9SDavid du Colombier {
1337*906943f9SDavid du Colombier 	Td *td;
1338*906943f9SDavid du Colombier 	Td *dtd;
1339*906943f9SDavid du Colombier 	Block *bp;
1340*906943f9SDavid du Colombier 
1341*906943f9SDavid du Colombier 	if(ep->maxpkt > 0x2000)
1342*906943f9SDavid du Colombier 		panic("ohci: max packet > two pages");
1343*906943f9SDavid du Colombier 	if(ep->maxpkt < count)
1344*906943f9SDavid du Colombier 		error("maxpkt too short");
1345*906943f9SDavid du Colombier 	bp = allocb(ep->maxpkt);	/* panics if no mem */
1346*906943f9SDavid du Colombier 	assert(bp != nil);
1347*906943f9SDavid du Colombier 	dtd = *dtdp;
1348*906943f9SDavid du Colombier 	td = dtd;
1349*906943f9SDavid du Colombier 	td->bp = bp;
1350*906943f9SDavid du Colombier 	if(count > 0){
1351*906943f9SDavid du Colombier 		td->cbp0 = td->cbp = ptr2pa(bp->wp);
1352*906943f9SDavid du Colombier 		td->be = ptr2pa(bp->wp + count - 1);
1353*906943f9SDavid du Colombier 		if(a != nil){
1354*906943f9SDavid du Colombier 			validaddr((uintptr)a, count, 0);		/* DEBUG */
1355*906943f9SDavid du Colombier 			memmove(bp->wp, a, count);
1356*906943f9SDavid du Colombier 		}
1357*906943f9SDavid du Colombier 		bp->wp += count;
1358*906943f9SDavid du Colombier 	}
1359*906943f9SDavid du Colombier 	td->nbytes = count;
1360*906943f9SDavid du Colombier 	td->ctrl = io->tok|Tdusetog|io->toggle|flags;
1361*906943f9SDavid du Colombier 	if(io->toggle == Tddata0)
1362*906943f9SDavid du Colombier 		io->toggle = Tddata1;
1363*906943f9SDavid du Colombier 	else
1364*906943f9SDavid du Colombier 		io->toggle = Tddata0;
1365*906943f9SDavid du Colombier 	assert(td->ep == ep);
1366*906943f9SDavid du Colombier 	td->io = io;
1367*906943f9SDavid du Colombier  	dtd = tdalloc();	/* new dummy */
1368*906943f9SDavid du Colombier 	dtd->ep = ep;
1369*906943f9SDavid du Colombier 	td->nexttd = ptr2pa(dtd);
1370*906943f9SDavid du Colombier 	td->next = dtd;
1371*906943f9SDavid du Colombier 	*dtdp = dtd;
1372*906943f9SDavid du Colombier 	return td;
1373*906943f9SDavid du Colombier }
1374*906943f9SDavid du Colombier 
1375*906943f9SDavid du Colombier /*
1376*906943f9SDavid du Colombier  * Try to get them idle
1377*906943f9SDavid du Colombier  */
1378*906943f9SDavid du Colombier static void
1379*906943f9SDavid du Colombier aborttds(Qio *io)
1380*906943f9SDavid du Colombier {
1381*906943f9SDavid du Colombier 	Ed *ed;
1382*906943f9SDavid du Colombier 	Td *td;
1383*906943f9SDavid du Colombier 
1384*906943f9SDavid du Colombier 	ed = io->ed;
1385*906943f9SDavid du Colombier 	ed->ctrl |= Edskip;
1386*906943f9SDavid du Colombier 	for(td = ed->tds; td != nil; td = td->next)
1387*906943f9SDavid du Colombier 		if(td->bp != nil)
1388*906943f9SDavid du Colombier 			td->bp->wp = td->bp->rp;
1389*906943f9SDavid du Colombier 	ed->head = (ed->head&0xF) | ed->tail;
1390*906943f9SDavid du Colombier 	if((ed->ctrl & Ediso) == 0)
1391*906943f9SDavid du Colombier 		ed->tds = pa2ptr(ed->tail);
1392*906943f9SDavid du Colombier }
1393*906943f9SDavid du Colombier 
1394*906943f9SDavid du Colombier static int
1395*906943f9SDavid du Colombier epiodone(void *a)
1396*906943f9SDavid du Colombier {
1397*906943f9SDavid du Colombier 	Qio *io;
1398*906943f9SDavid du Colombier 
1399*906943f9SDavid du Colombier 	io = a;
1400*906943f9SDavid du Colombier 	return io->state != Qrun;
1401*906943f9SDavid du Colombier }
1402*906943f9SDavid du Colombier 
1403*906943f9SDavid du Colombier static void
1404*906943f9SDavid du Colombier epiowait(Ctlr *ctlr, Qio *io, int tmout, ulong)
1405*906943f9SDavid du Colombier {
1406*906943f9SDavid du Colombier 	Ed *ed;
1407*906943f9SDavid du Colombier 	int timedout;
1408*906943f9SDavid du Colombier 
1409*906943f9SDavid du Colombier 	ed = io->ed;
1410*906943f9SDavid du Colombier 	if(0)ddqprint("ohci io %#p sleep on ed %#p state %s\n",
1411*906943f9SDavid du Colombier 		io, ed, iosname[io->state]);
1412*906943f9SDavid du Colombier 	timedout = 0;
1413*906943f9SDavid du Colombier 	if(waserror()){
1414*906943f9SDavid du Colombier 		dqprint("ohci io %#p ed %#p timed out\n", io, ed);
1415*906943f9SDavid du Colombier 		timedout++;
1416*906943f9SDavid du Colombier 	}else{
1417*906943f9SDavid du Colombier 		if(tmout == 0)
1418*906943f9SDavid du Colombier 			sleep(io, epiodone, io);
1419*906943f9SDavid du Colombier 		else
1420*906943f9SDavid du Colombier 			tsleep(io, epiodone, io, tmout);
14216a5dc222SDavid du Colombier 		poperror();
14226a5dc222SDavid du Colombier 	}
1423*906943f9SDavid du Colombier 	ilock(ctlr);
1424*906943f9SDavid du Colombier 	if(io->state == Qrun)
1425*906943f9SDavid du Colombier 		timedout = 1;
1426*906943f9SDavid du Colombier 	else if(io->state != Qdone && io->state != Qclose)
1427*906943f9SDavid du Colombier 		panic("epio: ed not done and not closed");
1428*906943f9SDavid du Colombier 	if(timedout){
1429*906943f9SDavid du Colombier 		aborttds(io);
1430*906943f9SDavid du Colombier 		io->err = "request timed out";
1431*906943f9SDavid du Colombier 		iunlock(ctlr);
1432*906943f9SDavid du Colombier 		if(!waserror()){
1433*906943f9SDavid du Colombier 			tsleep(&up->sleep, return0, 0, Abortdelay);
1434*906943f9SDavid du Colombier 			poperror();
1435*906943f9SDavid du Colombier 		}
1436*906943f9SDavid du Colombier 		ilock(ctlr);
1437*906943f9SDavid du Colombier 	}
1438*906943f9SDavid du Colombier 	if(io->state != Qclose)
1439*906943f9SDavid du Colombier 		io->state = Qidle;
1440*906943f9SDavid du Colombier 	iunlock(ctlr);
14416a5dc222SDavid du Colombier }
14426a5dc222SDavid du Colombier 
14436a5dc222SDavid du Colombier /*
1444*906943f9SDavid du Colombier  * Non iso I/O.
1445*906943f9SDavid du Colombier  * To make it work for control transfers, the caller may
1446*906943f9SDavid du Colombier  * lock the Qio for the entire control transfer.
1447*906943f9SDavid du Colombier  * If tmout is not 0 it is a timeout value in ms.
1448*906943f9SDavid du Colombier  *
14496a5dc222SDavid du Colombier  */
1450*906943f9SDavid du Colombier static long
1451*906943f9SDavid du Colombier epio(Ep *ep, Qio *io, void *a, long count, int tmout, int mustlock)
1452*906943f9SDavid du Colombier {
1453*906943f9SDavid du Colombier 	Ed *ed;
1454*906943f9SDavid du Colombier 	Ctlr *ctlr;
1455*906943f9SDavid du Colombier 	char buf[80];
1456*906943f9SDavid du Colombier 	uchar *c;
1457*906943f9SDavid du Colombier 	Td *td;
1458*906943f9SDavid du Colombier 	Td *ltd;
1459*906943f9SDavid du Colombier 	Td *ntd;
1460*906943f9SDavid du Colombier 	Td *td0;
1461*906943f9SDavid du Colombier 	ulong load;
1462*906943f9SDavid du Colombier 	int last;
1463*906943f9SDavid du Colombier 	int ntds;
1464*906943f9SDavid du Colombier 	long tot;
1465*906943f9SDavid du Colombier 	long n;
1466*906943f9SDavid du Colombier 	char *err;
1467*906943f9SDavid du Colombier 
1468*906943f9SDavid du Colombier 	ed = io->ed;
1469*906943f9SDavid du Colombier 	ctlr = ep->hp->aux;
1470*906943f9SDavid du Colombier 	io->debug = ep->debug;
1471*906943f9SDavid du Colombier 	ddeprint("ohci: %s ep%d.%d io %#p count %ld\n",
1472*906943f9SDavid du Colombier 		io->tok == Tdtokin ? "in" : "out",
1473*906943f9SDavid du Colombier 		ep->dev->nb, ep->nb, io, count);
1474*906943f9SDavid du Colombier 	if((debug > 1 || ep->debug > 1) && io->tok != Tdtokin){
1475*906943f9SDavid du Colombier 		seprintdata(buf, buf+sizeof(buf), a, count);
1476*906943f9SDavid du Colombier 		print("\t%s\n", buf);
14776a5dc222SDavid du Colombier 	}
1478*906943f9SDavid du Colombier 	if(mustlock){
1479*906943f9SDavid du Colombier 		qlock(io);
1480*906943f9SDavid du Colombier 		if(waserror()){
1481*906943f9SDavid du Colombier 			qunlock(io);
1482*906943f9SDavid du Colombier 			nexterror();
1483*906943f9SDavid du Colombier 		}
1484*906943f9SDavid du Colombier 	}
1485*906943f9SDavid du Colombier 	io->err = nil;
1486*906943f9SDavid du Colombier 	ilock(ctlr);
1487*906943f9SDavid du Colombier 	if(io->state == Qclose){	/* Tds released by cancelio */
1488*906943f9SDavid du Colombier 		iunlock(ctlr);
1489*906943f9SDavid du Colombier 		error(io->err ? io->err : Eio);
1490*906943f9SDavid du Colombier 	}
1491*906943f9SDavid du Colombier 	if(io->state != Qidle)
1492*906943f9SDavid du Colombier 		panic("epio: qio not idle");
1493*906943f9SDavid du Colombier 	io->state = Qinstall;
1494*906943f9SDavid du Colombier 
1495*906943f9SDavid du Colombier 	c = a;
1496*906943f9SDavid du Colombier 	ltd = td0 = ed->tds;
1497*906943f9SDavid du Colombier 	load = tot = 0;
1498*906943f9SDavid du Colombier 	do{
1499*906943f9SDavid du Colombier 		n = ep->maxpkt;
1500*906943f9SDavid du Colombier 		if(count-tot < n)
1501*906943f9SDavid du Colombier 			n = count-tot;
1502*906943f9SDavid du Colombier 		if(io->tok != Tdtokin)
1503*906943f9SDavid du Colombier 			td = epgettd(ep, io, &ltd, 0, c+tot, n);
1504*906943f9SDavid du Colombier 		else
1505*906943f9SDavid du Colombier 			td = epgettd(ep, io, &ltd, 0, nil, n);
1506*906943f9SDavid du Colombier 		tot += n;
1507*906943f9SDavid du Colombier 		load += ep->load;
1508*906943f9SDavid du Colombier 	}while(tot < count);
1509*906943f9SDavid du Colombier 	if(td0 == nil || ltd == nil || td0 == ltd)
1510*906943f9SDavid du Colombier 		panic("epio: no td");
1511*906943f9SDavid du Colombier 	td->last = 1;
1512*906943f9SDavid du Colombier 	if(debug > 2 || ep->debug > 2)
1513*906943f9SDavid du Colombier 		dumptds(td0, "put td", ep->ttype == Tiso);
1514*906943f9SDavid du Colombier 	iunlock(ctlr);
1515*906943f9SDavid du Colombier 
1516*906943f9SDavid du Colombier 	ilock(ctlr);
1517*906943f9SDavid du Colombier 	if(io->state != Qclose){
1518*906943f9SDavid du Colombier 		io->iotime = TK2MS(MACHP(0)->ticks);
1519*906943f9SDavid du Colombier 		io->state = Qrun;
1520*906943f9SDavid du Colombier 		ed->tail = ptr2pa(ltd);
1521*906943f9SDavid du Colombier 		if(ep->ttype == Tctl)
1522*906943f9SDavid du Colombier 			ctlr->ohci->cmdsts |= Sclf;
1523*906943f9SDavid du Colombier 		else if(ep->ttype == Tbulk)
1524*906943f9SDavid du Colombier 			ctlr->ohci->cmdsts |= Sblf;
1525*906943f9SDavid du Colombier 	}
1526*906943f9SDavid du Colombier 	iunlock(ctlr);
1527*906943f9SDavid du Colombier 
1528*906943f9SDavid du Colombier 	epiowait(ctlr, io, tmout, load);
1529*906943f9SDavid du Colombier 	ilock(ctlr);
1530*906943f9SDavid du Colombier 	if(debug > 1 || ep->debug > 1)
1531*906943f9SDavid du Colombier 		dumptds(td0, "got td", 0);
1532*906943f9SDavid du Colombier 	iunlock(ctlr);
1533*906943f9SDavid du Colombier 
1534*906943f9SDavid du Colombier 	tot = 0;
1535*906943f9SDavid du Colombier 	c = a;
1536*906943f9SDavid du Colombier 	ntds = last = 0;
1537*906943f9SDavid du Colombier 	for(td = td0; td != ltd; td = ntd){
1538*906943f9SDavid du Colombier 		ntds++;
1539*906943f9SDavid du Colombier 		/*
1540*906943f9SDavid du Colombier 		 * If the Td is flagged as last we must
1541*906943f9SDavid du Colombier 		 * ignore any following Td. The block may
1542*906943f9SDavid du Colombier 		 * seem to have bytes but interrupt has not seen
1543*906943f9SDavid du Colombier 		 * those Tds through the done queue, and they are void.
1544*906943f9SDavid du Colombier 		 */
1545*906943f9SDavid du Colombier 		if(last == 0 && tderrs(td) == Tdok){
1546*906943f9SDavid du Colombier 			n = BLEN(td->bp);
1547*906943f9SDavid du Colombier 			tot += n;
1548*906943f9SDavid du Colombier 			if(tdtok(td) == Tdtokin && n > 0){
1549*906943f9SDavid du Colombier 				memmove(c, td->bp->rp, n);
1550*906943f9SDavid du Colombier 				c += n;
1551*906943f9SDavid du Colombier 			}
1552*906943f9SDavid du Colombier 		}
1553*906943f9SDavid du Colombier 		last |= td->last;
1554*906943f9SDavid du Colombier 		ntd = td->next;
1555*906943f9SDavid du Colombier 		tdfree(td);
1556*906943f9SDavid du Colombier 	}
1557*906943f9SDavid du Colombier 	if(edtoggle(ed) == 0)
1558*906943f9SDavid du Colombier 		io->toggle = Tddata0;
1559*906943f9SDavid du Colombier 	else
1560*906943f9SDavid du Colombier 		io->toggle = Tddata1;
1561*906943f9SDavid du Colombier 
1562*906943f9SDavid du Colombier 	err = io->err;
1563*906943f9SDavid du Colombier 	if(mustlock){
1564*906943f9SDavid du Colombier 		qunlock(io);
1565*906943f9SDavid du Colombier 		poperror();
1566*906943f9SDavid du Colombier 	}
1567*906943f9SDavid du Colombier 	ddeprint("ohci: io %#p: %d tds: return %ld err '%s'\n\n",
1568*906943f9SDavid du Colombier 		io, ntds, tot, err);
1569*906943f9SDavid du Colombier 	if(err != nil)
1570*906943f9SDavid du Colombier 		error(err);
1571*906943f9SDavid du Colombier 	if(tot < 0)
1572*906943f9SDavid du Colombier 		error(Eio);
1573*906943f9SDavid du Colombier 	return tot;
1574*906943f9SDavid du Colombier }
1575*906943f9SDavid du Colombier 
1576*906943f9SDavid du Colombier /*
1577*906943f9SDavid du Colombier  * halt condition was cleared on the endpoint. update our toggles.
1578*906943f9SDavid du Colombier  */
1579*906943f9SDavid du Colombier static void
1580*906943f9SDavid du Colombier clrhalt(Ep *ep)
1581*906943f9SDavid du Colombier {
1582*906943f9SDavid du Colombier 	Qio *io;
1583*906943f9SDavid du Colombier 
1584*906943f9SDavid du Colombier 	ep->clrhalt = 0;
1585*906943f9SDavid du Colombier 	switch(ep->ttype){
1586*906943f9SDavid du Colombier 	case Tbulk:
1587*906943f9SDavid du Colombier 	case Tintr:
1588*906943f9SDavid du Colombier 		io = ep->aux;
1589*906943f9SDavid du Colombier 		if(ep->mode != OREAD){
1590*906943f9SDavid du Colombier 			qlock(&io[OWRITE]);
1591*906943f9SDavid du Colombier 			io[OWRITE].toggle = Tddata0;
1592*906943f9SDavid du Colombier 			deprint("ep clrhalt for io %#p\n", io+OWRITE);
1593*906943f9SDavid du Colombier 			qunlock(&io[OWRITE]);
1594*906943f9SDavid du Colombier 		}
1595*906943f9SDavid du Colombier 		if(ep->mode != OWRITE){
1596*906943f9SDavid du Colombier 			qlock(&io[OREAD]);
1597*906943f9SDavid du Colombier 			io[OREAD].toggle = Tddata0;
1598*906943f9SDavid du Colombier 			deprint("ep clrhalt for io %#p\n", io+OREAD);
1599*906943f9SDavid du Colombier 			qunlock(&io[OREAD]);
1600*906943f9SDavid du Colombier 		}
1601*906943f9SDavid du Colombier 		break;
16026a5dc222SDavid du Colombier 	}
16036a5dc222SDavid du Colombier }
16046a5dc222SDavid du Colombier 
16056a5dc222SDavid du Colombier static long
1606*906943f9SDavid du Colombier epread(Ep *ep, void *a, long count)
16076a5dc222SDavid du Colombier {
1608*906943f9SDavid du Colombier 	Ctlio *cio;
1609*906943f9SDavid du Colombier 	Qio *io;
1610*906943f9SDavid du Colombier 	char buf[80];
1611*906943f9SDavid du Colombier 	ulong delta;
16126a5dc222SDavid du Colombier 
1613*906943f9SDavid du Colombier 	if(ep->aux == nil)
1614*906943f9SDavid du Colombier 		panic("epread: not open");
1615*906943f9SDavid du Colombier 
1616*906943f9SDavid du Colombier 	switch(ep->ttype){
1617*906943f9SDavid du Colombier 	case Tctl:
1618*906943f9SDavid du Colombier 		cio = ep->aux;
1619*906943f9SDavid du Colombier 		qlock(cio);
1620*906943f9SDavid du Colombier 		if(waserror()){
1621*906943f9SDavid du Colombier 			qunlock(cio);
1622*906943f9SDavid du Colombier 			nexterror();
16236a5dc222SDavid du Colombier 		}
1624*906943f9SDavid du Colombier 		ddeprint("epread ctl ndata %d\n", cio->ndata);
1625*906943f9SDavid du Colombier 		if(cio->ndata < 0)
1626*906943f9SDavid du Colombier 			error("request expected");
1627*906943f9SDavid du Colombier 		else if(cio->ndata == 0){
1628*906943f9SDavid du Colombier 			cio->ndata = -1;
1629*906943f9SDavid du Colombier 			count = 0;
16306a5dc222SDavid du Colombier 		}else{
1631*906943f9SDavid du Colombier 			if(count > cio->ndata)
1632*906943f9SDavid du Colombier 				count = cio->ndata;
1633*906943f9SDavid du Colombier 			if(count > 0)
1634*906943f9SDavid du Colombier 				memmove(a, cio->data, count);
1635*906943f9SDavid du Colombier 			/* BUG for big transfers */
1636*906943f9SDavid du Colombier 			free(cio->data);
1637*906943f9SDavid du Colombier 			cio->data = nil;
1638*906943f9SDavid du Colombier 			cio->ndata = 0;	/* signal EOF next time */
16396a5dc222SDavid du Colombier 		}
1640*906943f9SDavid du Colombier 		qunlock(cio);
16416a5dc222SDavid du Colombier 		poperror();
1642*906943f9SDavid du Colombier 		if(debug>1 || ep->debug){
1643*906943f9SDavid du Colombier 			seprintdata(buf, buf+sizeof(buf), a, count);
1644*906943f9SDavid du Colombier 			print("epread: %s\n", buf);
1645*906943f9SDavid du Colombier 		}
1646*906943f9SDavid du Colombier 		return count;
1647*906943f9SDavid du Colombier 	case Tbulk:
1648*906943f9SDavid du Colombier 		io = ep->aux;
1649*906943f9SDavid du Colombier 		if(ep->clrhalt)
1650*906943f9SDavid du Colombier 			clrhalt(ep);
1651*906943f9SDavid du Colombier 		return epio(ep, &io[OREAD], a, count, Bulktmout, 1);
1652*906943f9SDavid du Colombier 	case Tintr:
1653*906943f9SDavid du Colombier 		io = ep->aux;
1654*906943f9SDavid du Colombier 		delta = TK2MS(MACHP(0)->ticks) - io[OREAD].iotime + 1;
1655*906943f9SDavid du Colombier 		if(delta < ep->pollival / 2)
1656*906943f9SDavid du Colombier 			tsleep(&up->sleep, return0, 0, ep->pollival/2 - delta);
1657*906943f9SDavid du Colombier 		if(ep->clrhalt)
1658*906943f9SDavid du Colombier 			clrhalt(ep);
1659*906943f9SDavid du Colombier 		return epio(ep, &io[OREAD], a, count, 0, 1);
1660*906943f9SDavid du Colombier 	case Tiso:
1661*906943f9SDavid du Colombier 		panic("ohci: iso read not implemented");
1662*906943f9SDavid du Colombier 		break;
1663*906943f9SDavid du Colombier 	default:
1664*906943f9SDavid du Colombier 		panic("epread: bad ep ttype %d", ep->ttype);
1665*906943f9SDavid du Colombier 	}
1666*906943f9SDavid du Colombier 	return -1;
1667*906943f9SDavid du Colombier }
16686a5dc222SDavid du Colombier 
1669*906943f9SDavid du Colombier /*
1670*906943f9SDavid du Colombier  * Control transfers are one setup write (data0)
1671*906943f9SDavid du Colombier  * plus zero or more reads/writes (data1, data0, ...)
1672*906943f9SDavid du Colombier  * plus a final write/read with data1 to ack.
1673*906943f9SDavid du Colombier  * For both host to device and device to host we perform
1674*906943f9SDavid du Colombier  * the entire transfer when the user writes the request,
1675*906943f9SDavid du Colombier  * and keep any data read from the device for a later read.
1676*906943f9SDavid du Colombier  * We call epio three times instead of placing all Tds at
1677*906943f9SDavid du Colombier  * the same time because doing so leads to crc/tmout errors
1678*906943f9SDavid du Colombier  * for some devices.
1679*906943f9SDavid du Colombier  * Upon errors on the data phase we must still run the status
1680*906943f9SDavid du Colombier  * phase or the device may cease responding in the future.
1681*906943f9SDavid du Colombier  */
1682*906943f9SDavid du Colombier static long
1683*906943f9SDavid du Colombier epctlio(Ep *ep, Ctlio *cio, void *a, long count)
1684*906943f9SDavid du Colombier {
1685*906943f9SDavid du Colombier 	uchar *c;
1686*906943f9SDavid du Colombier 	long len;
1687*906943f9SDavid du Colombier 
1688*906943f9SDavid du Colombier 	ddeprint("epctlio: cio %#p ep%d.%d count %ld\n",
1689*906943f9SDavid du Colombier 		cio, ep->dev->nb, ep->nb, count);
1690*906943f9SDavid du Colombier 	if(count < Rsetuplen)
1691*906943f9SDavid du Colombier 		error("short usb command");
1692*906943f9SDavid du Colombier 	qlock(cio);
1693*906943f9SDavid du Colombier 	free(cio->data);
1694*906943f9SDavid du Colombier 	cio->data = nil;
1695*906943f9SDavid du Colombier 	cio->ndata = 0;
1696*906943f9SDavid du Colombier 	if(waserror()){
1697*906943f9SDavid du Colombier 		qunlock(cio);
1698*906943f9SDavid du Colombier 		free(cio->data);
1699*906943f9SDavid du Colombier 		cio->data = nil;
1700*906943f9SDavid du Colombier 		cio->ndata = 0;
1701*906943f9SDavid du Colombier 		nexterror();
1702*906943f9SDavid du Colombier 	}
1703*906943f9SDavid du Colombier 
1704*906943f9SDavid du Colombier 	/* set the address if unset and out of configuration state */
1705*906943f9SDavid du Colombier 	if(ep->dev->state != Dconfig && cio->usbid == 0){
1706*906943f9SDavid du Colombier 		cio->usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
1707*906943f9SDavid du Colombier 		edsetaddr(cio->ed, cio->usbid);
1708*906943f9SDavid du Colombier 	}
1709*906943f9SDavid du Colombier 	/* adjust maxpkt if the user has learned a different one */
1710*906943f9SDavid du Colombier 	if(edmaxpkt(cio->ed) != ep->maxpkt)
1711*906943f9SDavid du Colombier 		edsetmaxpkt(cio->ed, ep->maxpkt);
1712*906943f9SDavid du Colombier 	c = a;
1713*906943f9SDavid du Colombier 	cio->tok = Tdtoksetup;
1714*906943f9SDavid du Colombier 	cio->toggle = Tddata0;
1715*906943f9SDavid du Colombier 	if(epio(ep, cio, a, Rsetuplen, Ctltmout, 0) < Rsetuplen)
1716*906943f9SDavid du Colombier 		error(Eio);
1717*906943f9SDavid du Colombier 
1718*906943f9SDavid du Colombier 	a = c + Rsetuplen;
1719*906943f9SDavid du Colombier 	count -= Rsetuplen;
1720*906943f9SDavid du Colombier 
1721*906943f9SDavid du Colombier 	cio->toggle = Tddata1;
1722*906943f9SDavid du Colombier 	if(c[Rtype] & Rd2h){
1723*906943f9SDavid du Colombier 		cio->tok = Tdtokin;
1724*906943f9SDavid du Colombier 		len = GET2(c+Rcount);
1725*906943f9SDavid du Colombier 		if(len <= 0)
1726*906943f9SDavid du Colombier 			error("bad length in d2h request");
1727*906943f9SDavid du Colombier 		if(len > Maxctllen)
1728*906943f9SDavid du Colombier 			error("d2h data too large to fit in ohci");
1729*906943f9SDavid du Colombier 		a = cio->data = smalloc(len+1);
1730*906943f9SDavid du Colombier 	}else{
1731*906943f9SDavid du Colombier 		cio->tok = Tdtokout;
1732*906943f9SDavid du Colombier 		len = count;
1733*906943f9SDavid du Colombier 	}
1734*906943f9SDavid du Colombier 	if(len > 0)
1735*906943f9SDavid du Colombier 		if(waserror())
1736*906943f9SDavid du Colombier 			len = -1;
1737*906943f9SDavid du Colombier 		else{
1738*906943f9SDavid du Colombier 			len = epio(ep, cio, a, len, Ctltmout, 0);
1739*906943f9SDavid du Colombier 			poperror();
1740*906943f9SDavid du Colombier 		}
1741*906943f9SDavid du Colombier 	if(c[Rtype] & Rd2h){
1742*906943f9SDavid du Colombier 		count = Rsetuplen;
1743*906943f9SDavid du Colombier 		cio->ndata = len;
1744*906943f9SDavid du Colombier 		cio->tok = Tdtokout;
1745*906943f9SDavid du Colombier 	}else{
1746*906943f9SDavid du Colombier 		if(len < 0)
1747*906943f9SDavid du Colombier 			count = -1;
1748*906943f9SDavid du Colombier 		else
1749*906943f9SDavid du Colombier 			count = Rsetuplen + len;
1750*906943f9SDavid du Colombier 		cio->tok = Tdtokin;
1751*906943f9SDavid du Colombier 	}
1752*906943f9SDavid du Colombier 	cio->toggle = Tddata1;
1753*906943f9SDavid du Colombier 	epio(ep, cio, nil, 0, Ctltmout, 0);
1754*906943f9SDavid du Colombier 	qunlock(cio);
1755*906943f9SDavid du Colombier 	poperror();
1756*906943f9SDavid du Colombier 	ddeprint("epctlio cio %#p return %ld\n", cio, count);
1757*906943f9SDavid du Colombier 	return count;
1758*906943f9SDavid du Colombier }
1759*906943f9SDavid du Colombier 
1760*906943f9SDavid du Colombier /*
1761*906943f9SDavid du Colombier  * Put new samples in the dummy Td.
1762*906943f9SDavid du Colombier  * BUG: This does only a transfer per Td. We could do up to 8.
1763*906943f9SDavid du Colombier  */
1764*906943f9SDavid du Colombier static long
1765*906943f9SDavid du Colombier putsamples(Ctlr *ctlr, Ep *ep, Isoio *iso, uchar *b, long count)
1766*906943f9SDavid du Colombier {
1767*906943f9SDavid du Colombier 	Td *td;
1768*906943f9SDavid du Colombier 	ulong n;
1769*906943f9SDavid du Colombier 
1770*906943f9SDavid du Colombier 	td = pa2ptr(iso->ed->tail);
1771*906943f9SDavid du Colombier 	n = count;
1772*906943f9SDavid du Colombier 	if(n > td->nbytes - BLEN(td->bp))
1773*906943f9SDavid du Colombier 		n = td->nbytes - BLEN(td->bp);
1774*906943f9SDavid du Colombier 	assert(td->bp->wp + n <= td->bp->lim);
1775*906943f9SDavid du Colombier 	memmove(td->bp->wp, b, n);
1776*906943f9SDavid du Colombier 	td->bp->wp += n;
1777*906943f9SDavid du Colombier 	if(BLEN(td->bp) == td->nbytes){	/* full Td: activate it */
1778*906943f9SDavid du Colombier 		ilock(ctlr);
1779*906943f9SDavid du Colombier 		isoadvance(ep, iso, td);
1780*906943f9SDavid du Colombier 		iunlock(ctlr);
1781*906943f9SDavid du Colombier 	}
1782*906943f9SDavid du Colombier 	return n;
1783*906943f9SDavid du Colombier }
1784*906943f9SDavid du Colombier 
1785*906943f9SDavid du Colombier static long
1786*906943f9SDavid du Colombier episowrite(Ep *ep, void *a, long count)
1787*906943f9SDavid du Colombier {
1788*906943f9SDavid du Colombier 	Isoio *iso;
1789*906943f9SDavid du Colombier 	uchar *b;
1790*906943f9SDavid du Colombier 	Ctlr *ctlr;
1791*906943f9SDavid du Colombier 	char *err;
1792*906943f9SDavid du Colombier 	long tot;
1793*906943f9SDavid du Colombier 	long nw;
1794*906943f9SDavid du Colombier 
1795*906943f9SDavid du Colombier 	ctlr = ep->hp->aux;
1796*906943f9SDavid du Colombier 	iso = ep->aux;
1797*906943f9SDavid du Colombier 	iso->debug = ep->debug;
1798*906943f9SDavid du Colombier 
1799*906943f9SDavid du Colombier 	qlock(iso);
1800*906943f9SDavid du Colombier 	if(waserror()){
1801*906943f9SDavid du Colombier 		qunlock(iso);
1802*906943f9SDavid du Colombier 		nexterror();
1803*906943f9SDavid du Colombier 	}
1804*906943f9SDavid du Colombier 	diprint("ohci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
1805*906943f9SDavid du Colombier 	ilock(ctlr);
1806*906943f9SDavid du Colombier 	if(iso->state == Qclose){
1807*906943f9SDavid du Colombier 		iunlock(ctlr);
1808*906943f9SDavid du Colombier 		error(iso->err ? iso->err : Eio);
1809*906943f9SDavid du Colombier 	}
1810*906943f9SDavid du Colombier 	iso->state = Qrun;
1811*906943f9SDavid du Colombier 	b = a;
1812*906943f9SDavid du Colombier 	for(tot = 0; tot < count; tot += nw){
1813*906943f9SDavid du Colombier 		while(isocanwrite(iso) == 0){
1814*906943f9SDavid du Colombier 			iunlock(ctlr);
1815*906943f9SDavid du Colombier 			diprint("ohci: episowrite: %#p sleep\n", iso);
1816*906943f9SDavid du Colombier 			if(waserror()){
1817*906943f9SDavid du Colombier 				if(iso->err == nil)
1818*906943f9SDavid du Colombier 					iso->err = "I/O timed out";
1819*906943f9SDavid du Colombier 				ilock(ctlr);
1820*906943f9SDavid du Colombier 				break;
1821*906943f9SDavid du Colombier 			}
1822*906943f9SDavid du Colombier 			tsleep(iso, isocanwrite, iso, Isotmout);
1823*906943f9SDavid du Colombier 			poperror();
1824*906943f9SDavid du Colombier 			ilock(ctlr);
1825*906943f9SDavid du Colombier 		}
1826*906943f9SDavid du Colombier 		err = iso->err;
1827*906943f9SDavid du Colombier 		iso->err = nil;
1828*906943f9SDavid du Colombier 		if(iso->state == Qclose || err != nil){
1829*906943f9SDavid du Colombier 			iunlock(ctlr);
1830*906943f9SDavid du Colombier 			error(err ? err : Eio);
1831*906943f9SDavid du Colombier 		}
1832*906943f9SDavid du Colombier 		if(iso->state != Qrun)
1833*906943f9SDavid du Colombier 			panic("episowrite: iso not running");
1834*906943f9SDavid du Colombier 		iunlock(ctlr);		/* We could page fault here */
1835*906943f9SDavid du Colombier 		nw = putsamples(ctlr, ep, iso, b+tot, count-tot);
1836*906943f9SDavid du Colombier 		ilock(ctlr);
1837*906943f9SDavid du Colombier 	}
1838*906943f9SDavid du Colombier 	if(iso->state != Qclose)
1839*906943f9SDavid du Colombier 		iso->state = Qdone;
1840*906943f9SDavid du Colombier 	iunlock(ctlr);
1841*906943f9SDavid du Colombier 	err = iso->err;		/* in case it failed early */
1842*906943f9SDavid du Colombier 	iso->err = nil;
1843*906943f9SDavid du Colombier 	qunlock(iso);
1844*906943f9SDavid du Colombier 	poperror();
1845*906943f9SDavid du Colombier 	if(err != nil)
1846*906943f9SDavid du Colombier 		error(err);
1847*906943f9SDavid du Colombier 	diprint("ohci: episowrite: %#p %ld bytes\n", iso, tot);
1848*906943f9SDavid du Colombier 	return tot;
1849*906943f9SDavid du Colombier }
1850*906943f9SDavid du Colombier 
1851*906943f9SDavid du Colombier static long
1852*906943f9SDavid du Colombier epwrite(Ep *ep, void *a, long count)
1853*906943f9SDavid du Colombier {
1854*906943f9SDavid du Colombier 	Qio *io;
1855*906943f9SDavid du Colombier 	Ctlio *cio;
1856*906943f9SDavid du Colombier 	ulong delta;
1857*906943f9SDavid du Colombier 	uchar *b;
1858*906943f9SDavid du Colombier 	long tot;
1859*906943f9SDavid du Colombier 	long nw;
1860*906943f9SDavid du Colombier 
1861*906943f9SDavid du Colombier 	if(ep->aux == nil)
1862*906943f9SDavid du Colombier 		panic("ohci: epwrite: not open");
1863*906943f9SDavid du Colombier 	switch(ep->ttype){
1864*906943f9SDavid du Colombier 	case Tctl:
1865*906943f9SDavid du Colombier 		cio = ep->aux;
1866*906943f9SDavid du Colombier 		return epctlio(ep, cio, a, count);
1867*906943f9SDavid du Colombier 	case Tbulk:
1868*906943f9SDavid du Colombier 		io = ep->aux;
1869*906943f9SDavid du Colombier 		if(ep->clrhalt)
1870*906943f9SDavid du Colombier 			clrhalt(ep);
1871*906943f9SDavid du Colombier 		/*
1872*906943f9SDavid du Colombier 		 * Put at most Tdatomic Tds (512 bytes) at a time.
1873*906943f9SDavid du Colombier 		 * Otherwise some devices produce babble errors.
1874*906943f9SDavid du Colombier 		 */
1875*906943f9SDavid du Colombier 		b = a;
1876*906943f9SDavid du Colombier 		for(tot = 0; tot < count ; tot += nw){
1877*906943f9SDavid du Colombier 			nw = count - tot;
1878*906943f9SDavid du Colombier 			if(nw > Tdatomic * ep->maxpkt)
1879*906943f9SDavid du Colombier 				nw = Tdatomic * ep->maxpkt;
1880*906943f9SDavid du Colombier 			nw = epio(ep, &io[OWRITE], b+tot, nw, Bulktmout, 1);
1881*906943f9SDavid du Colombier 		}
1882*906943f9SDavid du Colombier 		return tot;
1883*906943f9SDavid du Colombier 	case Tintr:
1884*906943f9SDavid du Colombier 		io = ep->aux;
1885*906943f9SDavid du Colombier 		delta = TK2MS(MACHP(0)->ticks) - io[OWRITE].iotime + 1;
1886*906943f9SDavid du Colombier 		if(delta < ep->pollival)
1887*906943f9SDavid du Colombier 			tsleep(&up->sleep, return0, 0, ep->pollival - delta);
1888*906943f9SDavid du Colombier 		if(ep->clrhalt)
1889*906943f9SDavid du Colombier 			clrhalt(ep);
1890*906943f9SDavid du Colombier 		return epio(ep, &io[OWRITE], a, count, 0, 1);
1891*906943f9SDavid du Colombier 	case Tiso:
1892*906943f9SDavid du Colombier 		return episowrite(ep, a, count);
1893*906943f9SDavid du Colombier 	default:
1894*906943f9SDavid du Colombier 		panic("ohci: epwrite: bad ep ttype %d", ep->ttype);
1895*906943f9SDavid du Colombier 	}
1896*906943f9SDavid du Colombier 	return -1;
1897*906943f9SDavid du Colombier }
1898*906943f9SDavid du Colombier 
1899*906943f9SDavid du Colombier static Ed*
1900*906943f9SDavid du Colombier newed(Ctlr *ctlr, Ep *ep, Qio *io, char *)
1901*906943f9SDavid du Colombier {
1902*906943f9SDavid du Colombier 	Ed *ed;
1903*906943f9SDavid du Colombier 	Td *td;
1904*906943f9SDavid du Colombier 
1905*906943f9SDavid du Colombier 	ed = io->ed = edalloc();	/* no errors raised here, really */
1906*906943f9SDavid du Colombier 	td = tdalloc();
1907*906943f9SDavid du Colombier 	td->ep = ep;
1908*906943f9SDavid du Colombier 	td->io = io;
1909*906943f9SDavid du Colombier 	ed->tail =  ptr2pa(td);
1910*906943f9SDavid du Colombier 	ed->head = ptr2pa(td);
1911*906943f9SDavid du Colombier 	ed->tds = td;
1912*906943f9SDavid du Colombier 	ed->ep = ep;
1913*906943f9SDavid du Colombier 	ed->ctrl = (ep->maxpkt & Edmpsmask) << Edmpsshift;
1914*906943f9SDavid du Colombier 	if(ep->ttype == Tiso)
1915*906943f9SDavid du Colombier 		ed->ctrl |= Ediso;
1916*906943f9SDavid du Colombier 	if(waserror()){
1917*906943f9SDavid du Colombier 		edfree(ed);
1918*906943f9SDavid du Colombier 		io->ed = nil;
1919*906943f9SDavid du Colombier 		nexterror();
1920*906943f9SDavid du Colombier 	}
1921*906943f9SDavid du Colombier 	/* For setup endpoints we start with the config address */
1922*906943f9SDavid du Colombier 	if(ep->ttype != Tctl)
1923*906943f9SDavid du Colombier 		edsetaddr(io->ed, io->usbid);
1924*906943f9SDavid du Colombier 	if(ep->dev->speed == Lowspeed)
1925*906943f9SDavid du Colombier 		ed->ctrl |= Edlow;
1926*906943f9SDavid du Colombier 	switch(io->tok){
1927*906943f9SDavid du Colombier 	case Tdtokin:
1928*906943f9SDavid du Colombier 		ed->ctrl |= Edin;
1929*906943f9SDavid du Colombier 		break;
1930*906943f9SDavid du Colombier 	case Tdtokout:
1931*906943f9SDavid du Colombier 		ed->ctrl |= Edout;
1932*906943f9SDavid du Colombier 		break;
1933*906943f9SDavid du Colombier 	default:
1934*906943f9SDavid du Colombier 		ed->ctrl |= Edtddir;	/* Td will say */
1935*906943f9SDavid du Colombier 		break;
1936*906943f9SDavid du Colombier 	}
1937*906943f9SDavid du Colombier 
1938*906943f9SDavid du Colombier 	switch(ep->ttype){
1939*906943f9SDavid du Colombier 	case Tctl:
1940*906943f9SDavid du Colombier 		ilock(ctlr);
1941*906943f9SDavid du Colombier 		edlinked(ed, ctlhd(ctlr));
1942*906943f9SDavid du Colombier 		setctlhd(ctlr, ed);
1943*906943f9SDavid du Colombier 		iunlock(ctlr);
1944*906943f9SDavid du Colombier 		break;
1945*906943f9SDavid du Colombier 	case Tbulk:
1946*906943f9SDavid du Colombier 		ilock(ctlr);
1947*906943f9SDavid du Colombier 		edlinked(ed, bulkhd(ctlr));
1948*906943f9SDavid du Colombier 		setbulkhd(ctlr, ed);
1949*906943f9SDavid du Colombier 		iunlock(ctlr);
1950*906943f9SDavid du Colombier 		break;
1951*906943f9SDavid du Colombier 	case Tintr:
1952*906943f9SDavid du Colombier 	case Tiso:
1953*906943f9SDavid du Colombier 		ilock(ctlr);
1954*906943f9SDavid du Colombier 		schedq(ctlr, io, ep->pollival);
1955*906943f9SDavid du Colombier 		iunlock(ctlr);
1956*906943f9SDavid du Colombier 		break;
1957*906943f9SDavid du Colombier 	default:
1958*906943f9SDavid du Colombier 		panic("ohci: newed: bad ttype");
1959*906943f9SDavid du Colombier 	}
1960*906943f9SDavid du Colombier 	poperror();
1961*906943f9SDavid du Colombier 	return ed;
1962*906943f9SDavid du Colombier }
1963*906943f9SDavid du Colombier 
1964*906943f9SDavid du Colombier static void
1965*906943f9SDavid du Colombier isoopen(Ctlr *ctlr, Ep *ep)
1966*906943f9SDavid du Colombier {
1967*906943f9SDavid du Colombier 	Td *td;
1968*906943f9SDavid du Colombier 	Td *edtds;
1969*906943f9SDavid du Colombier 	Isoio *iso;
1970*906943f9SDavid du Colombier 	int i;
1971*906943f9SDavid du Colombier 
1972*906943f9SDavid du Colombier 	iso = ep->aux;
1973*906943f9SDavid du Colombier 	iso->usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
1974*906943f9SDavid du Colombier 	iso->bw = ep->hz * ep->samplesz;	/* bytes/sec */
1975*906943f9SDavid du Colombier 	if(ep->mode != OWRITE){
1976*906943f9SDavid du Colombier 		print("ohci: bug: iso input streams not implemented\n");
1977*906943f9SDavid du Colombier 		error("ohci iso input streams not implemented");
1978*906943f9SDavid du Colombier 	}else
1979*906943f9SDavid du Colombier 		iso->tok = Tdtokout;
1980*906943f9SDavid du Colombier 
1981*906943f9SDavid du Colombier 	iso->left = 0;
1982*906943f9SDavid du Colombier 	iso->nerrs = 0;
1983*906943f9SDavid du Colombier 	iso->frno = TRUNC(ctlr->ohci->fmnumber + 10, Ntdframes);
1984*906943f9SDavid du Colombier 	iso->nframes = 1000 / ep->pollival;
1985*906943f9SDavid du Colombier 	if(iso->nframes < 10){
1986*906943f9SDavid du Colombier 		print("ohci: isoopen: less than 10 frames; using 10.\n");
1987*906943f9SDavid du Colombier 		iso->nframes = 10;
1988*906943f9SDavid du Colombier 	}
1989*906943f9SDavid du Colombier 	iso->navail = iso->nframes;
1990*906943f9SDavid du Colombier 	iso->atds = edtds = nil;
1991*906943f9SDavid du Colombier 	for(i = 0; i < iso->nframes-1; i++){	/* -1 for dummy */
1992*906943f9SDavid du Colombier 		td = tdalloc();
1993*906943f9SDavid du Colombier 		td->ep = ep;
1994*906943f9SDavid du Colombier 		td->io = iso;
1995*906943f9SDavid du Colombier 		td->bp = allocb(ep->maxpkt);
1996*906943f9SDavid du Colombier 		td->anext = iso->atds;		/* link as avail */
1997*906943f9SDavid du Colombier 		iso->atds = td;
1998*906943f9SDavid du Colombier 		td->next = edtds;
1999*906943f9SDavid du Colombier 		edtds = td;
2000*906943f9SDavid du Colombier 	}
2001*906943f9SDavid du Colombier 	newed(ctlr, ep, iso, "iso");		/* allocates a dummy td */
2002*906943f9SDavid du Colombier 	iso->ed->tds->bp = allocb(ep->maxpkt);	/* but not its block */
2003*906943f9SDavid du Colombier 	iso->ed->tds->next = edtds;
2004*906943f9SDavid du Colombier 	isodtdinit(ep, iso, iso->ed->tds);
2005*906943f9SDavid du Colombier }
2006*906943f9SDavid du Colombier 
2007*906943f9SDavid du Colombier /*
2008*906943f9SDavid du Colombier  * Allocate the endpoint and set it up for I/O
2009*906943f9SDavid du Colombier  * in the controller. This must follow what's said
2010*906943f9SDavid du Colombier  * in Ep regarding configuration, including perhaps
2011*906943f9SDavid du Colombier  * the saved toggles (saved on a previous close of
2012*906943f9SDavid du Colombier  * the endpoint data file by epclose).
2013*906943f9SDavid du Colombier  */
2014*906943f9SDavid du Colombier static void
2015*906943f9SDavid du Colombier epopen(Ep *ep)
2016*906943f9SDavid du Colombier {
2017*906943f9SDavid du Colombier 	Ctlr *ctlr;
2018*906943f9SDavid du Colombier 	Qio *io;
2019*906943f9SDavid du Colombier 	Ctlio *cio;
2020*906943f9SDavid du Colombier 	ulong usbid;
2021*906943f9SDavid du Colombier 
2022*906943f9SDavid du Colombier 	ctlr = ep->hp->aux;
2023*906943f9SDavid du Colombier 	deprint("ohci: epopen ep%d.%d\n", ep->dev->nb, ep->nb);
2024*906943f9SDavid du Colombier 	if(ep->aux != nil)
2025*906943f9SDavid du Colombier 		panic("ohci: epopen called with open ep");
2026*906943f9SDavid du Colombier 	if(waserror()){
2027*906943f9SDavid du Colombier 		free(ep->aux);
2028*906943f9SDavid du Colombier 		ep->aux = nil;
2029*906943f9SDavid du Colombier 		nexterror();
2030*906943f9SDavid du Colombier 	}
2031*906943f9SDavid du Colombier 	switch(ep->ttype){
2032*906943f9SDavid du Colombier 	case Tnone:
2033*906943f9SDavid du Colombier 		error("endpoint not configured");
2034*906943f9SDavid du Colombier 	case Tiso:
2035*906943f9SDavid du Colombier 		ep->aux = smalloc(sizeof(Isoio));
2036*906943f9SDavid du Colombier 		isoopen(ctlr, ep);
2037*906943f9SDavid du Colombier 		break;
2038*906943f9SDavid du Colombier 	case Tctl:
2039*906943f9SDavid du Colombier 		cio = ep->aux = smalloc(sizeof(Ctlio));
2040*906943f9SDavid du Colombier 		cio->debug = ep->debug;
2041*906943f9SDavid du Colombier 		cio->ndata = -1;
2042*906943f9SDavid du Colombier 		cio->data = nil;
2043*906943f9SDavid du Colombier 		cio->tok = -1;	/* invalid; Tds will say */
2044*906943f9SDavid du Colombier 		if(ep->dev->isroot != 0 && ep->nb == 0)	/* root hub */
2045*906943f9SDavid du Colombier 			break;
2046*906943f9SDavid du Colombier 		newed(ctlr, ep, cio, "epc");
2047*906943f9SDavid du Colombier 		break;
2048*906943f9SDavid du Colombier 	case Tbulk:
2049*906943f9SDavid du Colombier 		ep->pollival = 1;	/* assume this; doesn't really matter */
2050*906943f9SDavid du Colombier 		/* and fall... */
2051*906943f9SDavid du Colombier 	case Tintr:
2052*906943f9SDavid du Colombier 		io = ep->aux = smalloc(sizeof(Qio)*2);
2053*906943f9SDavid du Colombier 		io[OREAD].debug = io[OWRITE].debug = ep->debug;
2054*906943f9SDavid du Colombier 		usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
2055*906943f9SDavid du Colombier 		if(ep->mode != OREAD){
2056*906943f9SDavid du Colombier 			if(ep->toggle[OWRITE] != 0)
2057*906943f9SDavid du Colombier 				io[OWRITE].toggle = Tddata1;
2058*906943f9SDavid du Colombier 			else
2059*906943f9SDavid du Colombier 				io[OWRITE].toggle = Tddata0;
2060*906943f9SDavid du Colombier 			io[OWRITE].tok = Tdtokout;
2061*906943f9SDavid du Colombier 			io[OWRITE].usbid = usbid;
2062*906943f9SDavid du Colombier 			io[OWRITE].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */
2063*906943f9SDavid du Colombier 			newed(ctlr, ep, io+OWRITE, "epw");
2064*906943f9SDavid du Colombier 		}
2065*906943f9SDavid du Colombier 		if(ep->mode != OWRITE){
2066*906943f9SDavid du Colombier 			if(ep->toggle[OREAD] != 0)
2067*906943f9SDavid du Colombier 				io[OREAD].toggle = Tddata1;
2068*906943f9SDavid du Colombier 			else
2069*906943f9SDavid du Colombier 				io[OREAD].toggle = Tddata0;
2070*906943f9SDavid du Colombier 			io[OREAD].tok = Tdtokin;
2071*906943f9SDavid du Colombier 			io[OREAD].usbid = usbid;
2072*906943f9SDavid du Colombier 			io[OREAD].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */
2073*906943f9SDavid du Colombier 			newed(ctlr, ep, io+OREAD, "epr");
2074*906943f9SDavid du Colombier 		}
2075*906943f9SDavid du Colombier 		break;
2076*906943f9SDavid du Colombier 	}
2077*906943f9SDavid du Colombier 	deprint("ohci: epopen done:\n");
2078*906943f9SDavid du Colombier 	if(debug || ep->debug)
2079*906943f9SDavid du Colombier 		dump(ep->hp);
2080*906943f9SDavid du Colombier 	poperror();
2081*906943f9SDavid du Colombier }
2082*906943f9SDavid du Colombier 
2083*906943f9SDavid du Colombier static void
2084*906943f9SDavid du Colombier cancelio(Ep *ep, Qio *io)
2085*906943f9SDavid du Colombier {
2086*906943f9SDavid du Colombier 	Ed *ed;
2087*906943f9SDavid du Colombier 	Ctlr *ctlr;
2088*906943f9SDavid du Colombier 
2089*906943f9SDavid du Colombier 	ctlr = ep->hp->aux;
2090*906943f9SDavid du Colombier 	ed = io->ed;
2091*906943f9SDavid du Colombier 
2092*906943f9SDavid du Colombier 	ilock(ctlr);
2093*906943f9SDavid du Colombier 	if(io == nil || io->state == Qclose){
2094*906943f9SDavid du Colombier 		assert(io->ed == nil);
2095*906943f9SDavid du Colombier 		iunlock(ctlr);
2096*906943f9SDavid du Colombier 		return;
2097*906943f9SDavid du Colombier 	}
2098*906943f9SDavid du Colombier 	io->state = Qclose;
2099*906943f9SDavid du Colombier 	io->err = Eio;
2100*906943f9SDavid du Colombier 	aborttds(io);
2101*906943f9SDavid du Colombier 	iunlock(ctlr);
2102*906943f9SDavid du Colombier 	if(!waserror()){
2103*906943f9SDavid du Colombier 		tsleep(&up->sleep, return0, 0, Abortdelay);
2104*906943f9SDavid du Colombier 		poperror();
2105*906943f9SDavid du Colombier 	}
2106*906943f9SDavid du Colombier 
2107*906943f9SDavid du Colombier 	wakeup(io);
2108*906943f9SDavid du Colombier 	qlock(io);
2109*906943f9SDavid du Colombier 	/* wait for epio if running */
2110*906943f9SDavid du Colombier 	qunlock(io);
2111*906943f9SDavid du Colombier 
2112*906943f9SDavid du Colombier 	ilock(ctlr);
2113*906943f9SDavid du Colombier 	switch(ep->ttype){
2114*906943f9SDavid du Colombier 	case Tctl:
2115*906943f9SDavid du Colombier 		unlinkctl(ctlr, ed);
2116*906943f9SDavid du Colombier 		break;
2117*906943f9SDavid du Colombier 	case Tbulk:
2118*906943f9SDavid du Colombier 		unlinkbulk(ctlr, ed);
2119*906943f9SDavid du Colombier 		break;
2120*906943f9SDavid du Colombier 	case Tintr:
2121*906943f9SDavid du Colombier 	case Tiso:
2122*906943f9SDavid du Colombier 		unschedq(ctlr, io);
2123*906943f9SDavid du Colombier 		break;
2124*906943f9SDavid du Colombier 	default:
2125*906943f9SDavid du Colombier 		panic("ohci cancelio: bad ttype");
2126*906943f9SDavid du Colombier 	}
2127*906943f9SDavid du Colombier 	iunlock(ctlr);
2128*906943f9SDavid du Colombier 	edfree(io->ed);
2129*906943f9SDavid du Colombier 	io->ed = nil;
2130*906943f9SDavid du Colombier }
2131*906943f9SDavid du Colombier 
2132*906943f9SDavid du Colombier static void
2133*906943f9SDavid du Colombier epclose(Ep *ep)
2134*906943f9SDavid du Colombier {
2135*906943f9SDavid du Colombier 	Ctlio *cio;
2136*906943f9SDavid du Colombier 	Isoio *iso;
2137*906943f9SDavid du Colombier 	Qio *io;
2138*906943f9SDavid du Colombier 
2139*906943f9SDavid du Colombier 	deprint("ohci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
2140*906943f9SDavid du Colombier 	if(ep->aux == nil)
2141*906943f9SDavid du Colombier 		panic("ohci: epclose called with closed ep");
2142*906943f9SDavid du Colombier 	switch(ep->ttype){
2143*906943f9SDavid du Colombier 	case Tctl:
2144*906943f9SDavid du Colombier 		cio = ep->aux;
2145*906943f9SDavid du Colombier 		cancelio(ep, cio);
2146*906943f9SDavid du Colombier 		free(cio->data);
2147*906943f9SDavid du Colombier 		cio->data = nil;
2148*906943f9SDavid du Colombier 		break;
2149*906943f9SDavid du Colombier 	case Tbulk:
2150*906943f9SDavid du Colombier 	case Tintr:
2151*906943f9SDavid du Colombier 		io = ep->aux;
2152*906943f9SDavid du Colombier 		if(ep->mode != OWRITE){
2153*906943f9SDavid du Colombier 			cancelio(ep, &io[OREAD]);
2154*906943f9SDavid du Colombier 			if(io[OREAD].toggle == Tddata1)
2155*906943f9SDavid du Colombier 				ep->toggle[OREAD] = 1;
2156*906943f9SDavid du Colombier 		}
2157*906943f9SDavid du Colombier 		if(ep->mode != OREAD){
2158*906943f9SDavid du Colombier 			cancelio(ep, &io[OWRITE]);
2159*906943f9SDavid du Colombier 			if(io[OWRITE].toggle == Tddata1)
2160*906943f9SDavid du Colombier 				ep->toggle[OWRITE] = 1;
2161*906943f9SDavid du Colombier 		}
2162*906943f9SDavid du Colombier 		break;
2163*906943f9SDavid du Colombier 	case Tiso:
2164*906943f9SDavid du Colombier 		iso = ep->aux;
2165*906943f9SDavid du Colombier 		cancelio(ep, iso);
2166*906943f9SDavid du Colombier 		break;
2167*906943f9SDavid du Colombier 	default:
2168*906943f9SDavid du Colombier 		panic("epclose: bad ttype %d", ep->ttype);
2169*906943f9SDavid du Colombier 	}
2170*906943f9SDavid du Colombier 
2171*906943f9SDavid du Colombier 	deprint("ohci: epclose ep%d.%d: done\n", ep->dev->nb, ep->nb);
2172*906943f9SDavid du Colombier 	free(ep->aux);
2173*906943f9SDavid du Colombier 	ep->aux = nil;
2174*906943f9SDavid du Colombier }
2175*906943f9SDavid du Colombier 
2176*906943f9SDavid du Colombier static int
2177*906943f9SDavid du Colombier portreset(Hci *hp, int port, int on)
2178*906943f9SDavid du Colombier {
2179*906943f9SDavid du Colombier 	Ctlr *ctlr;
2180*906943f9SDavid du Colombier 
2181*906943f9SDavid du Colombier 	if(on == 0)
2182*906943f9SDavid du Colombier 		return 0;
2183*906943f9SDavid du Colombier 
2184*906943f9SDavid du Colombier 	ctlr = hp->aux;
2185*906943f9SDavid du Colombier 	qlock(&ctlr->resetl);
2186*906943f9SDavid du Colombier 	if(waserror()){
2187*906943f9SDavid du Colombier 		qunlock(&ctlr->resetl);
2188*906943f9SDavid du Colombier 		nexterror();
2189*906943f9SDavid du Colombier 	}
2190*906943f9SDavid du Colombier 	ilock(ctlr);
2191*906943f9SDavid du Colombier 	ctlr->ohci->rhportsts[port - 1] = Spp | Spr;
2192*906943f9SDavid du Colombier 	while((ctlr->ohci->rhportsts[port - 1] & Prsc) == 0){
2193*906943f9SDavid du Colombier 		iunlock(ctlr);
2194*906943f9SDavid du Colombier 		dprint("ohci: portreset, wait for reset complete\n");
2195*906943f9SDavid du Colombier 		ilock(ctlr);
2196*906943f9SDavid du Colombier 	}
2197*906943f9SDavid du Colombier 	ctlr->ohci->rhportsts[port - 1] = Prsc;
2198*906943f9SDavid du Colombier 	iunlock(ctlr);
2199*906943f9SDavid du Colombier 	poperror();
2200*906943f9SDavid du Colombier 	qunlock(&ctlr->resetl);
2201*906943f9SDavid du Colombier 	return 0;
2202*906943f9SDavid du Colombier }
2203*906943f9SDavid du Colombier 
2204*906943f9SDavid du Colombier static int
2205*906943f9SDavid du Colombier portenable(Hci *hp, int port, int on)
2206*906943f9SDavid du Colombier {
2207*906943f9SDavid du Colombier 	Ctlr *ctlr;
2208*906943f9SDavid du Colombier 
2209*906943f9SDavid du Colombier 	ctlr = hp->aux;
2210*906943f9SDavid du Colombier 	dprint("ohci: %#p port %d enable=%d\n", ctlr->ohci, port, on);
2211*906943f9SDavid du Colombier 	qlock(&ctlr->resetl);
2212*906943f9SDavid du Colombier 	if(waserror()){
2213*906943f9SDavid du Colombier 		qunlock(&ctlr->resetl);
2214*906943f9SDavid du Colombier 		nexterror();
2215*906943f9SDavid du Colombier 	}
2216*906943f9SDavid du Colombier 	ilock(ctlr);
2217*906943f9SDavid du Colombier 	if(on)
2218*906943f9SDavid du Colombier 		ctlr->ohci->rhportsts[port - 1] = Spe | Spp;
2219*906943f9SDavid du Colombier 	else
2220*906943f9SDavid du Colombier 		ctlr->ohci->rhportsts[port - 1] = Cpe;
2221*906943f9SDavid du Colombier 	iunlock(ctlr);
2222*906943f9SDavid du Colombier 	tsleep(&up->sleep, return0, 0, Enabledelay);
2223*906943f9SDavid du Colombier 	poperror();
2224*906943f9SDavid du Colombier 	qunlock(&ctlr->resetl);
2225*906943f9SDavid du Colombier 	return 0;
2226*906943f9SDavid du Colombier }
2227*906943f9SDavid du Colombier 
2228*906943f9SDavid du Colombier static int
2229*906943f9SDavid du Colombier portstatus(Hci *hp, int port)
2230*906943f9SDavid du Colombier {
2231*906943f9SDavid du Colombier 	int v;
2232*906943f9SDavid du Colombier 	Ctlr *ub;
2233*906943f9SDavid du Colombier 	ulong ohcistatus;
2234*906943f9SDavid du Colombier 
2235*906943f9SDavid du Colombier 	/*
2236*906943f9SDavid du Colombier 	 * We must return status bits as a
2237*906943f9SDavid du Colombier 	 * get port status hub request would do.
2238*906943f9SDavid du Colombier 	 */
2239*906943f9SDavid du Colombier 	ub = hp->aux;
2240*906943f9SDavid du Colombier 	ohcistatus = ub->ohci->rhportsts[port - 1];
2241*906943f9SDavid du Colombier 	v = 0;
2242*906943f9SDavid du Colombier 	if(ohcistatus & Ccs)
2243*906943f9SDavid du Colombier 		v |= HPpresent;
2244*906943f9SDavid du Colombier 	if(ohcistatus & Pes)
2245*906943f9SDavid du Colombier 		v |= HPenable;
2246*906943f9SDavid du Colombier 	if(ohcistatus & Pss)
2247*906943f9SDavid du Colombier 		v |= HPsuspend;
2248*906943f9SDavid du Colombier 	if(ohcistatus & Prs)
2249*906943f9SDavid du Colombier 		v |= HPreset;
2250*906943f9SDavid du Colombier 	else {
2251*906943f9SDavid du Colombier 		/* port is not in reset; these potential writes are ok */
2252*906943f9SDavid du Colombier 		if(ohcistatus & Csc){
2253*906943f9SDavid du Colombier 			v |= HPstatuschg;
2254*906943f9SDavid du Colombier 			ub->ohci->rhportsts[port - 1] = Csc;
2255*906943f9SDavid du Colombier 		}
2256*906943f9SDavid du Colombier 		if(ohcistatus & Pesc){
2257*906943f9SDavid du Colombier 			v |= HPchange;
2258*906943f9SDavid du Colombier 			ub->ohci->rhportsts[port - 1] = Pesc;
22596a5dc222SDavid du Colombier 		}
22606a5dc222SDavid du Colombier 	}
2261*906943f9SDavid du Colombier 	if(ohcistatus & Lsda)
2262*906943f9SDavid du Colombier 		v |= HPslow;
2263*906943f9SDavid du Colombier 	if(v & (HPstatuschg|HPchange))
2264*906943f9SDavid du Colombier 		ddprint("ohci port %d sts %#ulx hub sts %#x\n", port, ohcistatus, v);
2265*906943f9SDavid du Colombier 	return v;
2266*906943f9SDavid du Colombier }
2267*906943f9SDavid du Colombier 
2268*906943f9SDavid du Colombier static void
2269*906943f9SDavid du Colombier dumpohci(Ctlr *ctlr)
2270*906943f9SDavid du Colombier {
2271*906943f9SDavid du Colombier 	int i;
2272*906943f9SDavid du Colombier 	ulong *ohci;
2273*906943f9SDavid du Colombier 
2274*906943f9SDavid du Colombier 	ohci = &ctlr->ohci->revision;
2275*906943f9SDavid du Colombier 	print("ohci registers: \n");
2276*906943f9SDavid du Colombier 	for(i = 0; i < sizeof(Ohci)/sizeof(ulong); i++)
2277*906943f9SDavid du Colombier 		if(i < 3 || ohci[i] != 0)
2278*906943f9SDavid du Colombier 			print("\t[%#2.2x]\t%#8.8ulx\n", i * 4, ohci[i]);
2279*906943f9SDavid du Colombier 	print("\n");
2280*906943f9SDavid du Colombier }
2281*906943f9SDavid du Colombier 
2282*906943f9SDavid du Colombier static void
2283*906943f9SDavid du Colombier init(Hci *hp)
2284*906943f9SDavid du Colombier {
2285*906943f9SDavid du Colombier 	Ctlr *ctlr;
2286*906943f9SDavid du Colombier 	Ohci *ohci;
2287*906943f9SDavid du Colombier 	int i;
2288*906943f9SDavid du Colombier 	ulong ival;
2289*906943f9SDavid du Colombier 	ulong ctrl;
2290*906943f9SDavid du Colombier 	ulong fmi;
2291*906943f9SDavid du Colombier 
2292*906943f9SDavid du Colombier 	ctlr = hp->aux;
2293*906943f9SDavid du Colombier 	dprint("ohci %#p init\n", ctlr->ohci);
2294*906943f9SDavid du Colombier 	ohci = ctlr->ohci;
2295*906943f9SDavid du Colombier 
2296*906943f9SDavid du Colombier 	fmi =  ctlr->ohci->fminterval;
2297*906943f9SDavid du Colombier 	ctlr->ohci->cmdsts = Shcr;         /* reset the block */
2298*906943f9SDavid du Colombier 	while(ctlr->ohci->cmdsts & Shcr)
2299*906943f9SDavid du Colombier 		delay(1);  /* wait till reset complete, Ohci says 10us max. */
2300*906943f9SDavid du Colombier 	ctlr->ohci->fminterval = fmi;
2301*906943f9SDavid du Colombier 
2302*906943f9SDavid du Colombier 	/*
2303*906943f9SDavid du Colombier 	 * now that soft reset is done we are in suspend state.
2304*906943f9SDavid du Colombier 	 * Setup registers which take in suspend state
2305*906943f9SDavid du Colombier 	 * (will only be here for 2ms).
2306*906943f9SDavid du Colombier 	 */
2307*906943f9SDavid du Colombier 
2308*906943f9SDavid du Colombier 	ctlr->ohci->hcca = ptr2pa(ctlr->hcca);
2309*906943f9SDavid du Colombier 	setctlhd(ctlr, nil);
2310*906943f9SDavid du Colombier 	ctlr->ohci->ctlcurred = 0;
2311*906943f9SDavid du Colombier 	setbulkhd(ctlr, nil);
2312*906943f9SDavid du Colombier 	ctlr->ohci->bulkcurred = 0;
2313*906943f9SDavid du Colombier 
2314*906943f9SDavid du Colombier 	ohci->intrenable = Mie | Wdh | Ue;
2315*906943f9SDavid du Colombier 	ohci->control |= Ccle | Cble | Cple | Cie | Cfsoper;
2316*906943f9SDavid du Colombier 
2317*906943f9SDavid du Colombier 	/* set frame after operational */
2318*906943f9SDavid du Colombier 	ival = ohci->fminterval & ~(Fmaxpktmask << Fmaxpktshift);
2319*906943f9SDavid du Colombier 	ohci->fminterval = ival | (5120 << Fmaxpktshift);
2320*906943f9SDavid du Colombier 	ohci->rhdesca = Nps;	/* no power switching */
2321*906943f9SDavid du Colombier 	if(ohci->rhdesca & Nps){
2322*906943f9SDavid du Colombier 		dprint("ohci: ports are not power switched\n");
2323*906943f9SDavid du Colombier 	}else{
2324*906943f9SDavid du Colombier 		dprint("ohci: ports are power switched\n");
2325*906943f9SDavid du Colombier 		ohci->rhdesca &= ~Psm;
2326*906943f9SDavid du Colombier 		ohci->rhsts &= ~Lpsc;
2327*906943f9SDavid du Colombier 	}
2328*906943f9SDavid du Colombier 	for(i = 0; i < ctlr->nports; i++)	/* paranoia */
2329*906943f9SDavid du Colombier 		ohci->rhportsts[i] = 0;		/* this has no effect */
2330*906943f9SDavid du Colombier 	delay(50);
2331*906943f9SDavid du Colombier 
2332*906943f9SDavid du Colombier 	for(i = 0; i < ctlr->nports; i++)
2333*906943f9SDavid du Colombier 		ohci->rhportsts[i] = Spp | Spr;
2334*906943f9SDavid du Colombier 	delay(100);
2335*906943f9SDavid du Colombier 
2336*906943f9SDavid du Colombier 	ctrl = ohci->control;
2337*906943f9SDavid du Colombier 	if((ctrl & Cfsmask) != Cfsoper){
2338*906943f9SDavid du Colombier 		ctrl = (ctrl & ~Cfsmask) | Cfsoper;
2339*906943f9SDavid du Colombier 		ohci->control = ctrl;
2340*906943f9SDavid du Colombier 		ohci->rhsts = Sgp;
2341*906943f9SDavid du Colombier 	}
2342*906943f9SDavid du Colombier 
2343*906943f9SDavid du Colombier 	if(debug > 1)
2344*906943f9SDavid du Colombier 		dumpohci(ctlr);
2345*906943f9SDavid du Colombier }
2346*906943f9SDavid du Colombier 
2347*906943f9SDavid du Colombier static void
2348*906943f9SDavid du Colombier scanpci(void)
2349*906943f9SDavid du Colombier {
2350*906943f9SDavid du Colombier 	ulong mem;
2351*906943f9SDavid du Colombier 	Ctlr *ctlr;
2352*906943f9SDavid du Colombier 	Pcidev *p;
2353*906943f9SDavid du Colombier 	int i;
2354*906943f9SDavid du Colombier 	static int already = 0;
2355*906943f9SDavid du Colombier 
2356*906943f9SDavid du Colombier 	if(already)
2357*906943f9SDavid du Colombier 		return;
2358*906943f9SDavid du Colombier 	already = 1;
2359*906943f9SDavid du Colombier 	p = nil;
2360*906943f9SDavid du Colombier 	while(p = pcimatch(p, 0, 0)) {
2361*906943f9SDavid du Colombier 		/*
2362*906943f9SDavid du Colombier 		 * Find Ohci controllers (Programming Interface = 0x10).
2363*906943f9SDavid du Colombier 		 */
2364*906943f9SDavid du Colombier 		if(p->ccrb != Pcibcserial || p->ccru != Pciscusb ||
2365*906943f9SDavid du Colombier 		    p->ccrp != 0x10)
2366*906943f9SDavid du Colombier 			continue;
2367*906943f9SDavid du Colombier 		mem = p->mem[0].bar & ~0x0F;
2368*906943f9SDavid du Colombier 		dprint("ohci: %x/%x port 0x%lux size 0x%x irq %d\n",
2369*906943f9SDavid du Colombier 			p->vid, p->did, mem, p->mem[0].size, p->intl);
2370*906943f9SDavid du Colombier 		if(mem == 0){
2371*906943f9SDavid du Colombier 			print("ohci: failed to map registers\n");
2372*906943f9SDavid du Colombier 			continue;
2373*906943f9SDavid du Colombier 		}
2374*906943f9SDavid du Colombier 		if(p->intl == 0xFF || p->intl == 0) {
2375*906943f9SDavid du Colombier 			print("ohci: no irq assigned for port %#lux\n", mem);
2376*906943f9SDavid du Colombier 			continue;
2377*906943f9SDavid du Colombier 		}
2378*906943f9SDavid du Colombier 
2379*906943f9SDavid du Colombier 		ctlr = mallocz(sizeof(Ctlr), 1);
2380*906943f9SDavid du Colombier 		ctlr->pcidev = p;
2381*906943f9SDavid du Colombier 		ctlr->ohci = vmap(mem, p->mem[0].size);
2382*906943f9SDavid du Colombier 		dprint("scanpci: ctlr %#p, ohci %#p\n", ctlr, ctlr->ohci);
2383*906943f9SDavid du Colombier 		pcisetbme(p);
2384*906943f9SDavid du Colombier 		pcisetpms(p, 0);
2385*906943f9SDavid du Colombier 		for(i = 0; i < Nhcis; i++)
2386*906943f9SDavid du Colombier 			if(ctlrs[i] == nil){
2387*906943f9SDavid du Colombier 				ctlrs[i] = ctlr;
2388*906943f9SDavid du Colombier 				break;
2389*906943f9SDavid du Colombier 			}
2390*906943f9SDavid du Colombier 		if(i == Nhcis)
2391*906943f9SDavid du Colombier 			print("ohci: bug: no more controllers\n");
2392*906943f9SDavid du Colombier 	}
2393*906943f9SDavid du Colombier }
2394*906943f9SDavid du Colombier 
2395*906943f9SDavid du Colombier static void
2396*906943f9SDavid du Colombier usbdebug(Hci*, int d)
2397*906943f9SDavid du Colombier {
2398*906943f9SDavid du Colombier 	debug = d;
23996a5dc222SDavid du Colombier }
24006a5dc222SDavid du Colombier 
24016a5dc222SDavid du Colombier /*
24026a5dc222SDavid du Colombier  * build the periodic scheduling tree:
24036a5dc222SDavid du Colombier  * framesize must be a multiple of the tree size
24046a5dc222SDavid du Colombier  */
2405*906943f9SDavid du Colombier static void
2406*906943f9SDavid du Colombier mkqhtree(Ctlr *ctlr)
24076a5dc222SDavid du Colombier {
24086a5dc222SDavid du Colombier 	int i, n, d, o, leaf0, depth;
2409*906943f9SDavid du Colombier 	Ed **tree;
2410*906943f9SDavid du Colombier 	Qtree *qt;
24116a5dc222SDavid du Colombier 
24126a5dc222SDavid du Colombier 	depth = flog2(32);
24136a5dc222SDavid du Colombier 	n = (1 << (depth+1)) - 1;
24146a5dc222SDavid du Colombier 	qt = mallocz(sizeof(*qt), 1);
24156a5dc222SDavid du Colombier 	if(qt == nil)
2416*906943f9SDavid du Colombier 		panic("usb: can't allocate scheduling tree");
24176a5dc222SDavid du Colombier 	qt->nel = n;
24186a5dc222SDavid du Colombier 	qt->depth = depth;
24196a5dc222SDavid du Colombier 	qt->bw = mallocz(n * sizeof(qt->bw), 1);
2420*906943f9SDavid du Colombier 	qt->root = tree = mallocz(n * sizeof(Ed *), 1);
2421*906943f9SDavid du Colombier 	if(qt->bw == nil || qt->root == nil)
2422*906943f9SDavid du Colombier 		panic("usb: can't allocate scheduling tree");
2423*906943f9SDavid du Colombier 	for(i = 0; i < n; i++){
2424*906943f9SDavid du Colombier 		if((tree[i] = edalloc()) == nil)
2425*906943f9SDavid du Colombier 			panic("mkqhtree");
2426*906943f9SDavid du Colombier 		tree[i]->ctrl = (8 << Edmpsshift);	/* not needed */
2427*906943f9SDavid du Colombier 		tree[i]->ctrl |= Edskip;
2428*906943f9SDavid du Colombier 
2429*906943f9SDavid du Colombier 		if(i > 0)
2430*906943f9SDavid du Colombier 			edlinked(tree[i], tree[(i-1)/2]);
2431*906943f9SDavid du Colombier 		else
2432*906943f9SDavid du Colombier 			edlinked(tree[i], nil);
24336a5dc222SDavid du Colombier 	}
2434*906943f9SDavid du Colombier 	ctlr->ntree = i;
2435*906943f9SDavid du Colombier 	dprint("ohci: tree: %d endpoints allocated\n", i);
24366a5dc222SDavid du Colombier 
24376a5dc222SDavid du Colombier 	/* distribute leaves evenly round the frame list */
24386a5dc222SDavid du Colombier 	leaf0 = n / 2;
24396a5dc222SDavid du Colombier 	for(i = 0; i < 32; i++){
24406a5dc222SDavid du Colombier 		o = 0;
24416a5dc222SDavid du Colombier 		for(d = 0; d < depth; d++){
24426a5dc222SDavid du Colombier 			o <<= 1;
24436a5dc222SDavid du Colombier 			if(i & (1 << d))
24446a5dc222SDavid du Colombier 				o |= 1;
24456a5dc222SDavid du Colombier 		}
24466a5dc222SDavid du Colombier 		if(leaf0 + o >= n){
24476a5dc222SDavid du Colombier 			print("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n);
24486a5dc222SDavid du Colombier 			break;
24496a5dc222SDavid du Colombier 		}
2450*906943f9SDavid du Colombier 		ctlr->hcca->intrtable[i] = ptr2pa(tree[leaf0 + o]);
24516a5dc222SDavid du Colombier 	}
2452*906943f9SDavid du Colombier 	ctlr->tree = qt;
24536a5dc222SDavid du Colombier }
24546a5dc222SDavid du Colombier 
24556a5dc222SDavid du Colombier static void
2456*906943f9SDavid du Colombier ohcimeminit(Ctlr *ctlr)
24576a5dc222SDavid du Colombier {
2458*906943f9SDavid du Colombier 	Hcca *hcca;
24596a5dc222SDavid du Colombier 
2460*906943f9SDavid du Colombier 	edfree(edalloc());	/* allocate pools now */
2461*906943f9SDavid du Colombier 	tdfree(tdalloc());
2462*906943f9SDavid du Colombier 
2463*906943f9SDavid du Colombier 	hcca = xspanalloc(sizeof(Hcca), 256, 0);
2464*906943f9SDavid du Colombier 	if(hcca == nil)
2465*906943f9SDavid du Colombier 		panic("usbhreset: no memory for Hcca");
2466*906943f9SDavid du Colombier 	memset(hcca, 0, sizeof(*hcca));
2467*906943f9SDavid du Colombier 	ctlr->hcca = hcca;
2468*906943f9SDavid du Colombier 
2469*906943f9SDavid du Colombier 	mkqhtree(ctlr);
24706a5dc222SDavid du Colombier }
2471*906943f9SDavid du Colombier 
2472*906943f9SDavid du Colombier static void
2473*906943f9SDavid du Colombier ohcireset(Ctlr *ctlr)
2474*906943f9SDavid du Colombier {
24756a5dc222SDavid du Colombier 	ilock(ctlr);
2476*906943f9SDavid du Colombier 	dprint("ohci %#p reset\n", ctlr->ohci);
24776a5dc222SDavid du Colombier 
24786a5dc222SDavid du Colombier 	/*
24796a5dc222SDavid du Colombier 	 * usually enter here in reset, wait till its through,
24806a5dc222SDavid du Colombier 	 * then do our own so we are on known timing conditions.
2481*906943f9SDavid du Colombier 	 * Is this needed?
24826a5dc222SDavid du Colombier 	 */
24836a5dc222SDavid du Colombier 	delay(100);
2484*906943f9SDavid du Colombier 	ctlr->ohci->control = 0;
2485*906943f9SDavid du Colombier 	delay(100);
24866a5dc222SDavid du Colombier 
24876a5dc222SDavid du Colombier 	/* legacy support register: turn off lunacy mode */
2488*906943f9SDavid du Colombier 	pcicfgw16(ctlr->pcidev, 0xc0, 0x2000);
24896a5dc222SDavid du Colombier 
2490*906943f9SDavid du Colombier 	iunlock(ctlr);
24916a5dc222SDavid du Colombier }
24926a5dc222SDavid du Colombier 
2493*906943f9SDavid du Colombier static int
2494*906943f9SDavid du Colombier reset(Hci *hp)
2495*906943f9SDavid du Colombier {
2496*906943f9SDavid du Colombier 	static Lock resetlck;
2497*906943f9SDavid du Colombier 	int i;
2498*906943f9SDavid du Colombier 	Ctlr *ctlr;
2499*906943f9SDavid du Colombier 	Pcidev *p;
25006a5dc222SDavid du Colombier 
2501*906943f9SDavid du Colombier 	if(getconf("*nousbohci"))
25026a5dc222SDavid du Colombier 		return -1;
25036a5dc222SDavid du Colombier 
2504*906943f9SDavid du Colombier 	ilock(&resetlck);
2505*906943f9SDavid du Colombier 	scanpci();
25066a5dc222SDavid du Colombier 
25076a5dc222SDavid du Colombier 	/*
2508*906943f9SDavid du Colombier 	 * Any adapter matches if no hp->port is supplied,
2509*906943f9SDavid du Colombier 	 * otherwise the ports must match.
25106a5dc222SDavid du Colombier 	 */
2511*906943f9SDavid du Colombier 	ctlr = nil;
2512*906943f9SDavid du Colombier 	for(i = 0; i < Nhcis && ctlrs[i] != nil; i++){
2513*906943f9SDavid du Colombier 		ctlr = ctlrs[i];
2514*906943f9SDavid du Colombier 		if(ctlr->active == 0)
2515*906943f9SDavid du Colombier 		if(hp->port == 0 || hp->port == (uintptr)ctlr->ohci){
2516*906943f9SDavid du Colombier 			ctlr->active = 1;
2517*906943f9SDavid du Colombier 			break;
25186a5dc222SDavid du Colombier 		}
2519*906943f9SDavid du Colombier 	}
2520*906943f9SDavid du Colombier 	iunlock(&resetlck);
2521*906943f9SDavid du Colombier 	if(ctlrs[i] == nil || i == Nhcis)
2522*906943f9SDavid du Colombier 		return -1;
2523*906943f9SDavid du Colombier 	if(ctlr->ohci->control == ~0)
2524*906943f9SDavid du Colombier 		return -1;
2525*906943f9SDavid du Colombier 
25266a5dc222SDavid du Colombier 
25276a5dc222SDavid du Colombier 	p = ctlr->pcidev;
2528*906943f9SDavid du Colombier 	hp->aux = ctlr;
2529*906943f9SDavid du Colombier 	hp->port = (uintptr)ctlr->ohci;
2530*906943f9SDavid du Colombier 	hp->irq = p->intl;
2531*906943f9SDavid du Colombier 	hp->tbdf = p->tbdf;
2532*906943f9SDavid du Colombier 	ctlr->nports = hp->nports = ctlr->ohci->rhdesca & 0xff;
2533*906943f9SDavid du Colombier 
2534*906943f9SDavid du Colombier 	ohcireset(ctlr);
2535*906943f9SDavid du Colombier 	ohcimeminit(ctlr);
25366a5dc222SDavid du Colombier 
25376a5dc222SDavid du Colombier 	/*
2538*906943f9SDavid du Colombier 	 * Linkage to the generic HCI driver.
25396a5dc222SDavid du Colombier 	 */
2540*906943f9SDavid du Colombier 	hp->init = init;
2541*906943f9SDavid du Colombier 	hp->dump = dump;
2542*906943f9SDavid du Colombier 	hp->interrupt = interrupt;
2543*906943f9SDavid du Colombier 	hp->epopen = epopen;
2544*906943f9SDavid du Colombier 	hp->epclose = epclose;
2545*906943f9SDavid du Colombier 	hp->epread = epread;
2546*906943f9SDavid du Colombier 	hp->epwrite = epwrite;
2547*906943f9SDavid du Colombier 	hp->seprintep = seprintep;
2548*906943f9SDavid du Colombier 	hp->portenable = portenable;
2549*906943f9SDavid du Colombier 	hp->portreset = portreset;
2550*906943f9SDavid du Colombier 	hp->portstatus = portstatus;
2551*906943f9SDavid du Colombier 	hp->debug = usbdebug;
2552*906943f9SDavid du Colombier 	hp->type = "ohci";
25536a5dc222SDavid du Colombier 	return 0;
25546a5dc222SDavid du Colombier }
25556a5dc222SDavid du Colombier 
25566a5dc222SDavid du Colombier void
25576a5dc222SDavid du Colombier usbohcilink(void)
25586a5dc222SDavid du Colombier {
2559*906943f9SDavid du Colombier 	addhcitype("ohci", reset);
25606a5dc222SDavid du Colombier }
2561