xref: /plan9/sys/src/9/pc/usbohci.c (revision a587111c8770e522e3667ff2b63cba8a77811dd9)
16a5dc222SDavid du Colombier /*
2906943f9SDavid du Colombier  * USB Open Host Controller Interface (Ohci) driver
3906943f9SDavid du Colombier  *
4906943f9SDavid du Colombier  * BUGS:
5906943f9SDavid du Colombier  * - Missing isochronous input streams.
6906943f9SDavid du Colombier  * - Too many delays and ilocks.
7906943f9SDavid du Colombier  * - bandwidth admission control must be done per-frame.
8906943f9SDavid du Colombier  * - Buffering could be handled like in uhci, to avoid
9906943f9SDavid du Colombier  * needed block allocation and avoid allocs for small Tds.
10906943f9SDavid du Colombier  * - must warn of power overruns.
116a5dc222SDavid du Colombier  */
12906943f9SDavid 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 
2184860c5dSDavid du Colombier #include	"../port/usb.h"
226a5dc222SDavid du Colombier 
2339dc1420SDavid du Colombier typedef struct Ctlio Ctlio;
246a5dc222SDavid du Colombier typedef struct Ctlr Ctlr;
2539dc1420SDavid du Colombier typedef struct Ed Ed;
2639dc1420SDavid du Colombier typedef struct Edpool Edpool;
27906943f9SDavid du Colombier typedef struct Epx Epx;
28906943f9SDavid du Colombier typedef struct Hcca Hcca;
2939dc1420SDavid du Colombier typedef struct Isoio Isoio;
30906943f9SDavid du Colombier typedef struct Ohci Ohci;
31906943f9SDavid du Colombier typedef struct Qio Qio;
3239dc1420SDavid du Colombier typedef struct Qtree Qtree;
3339dc1420SDavid du Colombier typedef struct Td Td;
34906943f9SDavid du Colombier typedef struct Tdpool Tdpool;
356a5dc222SDavid du Colombier 
36906943f9SDavid du Colombier enum
376a5dc222SDavid du Colombier {
38906943f9SDavid du Colombier 	Incr		= 64,		/* for Td and Ed pools */
396a5dc222SDavid du Colombier 
40906943f9SDavid du Colombier 	Align		= 0x20,		/* OHCI only requires 0x10 */
41906943f9SDavid du Colombier 					/* use always a power of 2 */
426a5dc222SDavid du Colombier 
43906943f9SDavid du Colombier 	Abortdelay	= 1,		/* delay after cancelling Tds (ms) */
44906943f9SDavid du Colombier 	Tdatomic		= 8,		/* max nb. of Tds per bulk I/O op. */
45906943f9SDavid du Colombier 	Enabledelay	= 100,		/* waiting for a port to enable */
466a5dc222SDavid du Colombier 
476a5dc222SDavid du Colombier 
48906943f9SDavid du Colombier 	/* Queue states (software) */
49906943f9SDavid du Colombier 	Qidle		= 0,
50906943f9SDavid du Colombier 	Qinstall,
51906943f9SDavid du Colombier 	Qrun,
52906943f9SDavid du Colombier 	Qdone,
53906943f9SDavid du Colombier 	Qclose,
54906943f9SDavid du Colombier 	Qfree,
556a5dc222SDavid du Colombier 
56906943f9SDavid du Colombier 	/* Ed control bits */
57906943f9SDavid du Colombier 	Edmpsmask	= 0x7ff,	/* max packet size */
58906943f9SDavid du Colombier 	Edmpsshift	= 16,
59906943f9SDavid du Colombier 	Edlow		= 1 << 13,	/* low speed */
60906943f9SDavid du Colombier 	Edskip		= 1 << 14,	/* skip this ed */
61906943f9SDavid du Colombier 	Ediso		= 1 << 15,	/* iso Tds used */
62906943f9SDavid du Colombier 	Edtddir		= 0,		/* get dir from td */
63906943f9SDavid du Colombier 	Edin		= 2 << 11,	/* direction in */
64906943f9SDavid du Colombier 	Edout		= 1 << 11,	/* direction out */
65906943f9SDavid du Colombier 	Eddirmask	= 3 << 11,	/* direction bits */
66906943f9SDavid du Colombier 	Edhalt		= 1,		/* halted (in head ptr) */
67906943f9SDavid du Colombier 	Edtoggle	= 2,		/* toggle (in head ptr) 1 == data1 */
686a5dc222SDavid du Colombier 
69906943f9SDavid du Colombier 	/* Td control bits */
70906943f9SDavid du Colombier 	Tdround		= 1<<18,	/* (rounding) short packets ok */
71906943f9SDavid du Colombier 	Tdtoksetup	= 0<<19,	/* setup packet */
72906943f9SDavid du Colombier 	Tdtokin		= 2<<19,	/* in packet */
73906943f9SDavid du Colombier 	Tdtokout	= 1<<19,	/* out packet */
74906943f9SDavid du Colombier 	Tdtokmask	= 3<<19,	/* in/out/setup bits */
75906943f9SDavid du Colombier 	Tdnoioc		= 7<<21,	/* intr. cnt. value for no interrupt */
76906943f9SDavid du Colombier 	Tdusetog	= 1<<25,	/* use toggle from Td (1) or Ed (0) */
77906943f9SDavid du Colombier 	Tddata1		= 1<<24,	/* data toggle (1 == data1) */
78906943f9SDavid du Colombier 	Tddata0		= 0<<24,
79906943f9SDavid du Colombier 	Tdfcmask	= 7,		/* frame count (iso) */
80906943f9SDavid du Colombier 	Tdfcshift	= 24,
81906943f9SDavid du Colombier 	Tdsfmask	= 0xFFFF,	/* starting frame (iso) */
82906943f9SDavid du Colombier 	Tderrmask	= 3,		/* error counter */
83906943f9SDavid du Colombier 	Tderrshift	= 26,
84906943f9SDavid du Colombier 	Tdccmask	= 0xf,		/* condition code (status) */
85906943f9SDavid du Colombier 	Tdccshift	= 28,
86906943f9SDavid du Colombier 	Tdiccmask	= 0xf,		/* condition code (iso, offsets) */
87906943f9SDavid du Colombier 	Tdiccshift	= 12,
886a5dc222SDavid du Colombier 
89906943f9SDavid du Colombier 	Ntdframes	= 0x10000,	/* # of different iso frame numbers */
906a5dc222SDavid du Colombier 
91906943f9SDavid du Colombier 	/* Td errors (condition code) */
92906943f9SDavid du Colombier 	Tdok		= 0,
93906943f9SDavid du Colombier 	Tdcrc		= 1,
94906943f9SDavid du Colombier 	Tdbitstuff	= 2,
95906943f9SDavid du Colombier 	Tdbadtog	= 3,
96906943f9SDavid du Colombier 	Tdstalled	= 4,
97906943f9SDavid du Colombier 	Tdtmout		= 5,
98906943f9SDavid du Colombier 	Tdpidchk	= 6,
99906943f9SDavid du Colombier 	Tdbadpid	= 7,
100906943f9SDavid du Colombier 	Tddataovr	= 8,
101906943f9SDavid du Colombier 	Tddataund	= 9,
102906943f9SDavid du Colombier 	Tdbufovr	= 0xC,
103906943f9SDavid du Colombier 	Tdbufund	= 0xD,
104906943f9SDavid du Colombier 	Tdnotacc	= 0xE,
1056a5dc222SDavid du Colombier 
106906943f9SDavid du Colombier 	/* control register */
107906943f9SDavid du Colombier 	Cple		= 0x04,		/* periodic list enable */
108906943f9SDavid du Colombier 	Cie		= 0x08,		/* iso. list enable */
109906943f9SDavid du Colombier 	Ccle		= 0x10,		/* ctl list enable */
110906943f9SDavid du Colombier 	Cble		= 0x20,		/* bulk list enable */
111906943f9SDavid du Colombier 	Cfsmask		= 3 << 6,	/* functional state... */
112906943f9SDavid du Colombier 	Cfsreset	= 0 << 6,
113906943f9SDavid du Colombier 	Cfsresume	= 1 << 6,
114906943f9SDavid du Colombier 	Cfsoper		= 2 << 6,
115906943f9SDavid du Colombier 	Cfssuspend	= 3 << 6,
1166a5dc222SDavid du Colombier 
117906943f9SDavid du Colombier 	/* command status */
118906943f9SDavid du Colombier 	Sblf =	1 << 2,			/* bulk list (load) flag */
119906943f9SDavid du Colombier 	Sclf =	1 << 1,			/* control list (load) flag */
120906943f9SDavid du Colombier 	Shcr =	1 << 0,			/* host controller reset */
1216a5dc222SDavid du Colombier 
122906943f9SDavid du Colombier 	/* intr enable */
1236a5dc222SDavid du Colombier 	Mie =	1 << 31,
1246a5dc222SDavid du Colombier 	Oc =	1 << 30,
1256a5dc222SDavid du Colombier 	Rhsc =	1 << 6,
1266a5dc222SDavid du Colombier 	Fno =	1 << 5,
1276a5dc222SDavid du Colombier 	Ue =	1 << 4,
1286a5dc222SDavid du Colombier 	Rd =	1 << 3,
1296a5dc222SDavid du Colombier 	Sf =	1 << 2,
1306a5dc222SDavid du Colombier 	Wdh =	1 << 1,
1316a5dc222SDavid du Colombier 	So =	1 << 0,
132906943f9SDavid du Colombier 
133906943f9SDavid du Colombier 	Fmaxpktmask = 0x7fff,
134906943f9SDavid du Colombier 	Fmaxpktshift = 16,
1356a5dc222SDavid du Colombier 	HcRhDescA_POTPGT_MASK =	0xff << 24,
1366a5dc222SDavid du Colombier 	HcRhDescA_POTPGT_SHIFT =	24,
137906943f9SDavid du Colombier 
138906943f9SDavid du Colombier 	/* Rh status */
1396a5dc222SDavid du Colombier 	Lps =	1 << 0,
1406a5dc222SDavid du Colombier 	Cgp =	1 << 0,
1416a5dc222SDavid du Colombier 	Oci =	1 << 1,
142906943f9SDavid du Colombier 	Psm =	1 << 8,
143906943f9SDavid du Colombier 	Nps =	1 << 9,
1446a5dc222SDavid du Colombier 	Drwe =	1 << 15,
1456a5dc222SDavid du Colombier 	Srwe =	1 << 15,
146906943f9SDavid du Colombier 	Lpsc =	1 << 16,
1476a5dc222SDavid du Colombier 	Ccic =	1 << 17,
1486a5dc222SDavid du Colombier 	Crwe =	1 << 31,
1496a5dc222SDavid du Colombier 
150906943f9SDavid du Colombier 	/* port status */
151906943f9SDavid du Colombier 	Ccs =	0x00001,	/* current connect status */
152906943f9SDavid du Colombier 	Pes =	0x00002,	/* port enable status */
153906943f9SDavid du Colombier 	Pss =	0x00004,	/* port suspend status */
154906943f9SDavid du Colombier 	Poci =	0x00008,	/* over current indicator */
155906943f9SDavid du Colombier 	Prs =	0x00010,	/* port reset status */
156906943f9SDavid du Colombier 	Pps =	0x00100,	/* port power status */
157906943f9SDavid du Colombier 	Lsda =	0x00200,	/* low speed device attached */
158906943f9SDavid du Colombier 	Csc =	0x10000,	/* connect status change */
159906943f9SDavid du Colombier 	Pesc =	0x20000,	/* enable status change */
160906943f9SDavid du Colombier 	Pssc =	0x40000,	/* suspend status change */
161906943f9SDavid du Colombier 	Ocic =	0x80000,	/* over current ind. change */
162906943f9SDavid du Colombier 	Prsc =	0x100000,	/* reset status change */
163906943f9SDavid du Colombier 
164906943f9SDavid du Colombier 	/* port status write bits */
165906943f9SDavid du Colombier 	Cpe =	0x001,		/* clear port enable */
166906943f9SDavid du Colombier 	Spe =	0x002,		/* set port enable */
167906943f9SDavid du Colombier 	Spr =	0x010,		/* set port reset */
168906943f9SDavid du Colombier 	Spp =	0x100,		/* set port power */
169906943f9SDavid du Colombier 	Cpp =	0x200,		/* clear port power */
170906943f9SDavid du Colombier 
1716a5dc222SDavid du Colombier };
1726a5dc222SDavid du Colombier 
173906943f9SDavid du Colombier /*
174906943f9SDavid du Colombier  * Endpoint descriptor. (first 4 words used by hardware)
175906943f9SDavid du Colombier  */
176906943f9SDavid du Colombier struct Ed {
177906943f9SDavid du Colombier 	ulong	ctrl;
178906943f9SDavid du Colombier 	ulong	tail;		/* transfer descriptor */
179906943f9SDavid du Colombier 	ulong	head;
180906943f9SDavid du Colombier 	ulong	nexted;
181906943f9SDavid du Colombier 
182906943f9SDavid du Colombier 	Ed*	next;		/* sw; in free list or next in list */
183906943f9SDavid du Colombier 	Td*	tds;		/* in use by current xfer; all for iso */
184906943f9SDavid du Colombier 	Ep*	ep;		/* debug/align */
185906943f9SDavid du Colombier 	Ed*	inext;		/* debug/align (dump interrupt eds). */
1866a5dc222SDavid du Colombier };
1876a5dc222SDavid du Colombier 
188906943f9SDavid du Colombier /*
189906943f9SDavid du Colombier  * Endpoint I/O state (software), per direction.
190906943f9SDavid du Colombier  */
191906943f9SDavid du Colombier struct Qio
192906943f9SDavid du Colombier {
193906943f9SDavid du Colombier 	QLock;			/* for the entire I/O process */
194906943f9SDavid du Colombier 	Rendez;			/* wait for completion */
195906943f9SDavid du Colombier 	Ed*	ed;		/* to place Tds on it */
196906943f9SDavid du Colombier 	int	sched;		/* queue number (intr/iso) */
197906943f9SDavid du Colombier 	int	toggle;		/* Tddata0/Tddata1 */
198906943f9SDavid du Colombier 	ulong	usbid;		/* device/endpoint address */
199906943f9SDavid du Colombier 	int	tok;		/* Tdsetup, Tdtokin, Tdtokout */
200906943f9SDavid du Colombier 	long	iotime;		/* last I/O time; to hold interrupt polls */
201906943f9SDavid du Colombier 	int	debug;		/* for the endpoint */
202906943f9SDavid du Colombier 	char*	err;		/* error status */
203906943f9SDavid du Colombier 	int	state;		/* Qidle -> Qinstall -> Qrun -> Qdone | Qclose */
204906943f9SDavid du Colombier 	long	bw;		/* load (intr/iso) */
2056a5dc222SDavid du Colombier };
2066a5dc222SDavid du Colombier 
207906943f9SDavid du Colombier struct Ctlio
208906943f9SDavid du Colombier {
209906943f9SDavid du Colombier 	Qio;			/* single Ed for all transfers */
210906943f9SDavid du Colombier 	uchar*	data;		/* read from last ctl req. */
211906943f9SDavid du Colombier 	int	ndata;		/* number of bytes read */
212906943f9SDavid du Colombier };
2136a5dc222SDavid du Colombier 
214906943f9SDavid du Colombier struct Isoio
215906943f9SDavid du Colombier {
216906943f9SDavid du Colombier 	Qio;
217906943f9SDavid du Colombier 	int	nframes;	/* number of frames for a full second */
218906943f9SDavid du Colombier 	Td*	atds;		/* Tds avail for further I/O */
219906943f9SDavid du Colombier 	int	navail;		/* number of avail Tds */
220906943f9SDavid du Colombier 	ulong	frno;		/* next frame number avail for I/O */
221906943f9SDavid du Colombier 	ulong	left;		/* remainder after rounding Hz to samples/ms */
222906943f9SDavid du Colombier 	int	nerrs;		/* consecutive errors on iso I/O */
223906943f9SDavid du Colombier };
224906943f9SDavid du Colombier 
225906943f9SDavid du Colombier /*
226906943f9SDavid du Colombier  * Transfer descriptor. Size must be multiple of 32
227906943f9SDavid du Colombier  * First block is used by hardware (aligned to 32).
228906943f9SDavid du Colombier  */
229906943f9SDavid du Colombier struct Td
230906943f9SDavid du Colombier {
231906943f9SDavid du Colombier 	ulong	ctrl;
232add6b5c5SDavid du Colombier 	ulong	cbp;		/* current buffer pointer */
233906943f9SDavid du Colombier 	ulong	nexttd;
234906943f9SDavid du Colombier 	ulong	be;
235906943f9SDavid du Colombier 	ushort	offsets[8];	/* used by Iso Tds only */
236906943f9SDavid du Colombier 
237906943f9SDavid du Colombier 	Td*	next;		/* in free or Ed tds list */
238906943f9SDavid du Colombier 	Td*	anext;		/* in avail td list (iso) */
239906943f9SDavid du Colombier 	Ep*	ep;		/* using this Td for I/O */
240906943f9SDavid du Colombier 	Qio*	io;		/* using this Td for I/O */
241906943f9SDavid du Colombier 	Block*	bp;		/* data for this Td */
242906943f9SDavid du Colombier 	ulong	nbytes;		/* bytes in this Td */
243906943f9SDavid du Colombier 	ulong	cbp0;		/* initial value for cbp */
244906943f9SDavid du Colombier 	ulong	last;		/* true for last Td in Qio */
245906943f9SDavid du Colombier };
246906943f9SDavid du Colombier 
247906943f9SDavid du Colombier /*
248906943f9SDavid du Colombier  * Host controller communication area (hardware)
249906943f9SDavid du Colombier  */
250906943f9SDavid du Colombier struct Hcca
251906943f9SDavid du Colombier {
252906943f9SDavid du Colombier 	ulong	intrtable[32];
253906943f9SDavid du Colombier 	ushort	framenumber;
254906943f9SDavid du Colombier 	ushort	pad1;
255906943f9SDavid du Colombier 	ulong	donehead;
256906943f9SDavid du Colombier 	uchar	reserved[116];
257906943f9SDavid du Colombier };
258906943f9SDavid du Colombier 
259906943f9SDavid du Colombier /*
260906943f9SDavid du Colombier  * I/O registers
261906943f9SDavid du Colombier  */
262906943f9SDavid du Colombier struct Ohci
263906943f9SDavid du Colombier {
264906943f9SDavid du Colombier 	/* control and status group */
265906943f9SDavid du Colombier 	ulong	revision;		/*00*/
266906943f9SDavid du Colombier 	ulong	control;		/*04*/
267906943f9SDavid du Colombier 	ulong	cmdsts;			/*08*/
268906943f9SDavid du Colombier 	ulong	intrsts;			/*0c*/
269906943f9SDavid du Colombier 	ulong	intrenable;		/*10*/
270906943f9SDavid du Colombier 	ulong	intrdisable;		/*14*/
271906943f9SDavid du Colombier 
272906943f9SDavid du Colombier 	/* memory pointer group */
273906943f9SDavid du Colombier 	ulong	hcca;			/*18*/
274906943f9SDavid du Colombier 	ulong	periodcurred;		/*1c*/
275906943f9SDavid du Colombier 	ulong	ctlheaded;		/*20*/
276906943f9SDavid du Colombier 	ulong	ctlcurred;		/*24*/
277906943f9SDavid du Colombier 	ulong	bulkheaded;		/*28*/
278906943f9SDavid du Colombier 	ulong	bulkcurred;		/*2c*/
279906943f9SDavid du Colombier 	ulong	donehead;		/*30*/
280906943f9SDavid du Colombier 
281906943f9SDavid du Colombier 	/* frame counter group */
282906943f9SDavid du Colombier 	ulong	fminterval;		/*34*/
283906943f9SDavid du Colombier 	ulong	fmremaining;		/*38*/
284906943f9SDavid du Colombier 	ulong	fmnumber;		/*3c*/
285906943f9SDavid du Colombier 	ulong	periodicstart;		/*40*/
286906943f9SDavid du Colombier 	ulong	lsthreshold;		/*44*/
287906943f9SDavid du Colombier 
288906943f9SDavid du Colombier 	/* root hub group */
289906943f9SDavid du Colombier 	ulong	rhdesca;		/*48*/
290906943f9SDavid du Colombier 	ulong	rhdescb;		/*4c*/
291906943f9SDavid du Colombier 	ulong	rhsts;			/*50*/
292906943f9SDavid du Colombier 	ulong	rhportsts[15];		/*54*/
293906943f9SDavid du Colombier 	ulong	pad25[20];		/*90*/
294906943f9SDavid du Colombier 
295906943f9SDavid du Colombier 	/* unknown */
296906943f9SDavid du Colombier 	ulong	hostueaddr;		/*e0*/
297906943f9SDavid du Colombier 	ulong	hostuests;		/*e4*/
298906943f9SDavid du Colombier 	ulong	hosttimeoutctrl;		/*e8*/
299906943f9SDavid du Colombier 	ulong	pad59;			/*ec*/
300906943f9SDavid du Colombier 	ulong	pad60;			/*f0*/
301906943f9SDavid du Colombier 	ulong	hostrevision;		/*f4*/
302906943f9SDavid du Colombier 	ulong	pad62[2];
303906943f9SDavid du Colombier 					/*100*/
304906943f9SDavid du Colombier };
305906943f9SDavid du Colombier 
306906943f9SDavid du Colombier /*
307906943f9SDavid du Colombier  * Endpoint tree (software)
308906943f9SDavid du Colombier  */
309906943f9SDavid du Colombier struct Qtree
310906943f9SDavid du Colombier {
311906943f9SDavid du Colombier 	int	nel;
312906943f9SDavid du Colombier 	int	depth;
313906943f9SDavid du Colombier 	ulong*	bw;
314906943f9SDavid du Colombier 	Ed**	root;
315906943f9SDavid du Colombier };
316906943f9SDavid du Colombier 
317906943f9SDavid du Colombier struct Tdpool
318906943f9SDavid du Colombier {
319906943f9SDavid du Colombier 	Lock;
320906943f9SDavid du Colombier 	Td*	free;
321906943f9SDavid du Colombier 	int	nalloc;
322906943f9SDavid du Colombier 	int	ninuse;
323906943f9SDavid du Colombier 	int	nfree;
324906943f9SDavid du Colombier };
325906943f9SDavid du Colombier 
326906943f9SDavid du Colombier struct Edpool
327906943f9SDavid du Colombier {
328906943f9SDavid du Colombier 	Lock;
329906943f9SDavid du Colombier 	Ed*	free;
330906943f9SDavid du Colombier 	int	nalloc;
331906943f9SDavid du Colombier 	int	ninuse;
332906943f9SDavid du Colombier 	int	nfree;
333906943f9SDavid du Colombier };
334906943f9SDavid du Colombier 
335906943f9SDavid du Colombier struct Ctlr
336906943f9SDavid du Colombier {
337906943f9SDavid du Colombier 	Lock;			/* for ilock; lists and basic ctlr I/O */
338906943f9SDavid du Colombier 	QLock	resetl;		/* lock controller during USB reset */
339906943f9SDavid du Colombier 	int	active;
340906943f9SDavid du Colombier 	Ctlr*	next;
341906943f9SDavid du Colombier 	int	nports;
342906943f9SDavid du Colombier 
343906943f9SDavid du Colombier 	Ohci*	ohci;		/* base I/O address */
344906943f9SDavid du Colombier 	Hcca*	hcca;		/* intr/done Td lists (used by hardware) */
345906943f9SDavid du Colombier 	int	overrun;	/* sched. overrun */
346906943f9SDavid du Colombier 	Ed*	intrhd;		/* list of intr. eds in tree */
347906943f9SDavid du Colombier 	Qtree*	tree;		/* tree for t Ep i/o */
348906943f9SDavid du Colombier 	int	ntree;		/* number of dummy Eds in tree */
349c8cbc0e9SDavid du Colombier 	Pcidev*	pcidev;
350906943f9SDavid du Colombier };
351906943f9SDavid du Colombier 
352906943f9SDavid du Colombier #define dqprint		if(debug || io && io->debug)print
353906943f9SDavid du Colombier #define ddqprint		if(debug>1 || (io && io->debug>1))print
354906943f9SDavid du Colombier #define diprint		if(debug || iso && iso->debug)print
355906943f9SDavid du Colombier #define ddiprint		if(debug>1 || (iso && iso->debug>1))print
356906943f9SDavid du Colombier #define TRUNC(x, sz)	((x) & ((sz)-1))
357906943f9SDavid du Colombier 
358906943f9SDavid du Colombier static int ohciinterrupts[Nttypes];
359906943f9SDavid du Colombier static char* iosname[] = { "idle", "install", "run", "done", "close", "FREE" };
360906943f9SDavid du Colombier 
361906943f9SDavid du Colombier static int debug;
362906943f9SDavid du Colombier static Edpool edpool;
363906943f9SDavid du Colombier static Tdpool tdpool;
364906943f9SDavid du Colombier static Ctlr* ctlrs[Nhcis];
365906943f9SDavid du Colombier 
3666a5dc222SDavid du Colombier static	QLock	usbhstate;	/* protects name space state */
3676a5dc222SDavid du Colombier 
368906943f9SDavid du Colombier static int	schedendpt(Ctlr *ub, Ep *ep);
369906943f9SDavid du Colombier static void	unschedendpt(Ctlr *ub, Ep *ep);
370906943f9SDavid du Colombier static long	qtd(Ctlr*, Ep*, int, Block*, uchar*, uchar*, int, ulong);
3716a5dc222SDavid du Colombier 
372906943f9SDavid du Colombier static char* errmsgs[] =
3736a5dc222SDavid du Colombier {
374906943f9SDavid du Colombier [Tdcrc]		"crc error",
375906943f9SDavid du Colombier [Tdbitstuff]	"bit stuffing error",
376906943f9SDavid du Colombier [Tdbadtog]	"bad toggle",
377906943f9SDavid du Colombier [Tdstalled]	Estalled,
378906943f9SDavid du Colombier [Tdtmout]	"timeout error",
379906943f9SDavid du Colombier [Tdpidchk]	"pid check error",
380906943f9SDavid du Colombier [Tdbadpid]	"bad pid",
381906943f9SDavid du Colombier [Tddataovr]	"data overrun",
382906943f9SDavid du Colombier [Tddataund]	"data underrun",
383906943f9SDavid du Colombier [Tdbufovr]	"buffer overrun",
384906943f9SDavid du Colombier [Tdbufund]	"buffer underrun",
385906943f9SDavid du Colombier [Tdnotacc]	"not accessed"
386906943f9SDavid du Colombier };
3876a5dc222SDavid du Colombier 
388906943f9SDavid du Colombier static void*
pa2ptr(ulong pa)389906943f9SDavid du Colombier pa2ptr(ulong pa)
3906a5dc222SDavid du Colombier {
391906943f9SDavid du Colombier 	if(pa == 0)
3926a5dc222SDavid du Colombier 		return nil;
3936a5dc222SDavid du Colombier 	else
394906943f9SDavid du Colombier 		return KADDR(pa);
3956a5dc222SDavid du Colombier }
3966a5dc222SDavid du Colombier 
397906943f9SDavid du Colombier static ulong
ptr2pa(void * p)398906943f9SDavid du Colombier ptr2pa(void *p)
3996a5dc222SDavid du Colombier {
400906943f9SDavid du Colombier 	if(p == nil)
401906943f9SDavid du Colombier 		return 0;
402906943f9SDavid du Colombier 	else
403906943f9SDavid du Colombier 		return PADDR(p);
4046a5dc222SDavid du Colombier }
4056a5dc222SDavid du Colombier 
4066a5dc222SDavid du Colombier static void
waitSOF(Ctlr * ub)4076a5dc222SDavid du Colombier waitSOF(Ctlr *ub)
4086a5dc222SDavid du Colombier {
409906943f9SDavid du Colombier 	int frame = ub->hcca->framenumber & 0x3f;
4106a5dc222SDavid du Colombier 
4116a5dc222SDavid du Colombier 	do {
4126a5dc222SDavid du Colombier 		delay(2);
413906943f9SDavid du Colombier 	} while(frame == (ub->hcca->framenumber & 0x3f));
414906943f9SDavid du Colombier }
415906943f9SDavid du Colombier 
416906943f9SDavid du Colombier static char*
errmsg(int err)417906943f9SDavid du Colombier errmsg(int err)
418906943f9SDavid du Colombier {
419906943f9SDavid du Colombier 
420906943f9SDavid du Colombier 	if(err < nelem(errmsgs))
421906943f9SDavid du Colombier 		return errmsgs[err];
422906943f9SDavid du Colombier 	return nil;
423906943f9SDavid du Colombier }
424906943f9SDavid du Colombier 
425906943f9SDavid du Colombier static Ed*
ctlhd(Ctlr * ctlr)426906943f9SDavid du Colombier ctlhd(Ctlr *ctlr)
427906943f9SDavid du Colombier {
428906943f9SDavid du Colombier 	return pa2ptr(ctlr->ohci->ctlheaded);
429906943f9SDavid du Colombier }
430906943f9SDavid du Colombier 
431906943f9SDavid du Colombier static Ed*
bulkhd(Ctlr * ctlr)432906943f9SDavid du Colombier bulkhd(Ctlr *ctlr)
433906943f9SDavid du Colombier {
434906943f9SDavid du Colombier 	return pa2ptr(ctlr->ohci->bulkheaded);
4356a5dc222SDavid du Colombier }
4366a5dc222SDavid du Colombier 
4376a5dc222SDavid du Colombier static void
edlinked(Ed * ed,Ed * next)438906943f9SDavid du Colombier edlinked(Ed *ed, Ed *next)
4396a5dc222SDavid du Colombier {
4406a5dc222SDavid du Colombier 	if(ed == nil)
441906943f9SDavid du Colombier 		print("edlinked: nil ed: pc %#p\n", getcallerpc(&ed));
442906943f9SDavid du Colombier 	ed->nexted = ptr2pa(next);
443906943f9SDavid du Colombier 	ed->next = next;
4446a5dc222SDavid du Colombier }
4456a5dc222SDavid du Colombier 
4466a5dc222SDavid du Colombier static void
setctlhd(Ctlr * ctlr,Ed * ed)447906943f9SDavid du Colombier setctlhd(Ctlr *ctlr, Ed *ed)
4486a5dc222SDavid du Colombier {
449906943f9SDavid du Colombier 	ctlr->ohci->ctlheaded = ptr2pa(ed);
450906943f9SDavid du Colombier 	if(ed != nil)
451906943f9SDavid du Colombier 		ctlr->ohci->cmdsts |= Sclf;	/* reload it on next pass */
4526a5dc222SDavid du Colombier }
4536a5dc222SDavid du Colombier 
4546a5dc222SDavid du Colombier static void
setbulkhd(Ctlr * ctlr,Ed * ed)455906943f9SDavid du Colombier setbulkhd(Ctlr *ctlr, Ed *ed)
4566a5dc222SDavid du Colombier {
457906943f9SDavid du Colombier 	ctlr->ohci->bulkheaded = ptr2pa(ed);
458906943f9SDavid du Colombier 	if(ed != nil)
459906943f9SDavid du Colombier 		ctlr->ohci->cmdsts |= Sblf;	/* reload it on next pass */
460906943f9SDavid du Colombier }
4616a5dc222SDavid du Colombier 
462906943f9SDavid du Colombier static void
unlinkctl(Ctlr * ctlr,Ed * ed)463906943f9SDavid du Colombier unlinkctl(Ctlr *ctlr, Ed *ed)
464906943f9SDavid du Colombier {
465906943f9SDavid du Colombier 	Ed *this, *prev, *next;
466906943f9SDavid du Colombier 
467906943f9SDavid du Colombier 	ctlr->ohci->control &= ~Ccle;
468906943f9SDavid du Colombier 	waitSOF(ctlr);
469906943f9SDavid du Colombier 	this = ctlhd(ctlr);
470906943f9SDavid du Colombier 	ctlr->ohci->ctlcurred = 0;
4716a5dc222SDavid du Colombier 	prev = nil;
4726a5dc222SDavid du Colombier 	while(this != nil && this != ed){
4736a5dc222SDavid du Colombier 		prev = this;
474906943f9SDavid du Colombier 		this = this->next;
4756a5dc222SDavid du Colombier 	}
4766a5dc222SDavid du Colombier 	if(this == nil){
477906943f9SDavid du Colombier 		print("unlinkctl: not found\n");
4786a5dc222SDavid du Colombier 		return;
4796a5dc222SDavid du Colombier 	}
480906943f9SDavid du Colombier 	next = this->next;
4816a5dc222SDavid du Colombier 	if(prev == nil)
482906943f9SDavid du Colombier 		setctlhd(ctlr, next);
4836a5dc222SDavid du Colombier 	else
484906943f9SDavid du Colombier 		edlinked(prev, next);
485906943f9SDavid du Colombier 	ctlr->ohci->control |= Ccle;
486906943f9SDavid du Colombier 	edlinked(ed, nil);		/* wipe out next field */
4876a5dc222SDavid du Colombier }
4886a5dc222SDavid du Colombier 
4896a5dc222SDavid du Colombier static void
unlinkbulk(Ctlr * ctlr,Ed * ed)490906943f9SDavid du Colombier unlinkbulk(Ctlr *ctlr, Ed *ed)
4916a5dc222SDavid du Colombier {
492906943f9SDavid du Colombier 	Ed *this, *prev, *next;
4936a5dc222SDavid du Colombier 
494906943f9SDavid du Colombier 	ctlr->ohci->control &= ~Cble;
495906943f9SDavid du Colombier 	waitSOF(ctlr);
496906943f9SDavid du Colombier 	this = bulkhd(ctlr);
497906943f9SDavid du Colombier 	ctlr->ohci->bulkcurred = 0;
4986a5dc222SDavid du Colombier 	prev = nil;
4996a5dc222SDavid du Colombier 	while(this != nil && this != ed){
5006a5dc222SDavid du Colombier 		prev = this;
501906943f9SDavid du Colombier 		this = this->next;
5026a5dc222SDavid du Colombier 	}
503906943f9SDavid du Colombier 	if(this == nil){
504906943f9SDavid du Colombier 		print("unlinkbulk: not found\n");
505906943f9SDavid du Colombier 		return;
506906943f9SDavid du Colombier 	}
507906943f9SDavid du Colombier 	next = this->next;
5086a5dc222SDavid du Colombier 	if(prev == nil)
509906943f9SDavid du Colombier 		setbulkhd(ctlr, next);
5106a5dc222SDavid du Colombier 	else
511906943f9SDavid du Colombier 		edlinked(prev, next);
512906943f9SDavid du Colombier 	ctlr->ohci->control |= Cble;
513906943f9SDavid du Colombier 	edlinked(ed, nil);		/* wipe out next field */
5146a5dc222SDavid du Colombier }
5156a5dc222SDavid du Colombier 
5166a5dc222SDavid du Colombier static void
edsetaddr(Ed * ed,ulong addr)517906943f9SDavid du Colombier edsetaddr(Ed *ed, ulong addr)
5186a5dc222SDavid du Colombier {
5196a5dc222SDavid du Colombier 	ulong ctrl;
5206a5dc222SDavid du Colombier 
521906943f9SDavid du Colombier 	ctrl = ed->ctrl & ~((Epmax<<7)|Devmax);
522906943f9SDavid du Colombier 	ctrl |= (addr & ((Epmax<<7)|Devmax));
523906943f9SDavid du Colombier 	ed->ctrl = ctrl;
5246a5dc222SDavid du Colombier }
5256a5dc222SDavid du Colombier 
5266a5dc222SDavid du Colombier static void
edsettog(Ed * ed,int c)527906943f9SDavid du Colombier edsettog(Ed *ed, int c)
5286a5dc222SDavid du Colombier {
529906943f9SDavid du Colombier 	if(c != 0)
530906943f9SDavid du Colombier 		ed->head |= Edtoggle;
531906943f9SDavid du Colombier 	else
532906943f9SDavid du Colombier 		ed->head &= ~Edtoggle;
5336a5dc222SDavid du Colombier }
534906943f9SDavid du Colombier 
535906943f9SDavid du Colombier static int
edtoggle(Ed * ed)536906943f9SDavid du Colombier edtoggle(Ed *ed)
537906943f9SDavid du Colombier {
538906943f9SDavid du Colombier 	return ed->head & Edtoggle;
539906943f9SDavid du Colombier }
540906943f9SDavid du Colombier 
541906943f9SDavid du Colombier static int
edhalted(Ed * ed)542906943f9SDavid du Colombier edhalted(Ed *ed)
543906943f9SDavid du Colombier {
544906943f9SDavid du Colombier 	return ed->head & Edhalt;
545906943f9SDavid du Colombier }
546906943f9SDavid du Colombier 
547906943f9SDavid du Colombier static int
edmaxpkt(Ed * ed)548906943f9SDavid du Colombier edmaxpkt(Ed *ed)
549906943f9SDavid du Colombier {
550906943f9SDavid du Colombier 	return (ed->ctrl >> Edmpsshift) & Edmpsmask;
5516a5dc222SDavid du Colombier }
5526a5dc222SDavid du Colombier 
5536a5dc222SDavid du Colombier static void
edsetmaxpkt(Ed * ed,int m)554906943f9SDavid du Colombier edsetmaxpkt(Ed *ed, int m)
5556a5dc222SDavid du Colombier {
556906943f9SDavid du Colombier 	ulong c;
5576a5dc222SDavid du Colombier 
558906943f9SDavid du Colombier 	c = ed->ctrl & ~(Edmpsmask << Edmpsshift);
559906943f9SDavid du Colombier 	ed->ctrl = c | ((m&Edmpsmask) << Edmpsshift);
5606a5dc222SDavid du Colombier }
561906943f9SDavid du Colombier 
562906943f9SDavid du Colombier static int
tderrs(Td * td)563906943f9SDavid du Colombier tderrs(Td *td)
564906943f9SDavid du Colombier {
565906943f9SDavid du Colombier 	return (td->ctrl >> Tdccshift) & Tdccmask;
566906943f9SDavid du Colombier }
567906943f9SDavid du Colombier 
568906943f9SDavid du Colombier static int
tdtok(Td * td)569906943f9SDavid du Colombier tdtok(Td *td)
570906943f9SDavid du Colombier {
571906943f9SDavid du Colombier 	return (td->ctrl & Tdtokmask);
572906943f9SDavid du Colombier }
573906943f9SDavid du Colombier 
574906943f9SDavid du Colombier static Td*
tdalloc(void)575906943f9SDavid du Colombier tdalloc(void)
576906943f9SDavid du Colombier {
577906943f9SDavid du Colombier 	Td *td;
578906943f9SDavid du Colombier 	Td *pool;
579906943f9SDavid du Colombier 	int i;
580906943f9SDavid du Colombier 
581906943f9SDavid du Colombier 	lock(&tdpool);
582906943f9SDavid du Colombier 	if(tdpool.free == nil){
583906943f9SDavid du Colombier 		ddprint("ohci: tdalloc %d Tds\n", Incr);
584906943f9SDavid du Colombier 		pool = xspanalloc(Incr*sizeof(Td), Align, 0);
585906943f9SDavid du Colombier 		if(pool == nil)
586906943f9SDavid du Colombier 			panic("tdalloc");
587906943f9SDavid du Colombier 		for(i=Incr; --i>=0;){
588906943f9SDavid du Colombier 			pool[i].next = tdpool.free;
589906943f9SDavid du Colombier 			tdpool.free = &pool[i];
590906943f9SDavid du Colombier 		}
591906943f9SDavid du Colombier 		tdpool.nalloc += Incr;
592906943f9SDavid du Colombier 		tdpool.nfree += Incr;
593906943f9SDavid du Colombier 	}
594906943f9SDavid du Colombier 	tdpool.ninuse++;
595906943f9SDavid du Colombier 	tdpool.nfree--;
596906943f9SDavid du Colombier 	td = tdpool.free;
597906943f9SDavid du Colombier 	tdpool.free = td->next;
598906943f9SDavid du Colombier 	memset(td, 0, sizeof(Td));
599906943f9SDavid du Colombier 	unlock(&tdpool);
600906943f9SDavid du Colombier 
601906943f9SDavid du Colombier 	assert(((uintptr)td & 0xF) == 0);
602906943f9SDavid du Colombier 	return td;
603906943f9SDavid du Colombier }
604906943f9SDavid du Colombier 
605906943f9SDavid du Colombier static void
tdfree(Td * td)606906943f9SDavid du Colombier tdfree(Td *td)
607906943f9SDavid du Colombier {
608906943f9SDavid du Colombier 	if(td == 0)
609906943f9SDavid du Colombier 		return;
610906943f9SDavid du Colombier 	freeb(td->bp);
611906943f9SDavid du Colombier 	td->bp = nil;
612906943f9SDavid du Colombier 	lock(&tdpool);
613906943f9SDavid du Colombier 	if(td->nexttd == 0x77777777)
614906943f9SDavid du Colombier 		panic("ohci: tdfree: double free");
615906943f9SDavid du Colombier 	memset(td, 7, sizeof(Td));	/* poison */
616906943f9SDavid du Colombier 	td->next = tdpool.free;
617906943f9SDavid du Colombier 	tdpool.free = td;
618906943f9SDavid du Colombier 	tdpool.ninuse--;
619906943f9SDavid du Colombier 	tdpool.nfree++;
620906943f9SDavid du Colombier 	unlock(&tdpool);
621906943f9SDavid du Colombier }
622906943f9SDavid du Colombier 
623906943f9SDavid du Colombier static Ed*
edalloc(void)624906943f9SDavid du Colombier edalloc(void)
625906943f9SDavid du Colombier {
62639dc1420SDavid du Colombier 	Ed *ed, *pool;
627906943f9SDavid du Colombier 	int i;
628906943f9SDavid du Colombier 
629906943f9SDavid du Colombier 	lock(&edpool);
630906943f9SDavid du Colombier 	if(edpool.free == nil){
631906943f9SDavid du Colombier 		ddprint("ohci: edalloc %d Eds\n", Incr);
632906943f9SDavid du Colombier 		pool = xspanalloc(Incr*sizeof(Ed), Align, 0);
633906943f9SDavid du Colombier 		if(pool == nil)
634906943f9SDavid du Colombier 			panic("edalloc");
635906943f9SDavid du Colombier 		for(i=Incr; --i>=0;){
636906943f9SDavid du Colombier 			pool[i].next = edpool.free;
637906943f9SDavid du Colombier 			edpool.free = &pool[i];
638906943f9SDavid du Colombier 		}
639906943f9SDavid du Colombier 		edpool.nalloc += Incr;
640906943f9SDavid du Colombier 		edpool.nfree += Incr;
641906943f9SDavid du Colombier 	}
642906943f9SDavid du Colombier 	edpool.ninuse++;
643906943f9SDavid du Colombier 	edpool.nfree--;
644906943f9SDavid du Colombier 	ed = edpool.free;
645906943f9SDavid du Colombier 	edpool.free = ed->next;
646906943f9SDavid du Colombier 	memset(ed, 0, sizeof(Ed));
647906943f9SDavid du Colombier 	unlock(&edpool);
648906943f9SDavid du Colombier 
649906943f9SDavid du Colombier 	return ed;
650906943f9SDavid du Colombier }
651906943f9SDavid du Colombier 
652906943f9SDavid du Colombier static void
edfree(Ed * ed)653906943f9SDavid du Colombier edfree(Ed *ed)
654906943f9SDavid du Colombier {
655906943f9SDavid du Colombier 	Td *td, *next;
656906943f9SDavid du Colombier 	int i;
657906943f9SDavid du Colombier 
658906943f9SDavid du Colombier 	if(ed == 0)
659906943f9SDavid du Colombier 		return;
660906943f9SDavid du Colombier 	i = 0;
661906943f9SDavid du Colombier 	for(td = ed->tds; td != nil; td = next){
662906943f9SDavid du Colombier 		next = td->next;
663906943f9SDavid du Colombier 		tdfree(td);
664906943f9SDavid du Colombier 		if(i++ > 2000){
665906943f9SDavid du Colombier 			print("ohci: bug: ed with more than 2000 tds\n");
666906943f9SDavid du Colombier 			break;
667906943f9SDavid du Colombier 		}
668906943f9SDavid du Colombier 	}
669906943f9SDavid du Colombier 	lock(&edpool);
670906943f9SDavid du Colombier 	if(ed->nexted == 0x99999999)
671906943f9SDavid du Colombier 		panic("ohci: edfree: double free");
672906943f9SDavid du Colombier 	memset(ed, 9, sizeof(Ed));	/* poison */
673906943f9SDavid du Colombier 	ed->next = edpool.free;
674906943f9SDavid du Colombier 	edpool.free = ed;
675906943f9SDavid du Colombier 	edpool.ninuse--;
676906943f9SDavid du Colombier 	edpool.nfree++;
677906943f9SDavid du Colombier 	unlock(&edpool);
678906943f9SDavid du Colombier 	ddprint("edfree: ed %#p\n", ed);
6796a5dc222SDavid du Colombier }
6806a5dc222SDavid du Colombier 
6816a5dc222SDavid du Colombier /*
6826a5dc222SDavid du Colombier  * return smallest power of 2 >= n
6836a5dc222SDavid du Colombier  */
6846a5dc222SDavid du Colombier static int
flog2(int n)6856a5dc222SDavid du Colombier flog2(int n)
6866a5dc222SDavid du Colombier {
6876a5dc222SDavid du Colombier 	int i;
6886a5dc222SDavid du Colombier 
6896a5dc222SDavid du Colombier 	for(i = 0; (1 << i) < n; i++)
6906a5dc222SDavid du Colombier 		;
6916a5dc222SDavid du Colombier 	return i;
6926a5dc222SDavid du Colombier }
6936a5dc222SDavid du Colombier 
6946a5dc222SDavid du Colombier /*
6956a5dc222SDavid du Colombier  * return smallest power of 2 <= n
6966a5dc222SDavid du Colombier  */
6976a5dc222SDavid du Colombier static int
flog2lower(int n)6986a5dc222SDavid du Colombier flog2lower(int n)
6996a5dc222SDavid du Colombier {
7006a5dc222SDavid du Colombier 	int i;
7016a5dc222SDavid du Colombier 
7026a5dc222SDavid du Colombier 	for(i = 0; (1 << (i + 1)) <= n; i++)
7036a5dc222SDavid du Colombier 		;
7046a5dc222SDavid du Colombier 	return i;
7056a5dc222SDavid du Colombier }
7066a5dc222SDavid du Colombier 
7076a5dc222SDavid du Colombier static int
pickschedq(Qtree * qt,int pollival,ulong bw,ulong limit)708906943f9SDavid du Colombier pickschedq(Qtree *qt, int pollival, ulong bw, ulong limit)
7096a5dc222SDavid du Colombier {
7106a5dc222SDavid du Colombier 	int i, j, d, upperb, q;
7116a5dc222SDavid du Colombier 	ulong best, worst, total;
7126a5dc222SDavid du Colombier 
713906943f9SDavid du Colombier 	d = flog2lower(pollival);
7146a5dc222SDavid du Colombier 	if(d > qt->depth)
7156a5dc222SDavid du Colombier 		d = qt->depth;
7166a5dc222SDavid du Colombier 	q = -1;
7176a5dc222SDavid du Colombier 	worst = 0;
7186a5dc222SDavid du Colombier 	best = ~0;
7196a5dc222SDavid du Colombier 	upperb = (1 << (d+1)) - 1;
7206a5dc222SDavid du Colombier 	for(i = (1 << d) - 1; i < upperb; i++){
7216a5dc222SDavid du Colombier 		total = qt->bw[0];
7226a5dc222SDavid du Colombier 		for(j = i; j > 0; j = (j - 1) / 2)
7236a5dc222SDavid du Colombier 			total += qt->bw[j];
7246a5dc222SDavid du Colombier 		if(total < best){
7256a5dc222SDavid du Colombier 			best = total;
7266a5dc222SDavid du Colombier 			q = i;
7276a5dc222SDavid du Colombier 		}
7286a5dc222SDavid du Colombier 		if(total > worst)
7296a5dc222SDavid du Colombier 			worst = total;
7306a5dc222SDavid du Colombier 	}
7316a5dc222SDavid du Colombier 	if(worst + bw >= limit)
7326a5dc222SDavid du Colombier 		return -1;
7336a5dc222SDavid du Colombier 	return q;
7346a5dc222SDavid du Colombier }
7356a5dc222SDavid du Colombier 
7366a5dc222SDavid du Colombier static int
schedq(Ctlr * ctlr,Qio * io,int pollival)737906943f9SDavid du Colombier schedq(Ctlr *ctlr, Qio *io, int pollival)
7386a5dc222SDavid du Colombier {
7396a5dc222SDavid du Colombier 	int q;
740906943f9SDavid du Colombier 	Ed *ted;
7416a5dc222SDavid du Colombier 
742906943f9SDavid du Colombier 	q = pickschedq(ctlr->tree, pollival, io->bw, ~0);
743906943f9SDavid du Colombier 	ddqprint("ohci: sched %#p q %d, ival %d, bw %ld\n", io, q, pollival, io->bw);
7446a5dc222SDavid du Colombier 	if(q < 0){
745906943f9SDavid du Colombier 		print("ohci: no room for ed\n");
7466a5dc222SDavid du Colombier 		return -1;
7476a5dc222SDavid du Colombier 	}
748906943f9SDavid du Colombier 	ctlr->tree->bw[q] += io->bw;
749906943f9SDavid du Colombier 	ted = ctlr->tree->root[q];
750906943f9SDavid du Colombier 	io->sched = q;
751906943f9SDavid du Colombier 	edlinked(io->ed, ted->next);
752906943f9SDavid du Colombier 	edlinked(ted, io->ed);
753906943f9SDavid du Colombier 	io->ed->inext = ctlr->intrhd;
754906943f9SDavid du Colombier 	ctlr->intrhd = io->ed;
7556a5dc222SDavid du Colombier 	return 0;
7566a5dc222SDavid du Colombier }
7576a5dc222SDavid du Colombier 
7586a5dc222SDavid du Colombier static void
unschedq(Ctlr * ctlr,Qio * qio)759906943f9SDavid du Colombier unschedq(Ctlr *ctlr, Qio *qio)
7606a5dc222SDavid du Colombier {
7616a5dc222SDavid du Colombier 	int q;
762906943f9SDavid du Colombier 	Ed *prev, *this, *next;
763906943f9SDavid du Colombier 	Ed **l;
7646a5dc222SDavid du Colombier 
765906943f9SDavid du Colombier 	q = qio->sched;
766906943f9SDavid du Colombier 	if(q < 0)
7676a5dc222SDavid du Colombier 		return;
768906943f9SDavid du Colombier 	ctlr->tree->bw[q] -= qio->bw;
7696a5dc222SDavid du Colombier 
770906943f9SDavid du Colombier 	prev = ctlr->tree->root[q];
771906943f9SDavid du Colombier 	this = prev->next;
772906943f9SDavid du Colombier 	while(this != nil && this != qio->ed){
7736a5dc222SDavid du Colombier 		prev = this;
774906943f9SDavid du Colombier 		this = this->next;
7756a5dc222SDavid du Colombier 	}
7766a5dc222SDavid du Colombier 	if(this == nil)
777906943f9SDavid du Colombier 		print("ohci: unschedq %d: not found\n", q);
7786a5dc222SDavid du Colombier 	else{
779906943f9SDavid du Colombier 		next = this->next;
780906943f9SDavid du Colombier 		edlinked(prev, next);
7816a5dc222SDavid du Colombier 	}
782906943f9SDavid du Colombier 	waitSOF(ctlr);
783906943f9SDavid du Colombier 	for(l = &ctlr->intrhd; *l != nil; l = &(*l)->inext)
784906943f9SDavid du Colombier 		if(*l == qio->ed){
785906943f9SDavid du Colombier 			*l = (*l)->inext;
786906943f9SDavid du Colombier 			return;
787906943f9SDavid du Colombier 		}
788906943f9SDavid du Colombier 	print("ohci: unschedq: ed %#p not found\n", qio->ed);
7896a5dc222SDavid du Colombier }
7906a5dc222SDavid du Colombier 
791906943f9SDavid du Colombier static char*
seprinttdtok(char * s,char * e,int tok)792906943f9SDavid du Colombier seprinttdtok(char *s, char *e, int tok)
7936a5dc222SDavid du Colombier {
794906943f9SDavid du Colombier 	switch(tok){
795906943f9SDavid du Colombier 	case Tdtoksetup:
796906943f9SDavid du Colombier 		s = seprint(s, e, " setup");
797906943f9SDavid du Colombier 		break;
798906943f9SDavid du Colombier 	case Tdtokin:
799906943f9SDavid du Colombier 		s = seprint(s, e, " in");
800906943f9SDavid du Colombier 		break;
801906943f9SDavid du Colombier 	case Tdtokout:
802906943f9SDavid du Colombier 		s = seprint(s, e, " out");
803906943f9SDavid du Colombier 		break;
8046a5dc222SDavid du Colombier 	}
805906943f9SDavid du Colombier 	return s;
806906943f9SDavid du Colombier }
807906943f9SDavid du Colombier 
808906943f9SDavid du Colombier 
809906943f9SDavid du Colombier static char*
seprinttd(char * s,char * e,Td * td,int iso)810906943f9SDavid du Colombier seprinttd(char *s, char *e, Td *td, int iso)
811906943f9SDavid du Colombier {
812906943f9SDavid du Colombier 	int i;
813906943f9SDavid du Colombier 	Block *bp;
814906943f9SDavid du Colombier 
815906943f9SDavid du Colombier 	if(td == nil)
816906943f9SDavid du Colombier 		return seprint(s, e, "<nil td>\n");
817906943f9SDavid du Colombier 	s = seprint(s, e, "%#p ep %#p ctrl %#p", td, td->ep, td->ctrl);
818906943f9SDavid du Colombier 	s = seprint(s, e, " cc=%#ulx", (td->ctrl >> Tdccshift) & Tdccmask);
819906943f9SDavid du Colombier 	if(iso == 0){
820906943f9SDavid du Colombier 		if((td->ctrl & Tdround) != 0)
821906943f9SDavid du Colombier 			s = seprint(s, e, " rnd");
822906943f9SDavid du Colombier 		s = seprinttdtok(s, e, td->ctrl & Tdtokmask);
823906943f9SDavid du Colombier 		if((td->ctrl & Tdusetog) != 0)
824906943f9SDavid du Colombier 			s = seprint(s, e, " d%d", (td->ctrl & Tddata1) ? 1 : 0);
825906943f9SDavid du Colombier 		else
826906943f9SDavid du Colombier 			s = seprint(s, e, " d-");
827906943f9SDavid du Colombier 		s = seprint(s, e, " ec=%uld", (td->ctrl >> Tderrshift) & Tderrmask);
828906943f9SDavid du Colombier 	}else{
829906943f9SDavid du Colombier 		s = seprint(s, e, " fc=%uld", (td->ctrl >> Tdfcshift) & Tdfcmask);
830906943f9SDavid du Colombier 		s = seprint(s, e, " sf=%uld", td->ctrl & Tdsfmask);
831906943f9SDavid du Colombier 	}
832906943f9SDavid du Colombier 	s = seprint(s, e, " cbp0 %#p cbp %#p next %#p be %#p %s",
833906943f9SDavid du Colombier 		td->cbp0, td->cbp, td->nexttd, td->be, td->last ? "last" : "");
834906943f9SDavid du Colombier 	s = seprint(s, e, "\n\t\t%ld bytes", td->nbytes);
835906943f9SDavid du Colombier 	if((bp = td->bp) != nil){
836906943f9SDavid du Colombier 		s = seprint(s, e, " rp %#p wp %#p ", bp->rp, bp->wp);
837906943f9SDavid du Colombier 		if(BLEN(bp) > 0)
838906943f9SDavid du Colombier 			s = seprintdata(s, e, bp->rp, bp->wp - bp->rp);
839906943f9SDavid du Colombier 	}
840906943f9SDavid du Colombier 	if(iso == 0)
841906943f9SDavid du Colombier 		return seprint(s, e, "\n");
842906943f9SDavid du Colombier 	s = seprint(s, e, "\n\t\t");
843906943f9SDavid du Colombier 	/* we use only offsets[0] */
844906943f9SDavid du Colombier 	i = 0;
845906943f9SDavid du Colombier 	s = seprint(s, e, "[%d] %#ux cc=%#ux sz=%ud\n", i, td->offsets[i],
846906943f9SDavid du Colombier 		(td->offsets[i] >> Tdiccshift) & Tdiccmask,
847906943f9SDavid du Colombier 		td->offsets[i] & 0x7FF);
848906943f9SDavid du Colombier 	return s;
849906943f9SDavid du Colombier }
850906943f9SDavid du Colombier 
851906943f9SDavid du Colombier static void
dumptd(Td * td,char * p,int iso)852906943f9SDavid du Colombier dumptd(Td *td, char *p, int iso)
853906943f9SDavid du Colombier {
854906943f9SDavid du Colombier 	static char buf[512];	/* Too much */
855906943f9SDavid du Colombier 	char *s;
856906943f9SDavid du Colombier 
857906943f9SDavid du Colombier 	s = seprint(buf, buf+sizeof(buf), "%s: ", p);
858906943f9SDavid du Colombier 	s = seprinttd(s, buf+sizeof(buf), td, iso);
859906943f9SDavid du Colombier 	if(s > buf && s[-1] != '\n')
860906943f9SDavid du Colombier 		s[-1] = '\n';
861906943f9SDavid du Colombier 	print("\t%s", buf);
862906943f9SDavid du Colombier }
863906943f9SDavid du Colombier 
864906943f9SDavid du Colombier static void
dumptds(Td * td,char * p,int iso)865906943f9SDavid du Colombier dumptds(Td *td, char *p, int iso)
866906943f9SDavid du Colombier {
867906943f9SDavid du Colombier 	int i;
868906943f9SDavid du Colombier 
869906943f9SDavid du Colombier 	for(i = 0; td != nil; td = td->next){
870906943f9SDavid du Colombier 		dumptd(td, p, iso);
871906943f9SDavid du Colombier 		if(td->last)
872906943f9SDavid du Colombier 			break;
873906943f9SDavid du Colombier 		if(tdtok(td) == Tdtokin && ++i > 2){
874906943f9SDavid du Colombier 			print("\t\t...\n");
875906943f9SDavid du Colombier 			break;
876906943f9SDavid du Colombier 		}
877906943f9SDavid du Colombier 	}
878906943f9SDavid du Colombier }
879906943f9SDavid du Colombier 
880906943f9SDavid du Colombier static void
dumped(Ed * ed)881906943f9SDavid du Colombier dumped(Ed *ed)
882906943f9SDavid du Colombier {
88339dc1420SDavid du Colombier 	char *buf, *s, *e;
884906943f9SDavid du Colombier 
885906943f9SDavid du Colombier 	if(ed == nil){
886906943f9SDavid du Colombier 		print("<null ed>\n");
887906943f9SDavid du Colombier 		return;
888906943f9SDavid du Colombier 	}
889906943f9SDavid du Colombier 	buf = malloc(512);
890906943f9SDavid du Colombier 	/* no waserror; may want to use from interrupt context */
891906943f9SDavid du Colombier 	if(buf == nil)
892906943f9SDavid du Colombier 		return;
893906943f9SDavid du Colombier 	e = buf+512;
894906943f9SDavid du Colombier 	s = seprint(buf, e, "\ted %#p: ctrl %#p", ed, ed->ctrl);
895906943f9SDavid du Colombier 	if((ed->ctrl & Edskip) != 0)
896906943f9SDavid du Colombier 		s = seprint(s, e, " skip");
897906943f9SDavid du Colombier 	if((ed->ctrl & Ediso) != 0)
898906943f9SDavid du Colombier 		s = seprint(s, e, " iso");
899906943f9SDavid du Colombier 	if((ed->ctrl & Edlow) != 0)
900906943f9SDavid du Colombier 		s = seprint(s, e, " low");
901906943f9SDavid du Colombier 	s = seprint(s, e, " d%d", (ed->head & Edtoggle) ? 1 : 0);
902906943f9SDavid du Colombier 	if((ed->ctrl & Eddirmask) == Edin)
903906943f9SDavid du Colombier 		s = seprint(s, e, " in");
904906943f9SDavid du Colombier 	if((ed->ctrl & Eddirmask) == Edout)
905906943f9SDavid du Colombier 		s = seprint(s, e, " out");
906906943f9SDavid du Colombier 	if(edhalted(ed))
907906943f9SDavid du Colombier 		s = seprint(s, e, " hlt");
908906943f9SDavid du Colombier 	s = seprint(s, e, " ep%uld.%uld", (ed->ctrl>>7)&Epmax, ed->ctrl&0x7f);
909906943f9SDavid du Colombier 	s = seprint(s, e, " maxpkt %uld", (ed->ctrl>>Edmpsshift)&Edmpsmask);
910906943f9SDavid du Colombier 	seprint(s, e, " tail %#p head %#p next %#p\n",ed->tail,ed->head,ed->nexted);
911906943f9SDavid du Colombier 	print("%s", buf);
912906943f9SDavid du Colombier 	free(buf);
913906943f9SDavid du Colombier 	if(ed->tds != nil && (ed->ctrl & Ediso) == 0)
914906943f9SDavid du Colombier 		dumptds(ed->tds, "td", 0);
915906943f9SDavid du Colombier }
916906943f9SDavid du Colombier 
917906943f9SDavid du Colombier static char*
seprintio(char * s,char * e,Qio * io,char * pref)918906943f9SDavid du Colombier seprintio(char *s, char *e, Qio *io, char *pref)
919906943f9SDavid du Colombier {
920906943f9SDavid du Colombier 	s = seprint(s, e, "%s qio %#p ed %#p", pref, io, io->ed);
921906943f9SDavid du Colombier 	s = seprint(s, e, " tog %d iot %ld err %s id %#ulx",
922906943f9SDavid du Colombier 		io->toggle, io->iotime, io->err, io->usbid);
923906943f9SDavid du Colombier 	s = seprinttdtok(s, e, io->tok);
924906943f9SDavid du Colombier 	s = seprint(s, e, " %s\n", iosname[io->state]);
925906943f9SDavid du Colombier 	return s;
926906943f9SDavid du Colombier }
927906943f9SDavid du Colombier 
928906943f9SDavid du Colombier static char*
seprintep(char * s,char * e,Ep * ep)929906943f9SDavid du Colombier seprintep(char* s, char* e, Ep *ep)
930906943f9SDavid du Colombier {
931906943f9SDavid du Colombier 	Isoio *iso;
932906943f9SDavid du Colombier 	Qio *io;
933906943f9SDavid du Colombier 	Ctlio *cio;
934906943f9SDavid du Colombier 
935906943f9SDavid du Colombier 	if(ep == nil)
936906943f9SDavid du Colombier 		return seprint(s, e, "<nil ep>\n");
937906943f9SDavid du Colombier 	if(ep->aux == nil)
938906943f9SDavid du Colombier 		return seprint(s, e, "no mdep\n");
939906943f9SDavid du Colombier 	switch(ep->ttype){
940906943f9SDavid du Colombier 	case Tctl:
941906943f9SDavid du Colombier 		cio = ep->aux;
942906943f9SDavid du Colombier 		s = seprintio(s, e, cio, "c");
943906943f9SDavid du Colombier 		s = seprint(s, e, "\trepl %d ndata %d\n", ep->rhrepl, cio->ndata);
944906943f9SDavid du Colombier 		break;
945906943f9SDavid du Colombier 	case Tbulk:
946906943f9SDavid du Colombier 	case Tintr:
947906943f9SDavid du Colombier 		io = ep->aux;
948906943f9SDavid du Colombier 		if(ep->mode != OWRITE)
949906943f9SDavid du Colombier 			s = seprintio(s, e, &io[OREAD], "r");
950906943f9SDavid du Colombier 		if(ep->mode != OREAD)
951906943f9SDavid du Colombier 			s = seprintio(s, e, &io[OWRITE], "w");
952906943f9SDavid du Colombier 		break;
953906943f9SDavid du Colombier 	case Tiso:
954906943f9SDavid du Colombier 		iso = ep->aux;
955906943f9SDavid du Colombier 		s = seprintio(s, e, iso, "w");
956906943f9SDavid du Colombier 		s = seprint(s, e, "\tntds %d avail %d frno %uld left %uld next avail %#p\n",
957906943f9SDavid du Colombier 			iso->nframes, iso->navail, iso->frno, iso->left, iso->atds);
958906943f9SDavid du Colombier 		break;
959906943f9SDavid du Colombier 	}
960906943f9SDavid du Colombier 	return s;
961906943f9SDavid du Colombier }
962906943f9SDavid du Colombier 
963906943f9SDavid du Colombier static char*
seprintctl(char * s,char * se,ulong ctl)964906943f9SDavid du Colombier seprintctl(char *s, char *se, ulong ctl)
965906943f9SDavid du Colombier {
966906943f9SDavid du Colombier 	s = seprint(s, se, "en=");
967906943f9SDavid du Colombier 	if((ctl&Cple) != 0)
968906943f9SDavid du Colombier 		s = seprint(s, se, "p");
969906943f9SDavid du Colombier 	if((ctl&Cie) != 0)
970906943f9SDavid du Colombier 		s = seprint(s, se, "i");
971906943f9SDavid du Colombier 	if((ctl&Ccle) != 0)
972906943f9SDavid du Colombier 		s = seprint(s, se, "c");
973906943f9SDavid du Colombier 	if((ctl&Cble) != 0)
974906943f9SDavid du Colombier 		s = seprint(s, se, "b");
975906943f9SDavid du Colombier 	switch(ctl & Cfsmask){
976906943f9SDavid du Colombier 	case Cfsreset:
977906943f9SDavid du Colombier 		return seprint(s, se, " reset");
978906943f9SDavid du Colombier 	case Cfsresume:
979906943f9SDavid du Colombier 		return seprint(s, se, " resume");
980906943f9SDavid du Colombier 	case Cfsoper:
981906943f9SDavid du Colombier 		return seprint(s, se, " run");
982906943f9SDavid du Colombier 	case Cfssuspend:
983906943f9SDavid du Colombier 		return seprint(s, se, " suspend");
984906943f9SDavid du Colombier 	default:
985906943f9SDavid du Colombier 		return seprint(s, se, " ???");
986906943f9SDavid du Colombier 	}
987906943f9SDavid du Colombier }
988906943f9SDavid du Colombier 
989906943f9SDavid du Colombier static void
dump(Hci * hp)990906943f9SDavid du Colombier dump(Hci *hp)
991906943f9SDavid du Colombier {
992906943f9SDavid du Colombier 	Ctlr *ctlr;
993906943f9SDavid du Colombier 	Ed *ed;
994906943f9SDavid du Colombier 	char cs[20];
995906943f9SDavid du Colombier 
996906943f9SDavid du Colombier 	ctlr = hp->aux;
997906943f9SDavid du Colombier 	ilock(ctlr);
998906943f9SDavid du Colombier 	seprintctl(cs, cs+sizeof(cs), ctlr->ohci->control);
999906943f9SDavid du Colombier 	print("ohci ctlr %#p: frno %#ux ctl %#lux %s sts %#lux intr %#lux\n",
1000906943f9SDavid du Colombier 		ctlr, ctlr->hcca->framenumber, ctlr->ohci->control, cs,
1001906943f9SDavid du Colombier 		ctlr->ohci->cmdsts, ctlr->ohci->intrsts);
1002906943f9SDavid du Colombier 	print("ctlhd %#ulx cur %#ulx bulkhd %#ulx cur %#ulx done %#ulx\n",
1003906943f9SDavid du Colombier 		ctlr->ohci->ctlheaded, ctlr->ohci->ctlcurred,
1004906943f9SDavid du Colombier 		ctlr->ohci->bulkheaded, ctlr->ohci->bulkcurred,
1005906943f9SDavid du Colombier 		ctlr->ohci->donehead);
1006906943f9SDavid du Colombier 	if(ctlhd(ctlr) != nil)
1007906943f9SDavid du Colombier 		print("[ctl]\n");
1008906943f9SDavid du Colombier 	for(ed = ctlhd(ctlr); ed != nil; ed = ed->next)
1009906943f9SDavid du Colombier 		dumped(ed);
1010906943f9SDavid du Colombier 	if(bulkhd(ctlr) != nil)
1011906943f9SDavid du Colombier 		print("[bulk]\n");
1012906943f9SDavid du Colombier 	for(ed = bulkhd(ctlr); ed != nil; ed = ed->next)
1013906943f9SDavid du Colombier 		dumped(ed);
1014906943f9SDavid du Colombier 	if(ctlr->intrhd != nil)
1015906943f9SDavid du Colombier 		print("[intr]\n");
1016906943f9SDavid du Colombier 	for(ed = ctlr->intrhd; ed != nil; ed = ed->inext)
1017906943f9SDavid du Colombier 		dumped(ed);
1018906943f9SDavid du Colombier 	if(ctlr->tree->root[0]->next != nil)
1019906943f9SDavid du Colombier 		print("[iso]");
1020906943f9SDavid du Colombier 	for(ed = ctlr->tree->root[0]->next; ed != nil; ed = ed->next)
1021906943f9SDavid du Colombier 		dumped(ed);
1022906943f9SDavid du Colombier 	print("%d eds in tree\n", ctlr->ntree);
1023906943f9SDavid du Colombier 	iunlock(ctlr);
1024906943f9SDavid du Colombier 	lock(&tdpool);
1025906943f9SDavid du Colombier 	print("%d tds allocated = %d in use + %d free\n",
1026906943f9SDavid du Colombier 		tdpool.nalloc, tdpool.ninuse, tdpool.nfree);
1027906943f9SDavid du Colombier 	unlock(&tdpool);
1028906943f9SDavid du Colombier 	lock(&edpool);
1029906943f9SDavid du Colombier 	print("%d eds allocated = %d in use + %d free\n",
1030906943f9SDavid du Colombier 		edpool.nalloc, edpool.ninuse, edpool.nfree);
1031906943f9SDavid du Colombier 	unlock(&edpool);
1032906943f9SDavid du Colombier }
1033906943f9SDavid du Colombier 
1034906943f9SDavid du Colombier /*
1035906943f9SDavid du Colombier  * Compute size for the next iso Td and setup its
1036906943f9SDavid du Colombier  * descriptor for I/O according to the buffer size.
1037906943f9SDavid du Colombier  */
1038906943f9SDavid du Colombier static void
isodtdinit(Ep * ep,Isoio * iso,Td * td)1039906943f9SDavid du Colombier isodtdinit(Ep *ep, Isoio *iso, Td *td)
1040906943f9SDavid du Colombier {
1041906943f9SDavid du Colombier 	Block *bp;
1042906943f9SDavid du Colombier 	long size;
1043906943f9SDavid du Colombier 	int i;
1044906943f9SDavid du Colombier 
1045906943f9SDavid du Colombier 	bp = td->bp;
1046906943f9SDavid du Colombier 	assert(bp != nil && BLEN(bp) == 0);
1047906943f9SDavid du Colombier 	size = (ep->hz+iso->left) * ep->pollival / 1000;
1048906943f9SDavid du Colombier 	iso->left = (ep->hz+iso->left) * ep->pollival % 1000;
1049906943f9SDavid du Colombier 	size *= ep->samplesz;
1050906943f9SDavid du Colombier 	if(size > ep->maxpkt){
1051906943f9SDavid du Colombier 		print("ohci: ep%d.%d: size > maxpkt\n",
1052906943f9SDavid du Colombier 			ep->dev->nb, ep->nb);
1053906943f9SDavid du Colombier 		print("size = %uld max = %ld\n", size, ep->maxpkt);
1054906943f9SDavid du Colombier 		size = ep->maxpkt;
1055906943f9SDavid du Colombier 	}
1056906943f9SDavid du Colombier 	td->nbytes = size;
1057906943f9SDavid du Colombier 	memset(bp->wp, 0, size);	/* in case we don't fill it on time */
1058906943f9SDavid du Colombier 	td->cbp0 = td->cbp = ptr2pa(bp->rp) & ~0xFFF;
1059906943f9SDavid du Colombier 	td->ctrl = TRUNC(iso->frno, Ntdframes);
1060906943f9SDavid du Colombier 	td->offsets[0] = (ptr2pa(bp->rp) & 0xFFF);
1061906943f9SDavid du Colombier 	td->offsets[0] |= (Tdnotacc << Tdiccshift);
1062906943f9SDavid du Colombier 	/* in case the controller checks out the offests... */
1063906943f9SDavid du Colombier 	for(i = 1; i < nelem(td->offsets); i++)
1064906943f9SDavid du Colombier 		td->offsets[i] = td->offsets[0];
1065906943f9SDavid du Colombier 	td->be = ptr2pa(bp->rp + size - 1);
1066906943f9SDavid du Colombier 	td->ctrl |= (0 << Tdfcshift);	/* frame count is 1 */
1067906943f9SDavid du Colombier 
1068906943f9SDavid du Colombier 	iso->frno = TRUNC(iso->frno + ep->pollival, Ntdframes);
1069906943f9SDavid du Colombier }
1070906943f9SDavid du Colombier 
1071906943f9SDavid du Colombier /*
1072906943f9SDavid du Colombier  * start I/O on the dummy td and setup a new dummy to fill up.
1073906943f9SDavid du Colombier  */
1074906943f9SDavid du Colombier static void
isoadvance(Ep * ep,Isoio * iso,Td * td)1075906943f9SDavid du Colombier isoadvance(Ep *ep, Isoio *iso, Td *td)
1076906943f9SDavid du Colombier {
1077906943f9SDavid du Colombier 	Td *dtd;
1078906943f9SDavid du Colombier 
1079906943f9SDavid du Colombier 	dtd = iso->atds;
1080906943f9SDavid du Colombier 	iso->atds = dtd->anext;
1081906943f9SDavid du Colombier 	iso->navail--;
1082906943f9SDavid du Colombier 	dtd->anext = nil;
1083906943f9SDavid du Colombier 	dtd->bp->wp = dtd->bp->rp;
1084906943f9SDavid du Colombier 	dtd->nexttd = 0;
1085906943f9SDavid du Colombier 	td->nexttd = ptr2pa(dtd);
1086906943f9SDavid du Colombier 	isodtdinit(ep, iso, dtd);
1087906943f9SDavid du Colombier 	iso->ed->tail = ptr2pa(dtd);
1088906943f9SDavid du Colombier }
1089906943f9SDavid du Colombier 
1090906943f9SDavid du Colombier static int
isocanwrite(void * a)1091906943f9SDavid du Colombier isocanwrite(void *a)
1092906943f9SDavid du Colombier {
1093906943f9SDavid du Colombier 	Isoio *iso;
1094906943f9SDavid du Colombier 
1095906943f9SDavid du Colombier 	iso = a;
1096906943f9SDavid du Colombier 	return iso->state == Qclose || iso->err != nil ||
1097906943f9SDavid du Colombier 		iso->navail > iso->nframes / 2;
1098906943f9SDavid du Colombier }
1099906943f9SDavid du Colombier 
1100906943f9SDavid du Colombier /*
1101906943f9SDavid du Colombier  * Service a completed/failed Td from the done queue.
1102906943f9SDavid du Colombier  * It may be of any transfer type.
1103906943f9SDavid du Colombier  * The queue is not in completion order.
1104906943f9SDavid du Colombier  * (It's actually in reverse completion order).
1105906943f9SDavid du Colombier  *
1106906943f9SDavid du Colombier  * When an error, a short packet, or a last Td is found
1107906943f9SDavid du Colombier  * we awake the process waiting for the transfer.
1108906943f9SDavid du Colombier  * Although later we will process other Tds completed
1109906943f9SDavid du Colombier  * before, epio won't be able to touch the current Td
1110906943f9SDavid du Colombier  * until interrupt returns and releases the lock on the
1111906943f9SDavid du Colombier  * controller.
1112906943f9SDavid du Colombier  */
1113906943f9SDavid du Colombier static void
qhinterrupt(Ctlr *,Ep * ep,Qio * io,Td * td,int)1114906943f9SDavid du Colombier qhinterrupt(Ctlr *, Ep *ep, Qio *io, Td *td, int)
1115906943f9SDavid du Colombier {
1116906943f9SDavid du Colombier 	Block *bp;
111739dc1420SDavid du Colombier 	int mode, err;
1118906943f9SDavid du Colombier 	Ed *ed;
1119906943f9SDavid du Colombier 
1120906943f9SDavid du Colombier 	ed = io->ed;
1121906943f9SDavid du Colombier 	if(io->state != Qrun)
1122906943f9SDavid du Colombier 		return;
1123906943f9SDavid du Colombier 	if(tdtok(td) == Tdtokin)
1124906943f9SDavid du Colombier 		mode = OREAD;
1125906943f9SDavid du Colombier 	else
1126906943f9SDavid du Colombier 		mode = OWRITE;
1127906943f9SDavid du Colombier 	bp = td->bp;
1128906943f9SDavid du Colombier 	err = tderrs(td);
1129906943f9SDavid du Colombier 
1130906943f9SDavid du Colombier 	switch(err){
1131906943f9SDavid du Colombier 	case Tddataovr:			/* Overrun is not an error */
1132906943f9SDavid du Colombier 		break;
11333be656c8SDavid du Colombier 	case Tdok:
11343be656c8SDavid du Colombier 		/* virtualbox doesn't always report underflow on short packets */
11353be656c8SDavid du Colombier 		if(td->cbp == 0)
11363be656c8SDavid du Colombier 			break;
11373be656c8SDavid du Colombier 		/* fall through */
1138906943f9SDavid du Colombier 	case Tddataund:
1139906943f9SDavid du Colombier 		/* short input packets are ok */
1140906943f9SDavid du Colombier 		if(mode == OREAD){
1141906943f9SDavid du Colombier 			if(td->cbp == 0)
1142906943f9SDavid du Colombier 				panic("ohci: short packet but cbp == 0");
1143db0946c0SDavid du Colombier 			/*
1144db0946c0SDavid du Colombier 			 * td->cbp and td->cbp0 are the real addresses
1145db0946c0SDavid du Colombier 			 * corresponding to virtual addresses bp->wp and
1146db0946c0SDavid du Colombier 			 * bp->rp respectively.
1147db0946c0SDavid du Colombier 			 */
1148db0946c0SDavid du Colombier 			bp->wp = bp->rp + (td->cbp - td->cbp0);
1149906943f9SDavid du Colombier 			if(bp->wp < bp->rp)
1150906943f9SDavid du Colombier 				panic("ohci: wp < rp");
1151906943f9SDavid du Colombier 			/*
1152906943f9SDavid du Colombier 			 * It's ok. clear error and flag as last in xfer.
1153906943f9SDavid du Colombier 			 * epio must ignore following Tds.
1154906943f9SDavid du Colombier 			 */
1155906943f9SDavid du Colombier 			td->last = 1;
1156906943f9SDavid du Colombier 			td->ctrl &= ~(Tdccmask << Tdccshift);
1157906943f9SDavid du Colombier 			break;
1158906943f9SDavid du Colombier 		}
1159906943f9SDavid du Colombier 		/* else fall; it's an error */
1160906943f9SDavid du Colombier 	case Tdcrc:
1161906943f9SDavid du Colombier 	case Tdbitstuff:
1162906943f9SDavid du Colombier 	case Tdbadtog:
1163906943f9SDavid du Colombier 	case Tdstalled:
1164906943f9SDavid du Colombier 	case Tdtmout:
1165906943f9SDavid du Colombier 	case Tdpidchk:
1166906943f9SDavid du Colombier 	case Tdbadpid:
1167906943f9SDavid du Colombier 		bp->wp = bp->rp;	/* no bytes in xfer. */
1168906943f9SDavid du Colombier 		io->err = errmsg(err);
1169906943f9SDavid du Colombier 		if(debug || ep->debug){
1170906943f9SDavid du Colombier 			print("tdinterrupt: failed err %d (%s)\n", err, io->err);
1171906943f9SDavid du Colombier 			dumptd(td, "failed", ed->ctrl & Ediso);
1172906943f9SDavid du Colombier 		}
1173906943f9SDavid du Colombier 		td->last = 1;
1174906943f9SDavid du Colombier 		break;
1175906943f9SDavid du Colombier 	default:
1176c9b6d007SDavid du Colombier 		panic("ohci: td cc %ud unknown", err);
1177906943f9SDavid du Colombier 	}
1178906943f9SDavid du Colombier 
1179906943f9SDavid du Colombier 	if(td->last != 0){
1180906943f9SDavid du Colombier 		/*
1181906943f9SDavid du Colombier 		 * clear td list and halt flag.
1182906943f9SDavid du Colombier 		 */
1183906943f9SDavid du Colombier 		ed->head = (ed->head & Edtoggle) | ed->tail;
1184906943f9SDavid du Colombier 		ed->tds = pa2ptr(ed->tail);
1185906943f9SDavid du Colombier 		io->state = Qdone;
1186906943f9SDavid du Colombier 		wakeup(io);
1187906943f9SDavid du Colombier 	}
1188906943f9SDavid du Colombier }
1189906943f9SDavid du Colombier 
1190906943f9SDavid du Colombier /*
1191906943f9SDavid du Colombier  * BUG: Iso input streams are not implemented.
1192906943f9SDavid du Colombier  */
1193906943f9SDavid du Colombier static void
isointerrupt(Ctlr * ctlr,Ep * ep,Qio * io,Td * td,int)1194906943f9SDavid du Colombier isointerrupt(Ctlr *ctlr, Ep *ep, Qio *io, Td *td, int)
1195906943f9SDavid du Colombier {
1196906943f9SDavid du Colombier 	Isoio *iso;
1197906943f9SDavid du Colombier 	Block *bp;
1198906943f9SDavid du Colombier 	Ed *ed;
1199d37e33ffSDavid du Colombier 	int err, isoerr;
1200906943f9SDavid du Colombier 
1201906943f9SDavid du Colombier 	iso = ep->aux;
1202906943f9SDavid du Colombier 	ed = io->ed;
1203906943f9SDavid du Colombier 	if(io->state == Qclose)
1204906943f9SDavid du Colombier 		return;
1205906943f9SDavid du Colombier 	bp = td->bp;
1206906943f9SDavid du Colombier 	/*
1207906943f9SDavid du Colombier 	 * When we get more than half the frames consecutive errors
1208906943f9SDavid du Colombier 	 * we signal an actual error. Errors in the entire Td are
1209906943f9SDavid du Colombier 	 * more serious and are always singaled.
1210906943f9SDavid du Colombier 	 * Errors like overrun are not really errors. In fact, for
1211906943f9SDavid du Colombier 	 * output, errors cannot be really detected. The driver will
1212906943f9SDavid du Colombier 	 * hopefully notice I/O errors on input endpoints and detach the device.
1213906943f9SDavid du Colombier 	 */
1214906943f9SDavid du Colombier 	err = tderrs(td);
1215906943f9SDavid du Colombier 	isoerr = (td->offsets[0] >> Tdiccshift) & Tdiccmask;
1216906943f9SDavid du Colombier 	if(isoerr == Tdok || isoerr == Tdnotacc)
1217906943f9SDavid du Colombier 		iso->nerrs = 0;
1218906943f9SDavid du Colombier 	else if(iso->nerrs++ > iso->nframes/2)
1219906943f9SDavid du Colombier 		err = Tdstalled;
1220906943f9SDavid du Colombier 	if(err != Tdok && err != Tddataovr){
1221906943f9SDavid du Colombier 		bp->wp = bp->rp;
1222906943f9SDavid du Colombier 		io->err = errmsg(err);
1223906943f9SDavid du Colombier 		if(debug || ep->debug){
1224906943f9SDavid du Colombier 			print("ohci: isointerrupt: ep%d.%d: err %d (%s) frnum 0x%lux\n",
1225906943f9SDavid du Colombier 				ep->dev->nb, ep->nb,
1226906943f9SDavid du Colombier 				err, errmsg(err), ctlr->ohci->fmnumber);
1227906943f9SDavid du Colombier 			dumptd(td, "failed", ed->ctrl & Ediso);
1228906943f9SDavid du Colombier 		}
1229906943f9SDavid du Colombier 	}
1230906943f9SDavid du Colombier 	td->bp->wp = td->bp->rp;
1231906943f9SDavid du Colombier 	td->nbytes = 0;
1232906943f9SDavid du Colombier 	td->anext = iso->atds;
1233906943f9SDavid du Colombier 	iso->atds = td;
1234906943f9SDavid du Colombier 	iso->navail++;
1235906943f9SDavid du Colombier 	/*
1236906943f9SDavid du Colombier 	 * If almost all Tds are avail the user is not doing I/O at the
1237906943f9SDavid du Colombier 	 * required rate. We put another Td in place to keep the polling rate.
1238906943f9SDavid du Colombier 	 */
1239906943f9SDavid du Colombier 	if(iso->err == nil && iso->navail > iso->nframes - 10)
1240906943f9SDavid du Colombier 		isoadvance(ep, iso, pa2ptr(iso->ed->tail));
1241906943f9SDavid du Colombier 	/*
1242906943f9SDavid du Colombier 	 * If there's enough buffering futher I/O can be done.
1243906943f9SDavid du Colombier 	 */
1244906943f9SDavid du Colombier 	if(isocanwrite(iso))
1245906943f9SDavid du Colombier 		wakeup(iso);
1246906943f9SDavid du Colombier }
1247906943f9SDavid du Colombier 
1248906943f9SDavid du Colombier static void
interrupt(Ureg *,void * arg)1249906943f9SDavid du Colombier interrupt(Ureg *, void *arg)
1250906943f9SDavid du Colombier {
1251*9d244d11SDavid du Colombier 	Td *td, *ntd;
1252906943f9SDavid du Colombier 	Hci *hp;
1253906943f9SDavid du Colombier 	Ctlr *ctlr;
125439dc1420SDavid du Colombier 	ulong status, curred;
125539dc1420SDavid du Colombier 	int i, frno;
1256906943f9SDavid du Colombier 
1257906943f9SDavid du Colombier 	hp = arg;
1258906943f9SDavid du Colombier 	ctlr = hp->aux;
1259906943f9SDavid du Colombier 	ilock(ctlr);
1260*9d244d11SDavid du Colombier 	ctlr->ohci->intrdisable = Mie;
1261*9d244d11SDavid du Colombier 	coherence();
1262*9d244d11SDavid du Colombier 	status = ctlr->ohci->intrsts & ctlr->ohci->intrenable;
1263906943f9SDavid du Colombier 	status &= Oc|Rhsc|Fno|Ue|Rd|Sf|Wdh|So;
1264906943f9SDavid du Colombier 	frno = TRUNC(ctlr->ohci->fmnumber, Ntdframes);
1265*9d244d11SDavid du Colombier 	if(status & Wdh){
1266906943f9SDavid du Colombier 		/* lsb of donehead has bit to flag other intrs.  */
1267906943f9SDavid du Colombier 		td = pa2ptr(ctlr->hcca->donehead & ~0xF);
1268906943f9SDavid du Colombier 
1269906943f9SDavid du Colombier 		for(i = 0; td != nil && i < 1024; i++){
1270906943f9SDavid du Colombier 			if(0)ddprint("ohci tdinterrupt: td %#p\n", td);
1271906943f9SDavid du Colombier 			ntd = pa2ptr(td->nexttd & ~0xF);
1272906943f9SDavid du Colombier 			td->nexttd = 0;
1273906943f9SDavid du Colombier 			if(td->ep == nil || td->io == nil)
1274*9d244d11SDavid du Colombier 				panic("ohci: interrupt: ep %#p io %#p",
1275*9d244d11SDavid du Colombier 					td->ep, td->io);
1276906943f9SDavid du Colombier 			ohciinterrupts[td->ep->ttype]++;
1277906943f9SDavid du Colombier 			if(td->ep->ttype == Tiso)
1278906943f9SDavid du Colombier 				isointerrupt(ctlr, td->ep, td->io, td, frno);
1279906943f9SDavid du Colombier 			else
1280906943f9SDavid du Colombier 				qhinterrupt(ctlr, td->ep, td->io, td, frno);
1281906943f9SDavid du Colombier 			td = ntd;
1282906943f9SDavid du Colombier 		}
1283*9d244d11SDavid du Colombier 		if(i >= 1024)
1284906943f9SDavid du Colombier 			print("ohci: bug: more than 1024 done Tds?\n");
1285906943f9SDavid du Colombier 		ctlr->hcca->donehead = 0;
1286*9d244d11SDavid du Colombier 	}
1287906943f9SDavid du Colombier 
1288906943f9SDavid du Colombier 	ctlr->ohci->intrsts = status;
1289906943f9SDavid du Colombier 	status &= ~Wdh;
1290906943f9SDavid du Colombier 	status &= ~Sf;
1291906943f9SDavid du Colombier 	if(status & So){
1292906943f9SDavid du Colombier 		print("ohci: sched overrun: too much load\n");
1293906943f9SDavid du Colombier 		ctlr->overrun++;
1294906943f9SDavid du Colombier 		status &= ~So;
1295906943f9SDavid du Colombier 	}
1296906943f9SDavid du Colombier 	if((status & Ue) != 0){
1297906943f9SDavid du Colombier 		curred = ctlr->ohci->periodcurred;
1298906943f9SDavid du Colombier 		print("ohci: unrecoverable error frame 0x%.8lux ed 0x%.8lux, "
1299906943f9SDavid du Colombier 			"ints %d %d %d %d\n",
1300906943f9SDavid du Colombier 			ctlr->ohci->fmnumber, curred,
1301906943f9SDavid du Colombier 			ohciinterrupts[Tctl], ohciinterrupts[Tintr],
1302906943f9SDavid du Colombier 			ohciinterrupts[Tbulk], ohciinterrupts[Tiso]);
1303906943f9SDavid du Colombier 		if(curred != 0)
1304906943f9SDavid du Colombier 			dumped(pa2ptr(curred));
1305906943f9SDavid du Colombier 		status &= ~Ue;
1306906943f9SDavid du Colombier 	}
1307906943f9SDavid du Colombier 	if(status != 0)
1308906943f9SDavid du Colombier 		print("ohci interrupt: unhandled sts 0x%.8lux\n", status);
1309*9d244d11SDavid du Colombier 	ctlr->ohci->intrenable = Mie | Wdh | Ue;
1310906943f9SDavid du Colombier 	iunlock(ctlr);
1311906943f9SDavid du Colombier }
1312906943f9SDavid du Colombier 
1313906943f9SDavid du Colombier /*
1314906943f9SDavid du Colombier  * The old dummy Td is used to implement the new Td.
1315906943f9SDavid du Colombier  * A new dummy is linked at the end of the old one and
1316906943f9SDavid du Colombier  * returned, to link further Tds if needed.
1317906943f9SDavid du Colombier  */
1318906943f9SDavid du Colombier static Td*
epgettd(Ep * ep,Qio * io,Td ** dtdp,int flags,void * a,int count)1319906943f9SDavid du Colombier epgettd(Ep *ep, Qio *io, Td **dtdp, int flags, void *a, int count)
1320906943f9SDavid du Colombier {
132139dc1420SDavid du Colombier 	Td *td, *dtd;
1322906943f9SDavid du Colombier 	Block *bp;
1323906943f9SDavid du Colombier 
13243be656c8SDavid du Colombier 	if(count <= BY2PG)
13253be656c8SDavid du Colombier 		bp = allocb(count);
13263be656c8SDavid du Colombier 	else{
13273be656c8SDavid du Colombier 		if(count > 2*BY2PG)
13283be656c8SDavid du Colombier 			panic("ohci: transfer > two pages");
13293be656c8SDavid du Colombier 		/* maximum of one physical page crossing allowed */
13303be656c8SDavid du Colombier 		bp = allocb(count+BY2PG);
13313be656c8SDavid du Colombier 		bp->rp = (uchar*)PGROUND((uintptr)bp->rp);
13323be656c8SDavid du Colombier 		bp->wp = bp->rp;
13333be656c8SDavid du Colombier 	}
1334906943f9SDavid du Colombier 	dtd = *dtdp;
1335906943f9SDavid du Colombier 	td = dtd;
1336906943f9SDavid du Colombier 	td->bp = bp;
1337906943f9SDavid du Colombier 	if(count > 0){
1338906943f9SDavid du Colombier 		td->cbp0 = td->cbp = ptr2pa(bp->wp);
1339906943f9SDavid du Colombier 		td->be = ptr2pa(bp->wp + count - 1);
1340906943f9SDavid du Colombier 		if(a != nil){
1341c8cbc0e9SDavid du Colombier 			/* validaddr((uintptr)a, count, 0); DEBUG */
1342906943f9SDavid du Colombier 			memmove(bp->wp, a, count);
1343906943f9SDavid du Colombier 		}
1344906943f9SDavid du Colombier 		bp->wp += count;
1345906943f9SDavid du Colombier 	}
1346906943f9SDavid du Colombier 	td->nbytes = count;
1347906943f9SDavid du Colombier 	td->ctrl = io->tok|Tdusetog|io->toggle|flags;
1348906943f9SDavid du Colombier 	if(io->toggle == Tddata0)
1349906943f9SDavid du Colombier 		io->toggle = Tddata1;
1350906943f9SDavid du Colombier 	else
1351906943f9SDavid du Colombier 		io->toggle = Tddata0;
1352906943f9SDavid du Colombier 	assert(td->ep == ep);
1353906943f9SDavid du Colombier 	td->io = io;
1354906943f9SDavid du Colombier  	dtd = tdalloc();	/* new dummy */
1355906943f9SDavid du Colombier 	dtd->ep = ep;
1356906943f9SDavid du Colombier 	td->nexttd = ptr2pa(dtd);
1357906943f9SDavid du Colombier 	td->next = dtd;
1358906943f9SDavid du Colombier 	*dtdp = dtd;
1359906943f9SDavid du Colombier 	return td;
1360906943f9SDavid du Colombier }
1361906943f9SDavid du Colombier 
1362906943f9SDavid du Colombier /*
1363906943f9SDavid du Colombier  * Try to get them idle
1364906943f9SDavid du Colombier  */
1365906943f9SDavid du Colombier static void
aborttds(Qio * io)1366906943f9SDavid du Colombier aborttds(Qio *io)
1367906943f9SDavid du Colombier {
1368906943f9SDavid du Colombier 	Ed *ed;
1369906943f9SDavid du Colombier 	Td *td;
1370906943f9SDavid du Colombier 
1371906943f9SDavid du Colombier 	ed = io->ed;
1372db0946c0SDavid du Colombier 	if(ed == nil)
1373db0946c0SDavid du Colombier 		return;
1374906943f9SDavid du Colombier 	ed->ctrl |= Edskip;
1375906943f9SDavid du Colombier 	for(td = ed->tds; td != nil; td = td->next)
1376906943f9SDavid du Colombier 		if(td->bp != nil)
1377906943f9SDavid du Colombier 			td->bp->wp = td->bp->rp;
1378906943f9SDavid du Colombier 	ed->head = (ed->head&0xF) | ed->tail;
1379906943f9SDavid du Colombier 	if((ed->ctrl & Ediso) == 0)
1380906943f9SDavid du Colombier 		ed->tds = pa2ptr(ed->tail);
1381906943f9SDavid du Colombier }
1382906943f9SDavid du Colombier 
1383906943f9SDavid du Colombier static int
epiodone(void * a)1384906943f9SDavid du Colombier epiodone(void *a)
1385906943f9SDavid du Colombier {
1386906943f9SDavid du Colombier 	Qio *io;
1387906943f9SDavid du Colombier 
1388906943f9SDavid du Colombier 	io = a;
1389906943f9SDavid du Colombier 	return io->state != Qrun;
1390906943f9SDavid du Colombier }
1391906943f9SDavid du Colombier 
1392906943f9SDavid du Colombier static void
epiowait(Ctlr * ctlr,Qio * io,int tmout,ulong)1393906943f9SDavid du Colombier epiowait(Ctlr *ctlr, Qio *io, int tmout, ulong)
1394906943f9SDavid du Colombier {
1395906943f9SDavid du Colombier 	Ed *ed;
1396906943f9SDavid du Colombier 	int timedout;
1397906943f9SDavid du Colombier 
1398906943f9SDavid du Colombier 	ed = io->ed;
1399906943f9SDavid du Colombier 	if(0)ddqprint("ohci io %#p sleep on ed %#p state %s\n",
1400906943f9SDavid du Colombier 		io, ed, iosname[io->state]);
1401906943f9SDavid du Colombier 	timedout = 0;
1402906943f9SDavid du Colombier 	if(waserror()){
1403906943f9SDavid du Colombier 		dqprint("ohci io %#p ed %#p timed out\n", io, ed);
1404906943f9SDavid du Colombier 		timedout++;
1405906943f9SDavid du Colombier 	}else{
1406906943f9SDavid du Colombier 		if(tmout == 0)
1407906943f9SDavid du Colombier 			sleep(io, epiodone, io);
1408906943f9SDavid du Colombier 		else
1409906943f9SDavid du Colombier 			tsleep(io, epiodone, io, tmout);
14106a5dc222SDavid du Colombier 		poperror();
14116a5dc222SDavid du Colombier 	}
1412906943f9SDavid du Colombier 	ilock(ctlr);
1413906943f9SDavid du Colombier 	if(io->state == Qrun)
1414906943f9SDavid du Colombier 		timedout = 1;
1415906943f9SDavid du Colombier 	else if(io->state != Qdone && io->state != Qclose)
1416906943f9SDavid du Colombier 		panic("epio: ed not done and not closed");
1417906943f9SDavid du Colombier 	if(timedout){
1418906943f9SDavid du Colombier 		aborttds(io);
1419906943f9SDavid du Colombier 		io->err = "request timed out";
1420906943f9SDavid du Colombier 		iunlock(ctlr);
1421906943f9SDavid du Colombier 		if(!waserror()){
1422906943f9SDavid du Colombier 			tsleep(&up->sleep, return0, 0, Abortdelay);
1423906943f9SDavid du Colombier 			poperror();
1424906943f9SDavid du Colombier 		}
1425906943f9SDavid du Colombier 		ilock(ctlr);
1426906943f9SDavid du Colombier 	}
1427906943f9SDavid du Colombier 	if(io->state != Qclose)
1428906943f9SDavid du Colombier 		io->state = Qidle;
1429906943f9SDavid du Colombier 	iunlock(ctlr);
14306a5dc222SDavid du Colombier }
14316a5dc222SDavid du Colombier 
14326a5dc222SDavid du Colombier /*
1433906943f9SDavid du Colombier  * Non iso I/O.
1434906943f9SDavid du Colombier  * To make it work for control transfers, the caller may
1435906943f9SDavid du Colombier  * lock the Qio for the entire control transfer.
14366a5dc222SDavid du Colombier  */
1437906943f9SDavid du Colombier static long
epio(Ep * ep,Qio * io,void * a,long count,int mustlock)1438d37e33ffSDavid du Colombier epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
1439906943f9SDavid du Colombier {
1440906943f9SDavid du Colombier 	Ed *ed;
1441906943f9SDavid du Colombier 	Ctlr *ctlr;
1442906943f9SDavid du Colombier 	char buf[80];
1443906943f9SDavid du Colombier 	char *err;
144439dc1420SDavid du Colombier 	uchar *c;
144539dc1420SDavid du Colombier 	Td *td, *ltd, *ntd, *td0;
1446c8cbc0e9SDavid du Colombier 	int last, ntds, tmout;
144739dc1420SDavid du Colombier 	long tot, n;
144839dc1420SDavid du Colombier 	ulong load;
1449906943f9SDavid du Colombier 
1450906943f9SDavid du Colombier 	ed = io->ed;
1451906943f9SDavid du Colombier 	ctlr = ep->hp->aux;
1452906943f9SDavid du Colombier 	io->debug = ep->debug;
1453d37e33ffSDavid du Colombier 	tmout = ep->tmout;
1454906943f9SDavid du Colombier 	ddeprint("ohci: %s ep%d.%d io %#p count %ld\n",
1455906943f9SDavid du Colombier 		io->tok == Tdtokin ? "in" : "out",
1456906943f9SDavid du Colombier 		ep->dev->nb, ep->nb, io, count);
1457906943f9SDavid du Colombier 	if((debug > 1 || ep->debug > 1) && io->tok != Tdtokin){
1458906943f9SDavid du Colombier 		seprintdata(buf, buf+sizeof(buf), a, count);
1459906943f9SDavid du Colombier 		print("\t%s\n", buf);
14606a5dc222SDavid du Colombier 	}
1461906943f9SDavid du Colombier 	if(mustlock){
1462906943f9SDavid du Colombier 		qlock(io);
1463906943f9SDavid du Colombier 		if(waserror()){
1464906943f9SDavid du Colombier 			qunlock(io);
1465906943f9SDavid du Colombier 			nexterror();
1466906943f9SDavid du Colombier 		}
1467906943f9SDavid du Colombier 	}
1468906943f9SDavid du Colombier 	io->err = nil;
1469906943f9SDavid du Colombier 	ilock(ctlr);
1470906943f9SDavid du Colombier 	if(io->state == Qclose){	/* Tds released by cancelio */
1471906943f9SDavid du Colombier 		iunlock(ctlr);
1472906943f9SDavid du Colombier 		error(io->err ? io->err : Eio);
1473906943f9SDavid du Colombier 	}
1474906943f9SDavid du Colombier 	if(io->state != Qidle)
1475906943f9SDavid du Colombier 		panic("epio: qio not idle");
1476906943f9SDavid du Colombier 	io->state = Qinstall;
1477906943f9SDavid du Colombier 
1478906943f9SDavid du Colombier 	c = a;
1479906943f9SDavid du Colombier 	ltd = td0 = ed->tds;
1480906943f9SDavid du Colombier 	load = tot = 0;
1481906943f9SDavid du Colombier 	do{
14823be656c8SDavid du Colombier 		n = 2*BY2PG;
1483906943f9SDavid du Colombier 		if(count-tot < n)
1484906943f9SDavid du Colombier 			n = count-tot;
1485d24fba2aSDavid du Colombier 		if(c != nil && io->tok != Tdtokin)
1486906943f9SDavid du Colombier 			td = epgettd(ep, io, &ltd, 0, c+tot, n);
1487d24fba2aSDavid du Colombier 		else
1488906943f9SDavid du Colombier 			td = epgettd(ep, io, &ltd, 0, nil, n);
1489906943f9SDavid du Colombier 		tot += n;
1490906943f9SDavid du Colombier 		load += ep->load;
1491906943f9SDavid du Colombier 	}while(tot < count);
1492906943f9SDavid du Colombier 	if(td0 == nil || ltd == nil || td0 == ltd)
1493906943f9SDavid du Colombier 		panic("epio: no td");
1494906943f9SDavid du Colombier 	td->last = 1;
1495906943f9SDavid du Colombier 	if(debug > 2 || ep->debug > 2)
1496906943f9SDavid du Colombier 		dumptds(td0, "put td", ep->ttype == Tiso);
1497906943f9SDavid du Colombier 	iunlock(ctlr);
1498906943f9SDavid du Colombier 
1499906943f9SDavid du Colombier 	ilock(ctlr);
1500906943f9SDavid du Colombier 	if(io->state != Qclose){
1501906943f9SDavid du Colombier 		io->iotime = TK2MS(MACHP(0)->ticks);
1502906943f9SDavid du Colombier 		io->state = Qrun;
1503906943f9SDavid du Colombier 		ed->tail = ptr2pa(ltd);
1504906943f9SDavid du Colombier 		if(ep->ttype == Tctl)
1505906943f9SDavid du Colombier 			ctlr->ohci->cmdsts |= Sclf;
1506906943f9SDavid du Colombier 		else if(ep->ttype == Tbulk)
1507906943f9SDavid du Colombier 			ctlr->ohci->cmdsts |= Sblf;
1508906943f9SDavid du Colombier 	}
1509906943f9SDavid du Colombier 	iunlock(ctlr);
1510906943f9SDavid du Colombier 
1511906943f9SDavid du Colombier 	epiowait(ctlr, io, tmout, load);
1512906943f9SDavid du Colombier 	ilock(ctlr);
1513906943f9SDavid du Colombier 	if(debug > 1 || ep->debug > 1)
1514906943f9SDavid du Colombier 		dumptds(td0, "got td", 0);
1515906943f9SDavid du Colombier 	iunlock(ctlr);
1516906943f9SDavid du Colombier 
1517906943f9SDavid du Colombier 	tot = 0;
1518906943f9SDavid du Colombier 	c = a;
1519906943f9SDavid du Colombier 	ntds = last = 0;
1520906943f9SDavid du Colombier 	for(td = td0; td != ltd; td = ntd){
1521906943f9SDavid du Colombier 		ntds++;
1522906943f9SDavid du Colombier 		/*
1523906943f9SDavid du Colombier 		 * If the Td is flagged as last we must
1524906943f9SDavid du Colombier 		 * ignore any following Td. The block may
1525906943f9SDavid du Colombier 		 * seem to have bytes but interrupt has not seen
1526906943f9SDavid du Colombier 		 * those Tds through the done queue, and they are void.
1527906943f9SDavid du Colombier 		 */
1528906943f9SDavid du Colombier 		if(last == 0 && tderrs(td) == Tdok){
1529906943f9SDavid du Colombier 			n = BLEN(td->bp);
1530906943f9SDavid du Colombier 			tot += n;
1531d24fba2aSDavid du Colombier 			if(c != nil && tdtok(td) == Tdtokin && n > 0){
1532906943f9SDavid du Colombier 				memmove(c, td->bp->rp, n);
1533906943f9SDavid du Colombier 				c += n;
1534906943f9SDavid du Colombier 			}
1535906943f9SDavid du Colombier 		}
1536906943f9SDavid du Colombier 		last |= td->last;
1537906943f9SDavid du Colombier 		ntd = td->next;
1538906943f9SDavid du Colombier 		tdfree(td);
1539906943f9SDavid du Colombier 	}
1540906943f9SDavid du Colombier 	if(edtoggle(ed) == 0)
1541906943f9SDavid du Colombier 		io->toggle = Tddata0;
1542906943f9SDavid du Colombier 	else
1543906943f9SDavid du Colombier 		io->toggle = Tddata1;
1544906943f9SDavid du Colombier 
1545906943f9SDavid du Colombier 	err = io->err;
1546906943f9SDavid du Colombier 	if(mustlock){
1547906943f9SDavid du Colombier 		qunlock(io);
1548906943f9SDavid du Colombier 		poperror();
1549906943f9SDavid du Colombier 	}
1550906943f9SDavid du Colombier 	ddeprint("ohci: io %#p: %d tds: return %ld err '%s'\n\n",
1551906943f9SDavid du Colombier 		io, ntds, tot, err);
1552906943f9SDavid du Colombier 	if(err != nil)
1553906943f9SDavid du Colombier 		error(err);
1554906943f9SDavid du Colombier 	if(tot < 0)
1555906943f9SDavid du Colombier 		error(Eio);
1556906943f9SDavid du Colombier 	return tot;
1557906943f9SDavid du Colombier }
1558906943f9SDavid du Colombier 
1559906943f9SDavid du Colombier /*
1560906943f9SDavid du Colombier  * halt condition was cleared on the endpoint. update our toggles.
1561906943f9SDavid du Colombier  */
1562906943f9SDavid du Colombier static void
clrhalt(Ep * ep)1563906943f9SDavid du Colombier clrhalt(Ep *ep)
1564906943f9SDavid du Colombier {
1565906943f9SDavid du Colombier 	Qio *io;
1566906943f9SDavid du Colombier 
1567906943f9SDavid du Colombier 	ep->clrhalt = 0;
1568906943f9SDavid du Colombier 	switch(ep->ttype){
1569906943f9SDavid du Colombier 	case Tbulk:
1570906943f9SDavid du Colombier 	case Tintr:
1571906943f9SDavid du Colombier 		io = ep->aux;
1572906943f9SDavid du Colombier 		if(ep->mode != OREAD){
1573906943f9SDavid du Colombier 			qlock(&io[OWRITE]);
1574906943f9SDavid du Colombier 			io[OWRITE].toggle = Tddata0;
1575906943f9SDavid du Colombier 			deprint("ep clrhalt for io %#p\n", io+OWRITE);
1576906943f9SDavid du Colombier 			qunlock(&io[OWRITE]);
1577906943f9SDavid du Colombier 		}
1578906943f9SDavid du Colombier 		if(ep->mode != OWRITE){
1579906943f9SDavid du Colombier 			qlock(&io[OREAD]);
1580906943f9SDavid du Colombier 			io[OREAD].toggle = Tddata0;
1581906943f9SDavid du Colombier 			deprint("ep clrhalt for io %#p\n", io+OREAD);
1582906943f9SDavid du Colombier 			qunlock(&io[OREAD]);
1583906943f9SDavid du Colombier 		}
1584906943f9SDavid du Colombier 		break;
15856a5dc222SDavid du Colombier 	}
15866a5dc222SDavid du Colombier }
15876a5dc222SDavid du Colombier 
15886a5dc222SDavid du Colombier static long
epread(Ep * ep,void * a,long count)1589906943f9SDavid du Colombier epread(Ep *ep, void *a, long count)
15906a5dc222SDavid du Colombier {
1591906943f9SDavid du Colombier 	Ctlio *cio;
1592906943f9SDavid du Colombier 	Qio *io;
1593906943f9SDavid du Colombier 	char buf[80];
1594906943f9SDavid du Colombier 	ulong delta;
15956a5dc222SDavid du Colombier 
1596906943f9SDavid du Colombier 	if(ep->aux == nil)
1597906943f9SDavid du Colombier 		panic("epread: not open");
1598906943f9SDavid du Colombier 
1599906943f9SDavid du Colombier 	switch(ep->ttype){
1600906943f9SDavid du Colombier 	case Tctl:
1601906943f9SDavid du Colombier 		cio = ep->aux;
1602906943f9SDavid du Colombier 		qlock(cio);
1603906943f9SDavid du Colombier 		if(waserror()){
1604906943f9SDavid du Colombier 			qunlock(cio);
1605906943f9SDavid du Colombier 			nexterror();
16066a5dc222SDavid du Colombier 		}
1607906943f9SDavid du Colombier 		ddeprint("epread ctl ndata %d\n", cio->ndata);
1608906943f9SDavid du Colombier 		if(cio->ndata < 0)
1609906943f9SDavid du Colombier 			error("request expected");
1610906943f9SDavid du Colombier 		else if(cio->ndata == 0){
1611906943f9SDavid du Colombier 			cio->ndata = -1;
1612906943f9SDavid du Colombier 			count = 0;
16136a5dc222SDavid du Colombier 		}else{
1614906943f9SDavid du Colombier 			if(count > cio->ndata)
1615906943f9SDavid du Colombier 				count = cio->ndata;
1616906943f9SDavid du Colombier 			if(count > 0)
1617906943f9SDavid du Colombier 				memmove(a, cio->data, count);
1618906943f9SDavid du Colombier 			/* BUG for big transfers */
1619906943f9SDavid du Colombier 			free(cio->data);
1620906943f9SDavid du Colombier 			cio->data = nil;
1621906943f9SDavid du Colombier 			cio->ndata = 0;	/* signal EOF next time */
16226a5dc222SDavid du Colombier 		}
1623906943f9SDavid du Colombier 		qunlock(cio);
16246a5dc222SDavid du Colombier 		poperror();
1625906943f9SDavid du Colombier 		if(debug>1 || ep->debug){
1626906943f9SDavid du Colombier 			seprintdata(buf, buf+sizeof(buf), a, count);
1627906943f9SDavid du Colombier 			print("epread: %s\n", buf);
1628906943f9SDavid du Colombier 		}
1629906943f9SDavid du Colombier 		return count;
1630906943f9SDavid du Colombier 	case Tbulk:
1631906943f9SDavid du Colombier 		io = ep->aux;
1632906943f9SDavid du Colombier 		if(ep->clrhalt)
1633906943f9SDavid du Colombier 			clrhalt(ep);
1634d37e33ffSDavid du Colombier 		return epio(ep, &io[OREAD], a, count, 1);
1635906943f9SDavid du Colombier 	case Tintr:
1636906943f9SDavid du Colombier 		io = ep->aux;
1637906943f9SDavid du Colombier 		delta = TK2MS(MACHP(0)->ticks) - io[OREAD].iotime + 1;
1638906943f9SDavid du Colombier 		if(delta < ep->pollival / 2)
1639906943f9SDavid du Colombier 			tsleep(&up->sleep, return0, 0, ep->pollival/2 - delta);
1640906943f9SDavid du Colombier 		if(ep->clrhalt)
1641906943f9SDavid du Colombier 			clrhalt(ep);
1642d37e33ffSDavid du Colombier 		return epio(ep, &io[OREAD], a, count, 1);
1643906943f9SDavid du Colombier 	case Tiso:
1644906943f9SDavid du Colombier 		panic("ohci: iso read not implemented");
1645906943f9SDavid du Colombier 		break;
1646906943f9SDavid du Colombier 	default:
1647906943f9SDavid du Colombier 		panic("epread: bad ep ttype %d", ep->ttype);
1648906943f9SDavid du Colombier 	}
1649906943f9SDavid du Colombier 	return -1;
1650906943f9SDavid du Colombier }
16516a5dc222SDavid du Colombier 
1652906943f9SDavid du Colombier /*
1653906943f9SDavid du Colombier  * Control transfers are one setup write (data0)
1654906943f9SDavid du Colombier  * plus zero or more reads/writes (data1, data0, ...)
1655906943f9SDavid du Colombier  * plus a final write/read with data1 to ack.
1656906943f9SDavid du Colombier  * For both host to device and device to host we perform
1657906943f9SDavid du Colombier  * the entire transfer when the user writes the request,
1658906943f9SDavid du Colombier  * and keep any data read from the device for a later read.
1659906943f9SDavid du Colombier  * We call epio three times instead of placing all Tds at
1660906943f9SDavid du Colombier  * the same time because doing so leads to crc/tmout errors
1661906943f9SDavid du Colombier  * for some devices.
1662906943f9SDavid du Colombier  * Upon errors on the data phase we must still run the status
1663906943f9SDavid du Colombier  * phase or the device may cease responding in the future.
1664906943f9SDavid du Colombier  */
1665906943f9SDavid du Colombier static long
epctlio(Ep * ep,Ctlio * cio,void * a,long count)1666906943f9SDavid du Colombier epctlio(Ep *ep, Ctlio *cio, void *a, long count)
1667906943f9SDavid du Colombier {
1668906943f9SDavid du Colombier 	uchar *c;
1669906943f9SDavid du Colombier 	long len;
1670906943f9SDavid du Colombier 
1671906943f9SDavid du Colombier 	ddeprint("epctlio: cio %#p ep%d.%d count %ld\n",
1672906943f9SDavid du Colombier 		cio, ep->dev->nb, ep->nb, count);
1673906943f9SDavid du Colombier 	if(count < Rsetuplen)
1674906943f9SDavid du Colombier 		error("short usb command");
1675906943f9SDavid du Colombier 	qlock(cio);
1676906943f9SDavid du Colombier 	free(cio->data);
1677906943f9SDavid du Colombier 	cio->data = nil;
1678906943f9SDavid du Colombier 	cio->ndata = 0;
1679906943f9SDavid du Colombier 	if(waserror()){
1680906943f9SDavid du Colombier 		qunlock(cio);
1681906943f9SDavid du Colombier 		free(cio->data);
1682906943f9SDavid du Colombier 		cio->data = nil;
1683906943f9SDavid du Colombier 		cio->ndata = 0;
1684906943f9SDavid du Colombier 		nexterror();
1685906943f9SDavid du Colombier 	}
1686906943f9SDavid du Colombier 
1687906943f9SDavid du Colombier 	/* set the address if unset and out of configuration state */
1688a23bc242SDavid du Colombier 	if(ep->dev->state != Dconfig && ep->dev->state != Dreset)
1689a23bc242SDavid du Colombier 		if(cio->usbid == 0){
1690906943f9SDavid du Colombier 			cio->usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
1691906943f9SDavid du Colombier 			edsetaddr(cio->ed, cio->usbid);
1692906943f9SDavid du Colombier 		}
1693906943f9SDavid du Colombier 	/* adjust maxpkt if the user has learned a different one */
1694906943f9SDavid du Colombier 	if(edmaxpkt(cio->ed) != ep->maxpkt)
1695906943f9SDavid du Colombier 		edsetmaxpkt(cio->ed, ep->maxpkt);
1696906943f9SDavid du Colombier 	c = a;
1697906943f9SDavid du Colombier 	cio->tok = Tdtoksetup;
1698906943f9SDavid du Colombier 	cio->toggle = Tddata0;
1699d37e33ffSDavid du Colombier 	if(epio(ep, cio, a, Rsetuplen, 0) < Rsetuplen)
1700906943f9SDavid du Colombier 		error(Eio);
1701906943f9SDavid du Colombier 
1702906943f9SDavid du Colombier 	a = c + Rsetuplen;
1703906943f9SDavid du Colombier 	count -= Rsetuplen;
1704906943f9SDavid du Colombier 
1705906943f9SDavid du Colombier 	cio->toggle = Tddata1;
1706906943f9SDavid du Colombier 	if(c[Rtype] & Rd2h){
1707906943f9SDavid du Colombier 		cio->tok = Tdtokin;
1708906943f9SDavid du Colombier 		len = GET2(c+Rcount);
1709906943f9SDavid du Colombier 		if(len <= 0)
1710906943f9SDavid du Colombier 			error("bad length in d2h request");
1711906943f9SDavid du Colombier 		if(len > Maxctllen)
1712906943f9SDavid du Colombier 			error("d2h data too large to fit in ohci");
1713906943f9SDavid du Colombier 		a = cio->data = smalloc(len+1);
1714906943f9SDavid du Colombier 	}else{
1715906943f9SDavid du Colombier 		cio->tok = Tdtokout;
1716906943f9SDavid du Colombier 		len = count;
1717906943f9SDavid du Colombier 	}
1718906943f9SDavid du Colombier 	if(len > 0)
1719906943f9SDavid du Colombier 		if(waserror())
1720906943f9SDavid du Colombier 			len = -1;
1721906943f9SDavid du Colombier 		else{
1722d37e33ffSDavid du Colombier 			len = epio(ep, cio, a, len, 0);
1723906943f9SDavid du Colombier 			poperror();
1724906943f9SDavid du Colombier 		}
1725906943f9SDavid du Colombier 	if(c[Rtype] & Rd2h){
1726906943f9SDavid du Colombier 		count = Rsetuplen;
1727906943f9SDavid du Colombier 		cio->ndata = len;
1728906943f9SDavid du Colombier 		cio->tok = Tdtokout;
1729906943f9SDavid du Colombier 	}else{
1730906943f9SDavid du Colombier 		if(len < 0)
1731906943f9SDavid du Colombier 			count = -1;
1732906943f9SDavid du Colombier 		else
1733906943f9SDavid du Colombier 			count = Rsetuplen + len;
1734906943f9SDavid du Colombier 		cio->tok = Tdtokin;
1735906943f9SDavid du Colombier 	}
1736906943f9SDavid du Colombier 	cio->toggle = Tddata1;
1737d24fba2aSDavid du Colombier 	epio(ep, cio, nil, 0, 0);
1738906943f9SDavid du Colombier 	qunlock(cio);
1739906943f9SDavid du Colombier 	poperror();
1740906943f9SDavid du Colombier 	ddeprint("epctlio cio %#p return %ld\n", cio, count);
1741906943f9SDavid du Colombier 	return count;
1742906943f9SDavid du Colombier }
1743906943f9SDavid du Colombier 
1744906943f9SDavid du Colombier /*
1745906943f9SDavid du Colombier  * Put new samples in the dummy Td.
1746906943f9SDavid du Colombier  * BUG: This does only a transfer per Td. We could do up to 8.
1747906943f9SDavid du Colombier  */
1748906943f9SDavid du Colombier static long
putsamples(Ctlr * ctlr,Ep * ep,Isoio * iso,uchar * b,long count)1749906943f9SDavid du Colombier putsamples(Ctlr *ctlr, Ep *ep, Isoio *iso, uchar *b, long count)
1750906943f9SDavid du Colombier {
1751906943f9SDavid du Colombier 	Td *td;
1752906943f9SDavid du Colombier 	ulong n;
1753906943f9SDavid du Colombier 
1754906943f9SDavid du Colombier 	td = pa2ptr(iso->ed->tail);
1755906943f9SDavid du Colombier 	n = count;
1756906943f9SDavid du Colombier 	if(n > td->nbytes - BLEN(td->bp))
1757906943f9SDavid du Colombier 		n = td->nbytes - BLEN(td->bp);
1758906943f9SDavid du Colombier 	assert(td->bp->wp + n <= td->bp->lim);
1759906943f9SDavid du Colombier 	memmove(td->bp->wp, b, n);
1760906943f9SDavid du Colombier 	td->bp->wp += n;
1761906943f9SDavid du Colombier 	if(BLEN(td->bp) == td->nbytes){	/* full Td: activate it */
1762906943f9SDavid du Colombier 		ilock(ctlr);
1763906943f9SDavid du Colombier 		isoadvance(ep, iso, td);
1764906943f9SDavid du Colombier 		iunlock(ctlr);
1765906943f9SDavid du Colombier 	}
1766906943f9SDavid du Colombier 	return n;
1767906943f9SDavid du Colombier }
1768906943f9SDavid du Colombier 
1769906943f9SDavid du Colombier static long
episowrite(Ep * ep,void * a,long count)1770906943f9SDavid du Colombier episowrite(Ep *ep, void *a, long count)
1771906943f9SDavid du Colombier {
1772d37e33ffSDavid du Colombier 	long tot, nw;
1773d37e33ffSDavid du Colombier 	char *err;
1774906943f9SDavid du Colombier 	uchar *b;
1775906943f9SDavid du Colombier 	Ctlr *ctlr;
1776d37e33ffSDavid du Colombier 	Isoio *iso;
1777906943f9SDavid du Colombier 
1778906943f9SDavid du Colombier 	ctlr = ep->hp->aux;
1779906943f9SDavid du Colombier 	iso = ep->aux;
1780906943f9SDavid du Colombier 	iso->debug = ep->debug;
1781906943f9SDavid du Colombier 
1782906943f9SDavid du Colombier 	qlock(iso);
1783906943f9SDavid du Colombier 	if(waserror()){
1784906943f9SDavid du Colombier 		qunlock(iso);
1785906943f9SDavid du Colombier 		nexterror();
1786906943f9SDavid du Colombier 	}
1787906943f9SDavid du Colombier 	diprint("ohci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
1788906943f9SDavid du Colombier 	ilock(ctlr);
1789906943f9SDavid du Colombier 	if(iso->state == Qclose){
1790906943f9SDavid du Colombier 		iunlock(ctlr);
1791906943f9SDavid du Colombier 		error(iso->err ? iso->err : Eio);
1792906943f9SDavid du Colombier 	}
1793906943f9SDavid du Colombier 	iso->state = Qrun;
1794906943f9SDavid du Colombier 	b = a;
1795906943f9SDavid du Colombier 	for(tot = 0; tot < count; tot += nw){
1796906943f9SDavid du Colombier 		while(isocanwrite(iso) == 0){
1797906943f9SDavid du Colombier 			iunlock(ctlr);
1798906943f9SDavid du Colombier 			diprint("ohci: episowrite: %#p sleep\n", iso);
1799906943f9SDavid du Colombier 			if(waserror()){
1800906943f9SDavid du Colombier 				if(iso->err == nil)
1801906943f9SDavid du Colombier 					iso->err = "I/O timed out";
1802906943f9SDavid du Colombier 				ilock(ctlr);
1803906943f9SDavid du Colombier 				break;
1804906943f9SDavid du Colombier 			}
1805d37e33ffSDavid du Colombier 			tsleep(iso, isocanwrite, iso, ep->tmout);
1806906943f9SDavid du Colombier 			poperror();
1807906943f9SDavid du Colombier 			ilock(ctlr);
1808906943f9SDavid du Colombier 		}
1809906943f9SDavid du Colombier 		err = iso->err;
1810906943f9SDavid du Colombier 		iso->err = nil;
1811906943f9SDavid du Colombier 		if(iso->state == Qclose || err != nil){
1812906943f9SDavid du Colombier 			iunlock(ctlr);
1813906943f9SDavid du Colombier 			error(err ? err : Eio);
1814906943f9SDavid du Colombier 		}
1815906943f9SDavid du Colombier 		if(iso->state != Qrun)
1816906943f9SDavid du Colombier 			panic("episowrite: iso not running");
1817906943f9SDavid du Colombier 		iunlock(ctlr);		/* We could page fault here */
1818906943f9SDavid du Colombier 		nw = putsamples(ctlr, ep, iso, b+tot, count-tot);
1819906943f9SDavid du Colombier 		ilock(ctlr);
1820906943f9SDavid du Colombier 	}
1821906943f9SDavid du Colombier 	if(iso->state != Qclose)
1822906943f9SDavid du Colombier 		iso->state = Qdone;
1823906943f9SDavid du Colombier 	iunlock(ctlr);
1824906943f9SDavid du Colombier 	err = iso->err;		/* in case it failed early */
1825906943f9SDavid du Colombier 	iso->err = nil;
1826906943f9SDavid du Colombier 	qunlock(iso);
1827906943f9SDavid du Colombier 	poperror();
1828906943f9SDavid du Colombier 	if(err != nil)
1829906943f9SDavid du Colombier 		error(err);
1830906943f9SDavid du Colombier 	diprint("ohci: episowrite: %#p %ld bytes\n", iso, tot);
1831906943f9SDavid du Colombier 	return tot;
1832906943f9SDavid du Colombier }
1833906943f9SDavid du Colombier 
1834906943f9SDavid du Colombier static long
epwrite(Ep * ep,void * a,long count)1835906943f9SDavid du Colombier epwrite(Ep *ep, void *a, long count)
1836906943f9SDavid du Colombier {
1837906943f9SDavid du Colombier 	Qio *io;
1838906943f9SDavid du Colombier 	Ctlio *cio;
1839906943f9SDavid du Colombier 	ulong delta;
1840906943f9SDavid du Colombier 	uchar *b;
184139dc1420SDavid du Colombier 	long tot, nw;
1842906943f9SDavid du Colombier 
1843906943f9SDavid du Colombier 	if(ep->aux == nil)
1844906943f9SDavid du Colombier 		panic("ohci: epwrite: not open");
1845906943f9SDavid du Colombier 	switch(ep->ttype){
1846906943f9SDavid du Colombier 	case Tctl:
1847906943f9SDavid du Colombier 		cio = ep->aux;
1848906943f9SDavid du Colombier 		return epctlio(ep, cio, a, count);
1849906943f9SDavid du Colombier 	case Tbulk:
1850906943f9SDavid du Colombier 		io = ep->aux;
1851906943f9SDavid du Colombier 		if(ep->clrhalt)
1852906943f9SDavid du Colombier 			clrhalt(ep);
1853906943f9SDavid du Colombier 		/*
1854906943f9SDavid du Colombier 		 * Put at most Tdatomic Tds (512 bytes) at a time.
1855906943f9SDavid du Colombier 		 * Otherwise some devices produce babble errors.
1856906943f9SDavid du Colombier 		 */
1857906943f9SDavid du Colombier 		b = a;
1858add6b5c5SDavid du Colombier 		assert(a != nil);
1859906943f9SDavid du Colombier 		for(tot = 0; tot < count ; tot += nw){
1860906943f9SDavid du Colombier 			nw = count - tot;
1861906943f9SDavid du Colombier 			if(nw > Tdatomic * ep->maxpkt)
1862906943f9SDavid du Colombier 				nw = Tdatomic * ep->maxpkt;
1863d37e33ffSDavid du Colombier 			nw = epio(ep, &io[OWRITE], b+tot, nw, 1);
1864906943f9SDavid du Colombier 		}
1865906943f9SDavid du Colombier 		return tot;
1866906943f9SDavid du Colombier 	case Tintr:
1867906943f9SDavid du Colombier 		io = ep->aux;
1868906943f9SDavid du Colombier 		delta = TK2MS(MACHP(0)->ticks) - io[OWRITE].iotime + 1;
1869906943f9SDavid du Colombier 		if(delta < ep->pollival)
1870906943f9SDavid du Colombier 			tsleep(&up->sleep, return0, 0, ep->pollival - delta);
1871906943f9SDavid du Colombier 		if(ep->clrhalt)
1872906943f9SDavid du Colombier 			clrhalt(ep);
1873d37e33ffSDavid du Colombier 		return epio(ep, &io[OWRITE], a, count, 1);
1874906943f9SDavid du Colombier 	case Tiso:
1875906943f9SDavid du Colombier 		return episowrite(ep, a, count);
1876906943f9SDavid du Colombier 	default:
1877906943f9SDavid du Colombier 		panic("ohci: epwrite: bad ep ttype %d", ep->ttype);
1878906943f9SDavid du Colombier 	}
1879906943f9SDavid du Colombier 	return -1;
1880906943f9SDavid du Colombier }
1881906943f9SDavid du Colombier 
1882906943f9SDavid du Colombier static Ed*
newed(Ctlr * ctlr,Ep * ep,Qio * io,char *)1883906943f9SDavid du Colombier newed(Ctlr *ctlr, Ep *ep, Qio *io, char *)
1884906943f9SDavid du Colombier {
1885906943f9SDavid du Colombier 	Ed *ed;
1886906943f9SDavid du Colombier 	Td *td;
1887906943f9SDavid du Colombier 
1888906943f9SDavid du Colombier 	ed = io->ed = edalloc();	/* no errors raised here, really */
1889906943f9SDavid du Colombier 	td = tdalloc();
1890906943f9SDavid du Colombier 	td->ep = ep;
1891906943f9SDavid du Colombier 	td->io = io;
1892906943f9SDavid du Colombier 	ed->tail =  ptr2pa(td);
1893906943f9SDavid du Colombier 	ed->head = ptr2pa(td);
1894906943f9SDavid du Colombier 	ed->tds = td;
1895906943f9SDavid du Colombier 	ed->ep = ep;
1896906943f9SDavid du Colombier 	ed->ctrl = (ep->maxpkt & Edmpsmask) << Edmpsshift;
1897906943f9SDavid du Colombier 	if(ep->ttype == Tiso)
1898906943f9SDavid du Colombier 		ed->ctrl |= Ediso;
1899906943f9SDavid du Colombier 	if(waserror()){
1900906943f9SDavid du Colombier 		edfree(ed);
1901906943f9SDavid du Colombier 		io->ed = nil;
1902906943f9SDavid du Colombier 		nexterror();
1903906943f9SDavid du Colombier 	}
1904906943f9SDavid du Colombier 	/* For setup endpoints we start with the config address */
1905906943f9SDavid du Colombier 	if(ep->ttype != Tctl)
1906906943f9SDavid du Colombier 		edsetaddr(io->ed, io->usbid);
1907906943f9SDavid du Colombier 	if(ep->dev->speed == Lowspeed)
1908906943f9SDavid du Colombier 		ed->ctrl |= Edlow;
1909906943f9SDavid du Colombier 	switch(io->tok){
1910906943f9SDavid du Colombier 	case Tdtokin:
1911906943f9SDavid du Colombier 		ed->ctrl |= Edin;
1912906943f9SDavid du Colombier 		break;
1913906943f9SDavid du Colombier 	case Tdtokout:
1914906943f9SDavid du Colombier 		ed->ctrl |= Edout;
1915906943f9SDavid du Colombier 		break;
1916906943f9SDavid du Colombier 	default:
1917906943f9SDavid du Colombier 		ed->ctrl |= Edtddir;	/* Td will say */
1918906943f9SDavid du Colombier 		break;
1919906943f9SDavid du Colombier 	}
1920906943f9SDavid du Colombier 
1921906943f9SDavid du Colombier 	switch(ep->ttype){
1922906943f9SDavid du Colombier 	case Tctl:
1923906943f9SDavid du Colombier 		ilock(ctlr);
1924906943f9SDavid du Colombier 		edlinked(ed, ctlhd(ctlr));
1925906943f9SDavid du Colombier 		setctlhd(ctlr, ed);
1926906943f9SDavid du Colombier 		iunlock(ctlr);
1927906943f9SDavid du Colombier 		break;
1928906943f9SDavid du Colombier 	case Tbulk:
1929906943f9SDavid du Colombier 		ilock(ctlr);
1930906943f9SDavid du Colombier 		edlinked(ed, bulkhd(ctlr));
1931906943f9SDavid du Colombier 		setbulkhd(ctlr, ed);
1932906943f9SDavid du Colombier 		iunlock(ctlr);
1933906943f9SDavid du Colombier 		break;
1934906943f9SDavid du Colombier 	case Tintr:
1935906943f9SDavid du Colombier 	case Tiso:
1936906943f9SDavid du Colombier 		ilock(ctlr);
1937906943f9SDavid du Colombier 		schedq(ctlr, io, ep->pollival);
1938906943f9SDavid du Colombier 		iunlock(ctlr);
1939906943f9SDavid du Colombier 		break;
1940906943f9SDavid du Colombier 	default:
1941906943f9SDavid du Colombier 		panic("ohci: newed: bad ttype");
1942906943f9SDavid du Colombier 	}
1943906943f9SDavid du Colombier 	poperror();
1944906943f9SDavid du Colombier 	return ed;
1945906943f9SDavid du Colombier }
1946906943f9SDavid du Colombier 
1947906943f9SDavid du Colombier static void
isoopen(Ctlr * ctlr,Ep * ep)1948906943f9SDavid du Colombier isoopen(Ctlr *ctlr, Ep *ep)
1949906943f9SDavid du Colombier {
195039dc1420SDavid du Colombier 	Td *td, *edtds;
1951906943f9SDavid du Colombier 	Isoio *iso;
1952906943f9SDavid du Colombier 	int i;
1953906943f9SDavid du Colombier 
1954906943f9SDavid du Colombier 	iso = ep->aux;
1955906943f9SDavid du Colombier 	iso->usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
1956906943f9SDavid du Colombier 	iso->bw = ep->hz * ep->samplesz;	/* bytes/sec */
1957906943f9SDavid du Colombier 	if(ep->mode != OWRITE){
1958906943f9SDavid du Colombier 		print("ohci: bug: iso input streams not implemented\n");
1959906943f9SDavid du Colombier 		error("ohci iso input streams not implemented");
1960906943f9SDavid du Colombier 	}else
1961906943f9SDavid du Colombier 		iso->tok = Tdtokout;
1962906943f9SDavid du Colombier 
1963906943f9SDavid du Colombier 	iso->left = 0;
1964906943f9SDavid du Colombier 	iso->nerrs = 0;
1965906943f9SDavid du Colombier 	iso->frno = TRUNC(ctlr->ohci->fmnumber + 10, Ntdframes);
1966906943f9SDavid du Colombier 	iso->nframes = 1000 / ep->pollival;
1967906943f9SDavid du Colombier 	if(iso->nframes < 10){
1968906943f9SDavid du Colombier 		print("ohci: isoopen: less than 10 frames; using 10.\n");
1969906943f9SDavid du Colombier 		iso->nframes = 10;
1970906943f9SDavid du Colombier 	}
1971906943f9SDavid du Colombier 	iso->navail = iso->nframes;
1972906943f9SDavid du Colombier 	iso->atds = edtds = nil;
1973906943f9SDavid du Colombier 	for(i = 0; i < iso->nframes-1; i++){	/* -1 for dummy */
1974906943f9SDavid du Colombier 		td = tdalloc();
1975906943f9SDavid du Colombier 		td->ep = ep;
1976906943f9SDavid du Colombier 		td->io = iso;
1977906943f9SDavid du Colombier 		td->bp = allocb(ep->maxpkt);
1978906943f9SDavid du Colombier 		td->anext = iso->atds;		/* link as avail */
1979906943f9SDavid du Colombier 		iso->atds = td;
1980906943f9SDavid du Colombier 		td->next = edtds;
1981906943f9SDavid du Colombier 		edtds = td;
1982906943f9SDavid du Colombier 	}
1983906943f9SDavid du Colombier 	newed(ctlr, ep, iso, "iso");		/* allocates a dummy td */
1984906943f9SDavid du Colombier 	iso->ed->tds->bp = allocb(ep->maxpkt);	/* but not its block */
1985906943f9SDavid du Colombier 	iso->ed->tds->next = edtds;
1986906943f9SDavid du Colombier 	isodtdinit(ep, iso, iso->ed->tds);
1987906943f9SDavid du Colombier }
1988906943f9SDavid du Colombier 
1989906943f9SDavid du Colombier /*
1990906943f9SDavid du Colombier  * Allocate the endpoint and set it up for I/O
1991906943f9SDavid du Colombier  * in the controller. This must follow what's said
1992906943f9SDavid du Colombier  * in Ep regarding configuration, including perhaps
1993906943f9SDavid du Colombier  * the saved toggles (saved on a previous close of
1994906943f9SDavid du Colombier  * the endpoint data file by epclose).
1995906943f9SDavid du Colombier  */
1996906943f9SDavid du Colombier static void
epopen(Ep * ep)1997906943f9SDavid du Colombier epopen(Ep *ep)
1998906943f9SDavid du Colombier {
1999906943f9SDavid du Colombier 	Ctlr *ctlr;
2000906943f9SDavid du Colombier 	Qio *io;
2001906943f9SDavid du Colombier 	Ctlio *cio;
2002906943f9SDavid du Colombier 	ulong usbid;
2003906943f9SDavid du Colombier 
2004906943f9SDavid du Colombier 	ctlr = ep->hp->aux;
2005906943f9SDavid du Colombier 	deprint("ohci: epopen ep%d.%d\n", ep->dev->nb, ep->nb);
2006906943f9SDavid du Colombier 	if(ep->aux != nil)
2007906943f9SDavid du Colombier 		panic("ohci: epopen called with open ep");
2008906943f9SDavid du Colombier 	if(waserror()){
2009906943f9SDavid du Colombier 		free(ep->aux);
2010906943f9SDavid du Colombier 		ep->aux = nil;
2011906943f9SDavid du Colombier 		nexterror();
2012906943f9SDavid du Colombier 	}
2013906943f9SDavid du Colombier 	switch(ep->ttype){
2014906943f9SDavid du Colombier 	case Tnone:
2015906943f9SDavid du Colombier 		error("endpoint not configured");
2016906943f9SDavid du Colombier 	case Tiso:
2017906943f9SDavid du Colombier 		ep->aux = smalloc(sizeof(Isoio));
2018906943f9SDavid du Colombier 		isoopen(ctlr, ep);
2019906943f9SDavid du Colombier 		break;
2020906943f9SDavid du Colombier 	case Tctl:
2021906943f9SDavid du Colombier 		cio = ep->aux = smalloc(sizeof(Ctlio));
2022906943f9SDavid du Colombier 		cio->debug = ep->debug;
2023906943f9SDavid du Colombier 		cio->ndata = -1;
2024906943f9SDavid du Colombier 		cio->data = nil;
2025906943f9SDavid du Colombier 		cio->tok = -1;	/* invalid; Tds will say */
2026906943f9SDavid du Colombier 		if(ep->dev->isroot != 0 && ep->nb == 0)	/* root hub */
2027906943f9SDavid du Colombier 			break;
2028906943f9SDavid du Colombier 		newed(ctlr, ep, cio, "epc");
2029906943f9SDavid du Colombier 		break;
2030906943f9SDavid du Colombier 	case Tbulk:
2031906943f9SDavid du Colombier 		ep->pollival = 1;	/* assume this; doesn't really matter */
2032906943f9SDavid du Colombier 		/* and fall... */
2033906943f9SDavid du Colombier 	case Tintr:
2034906943f9SDavid du Colombier 		io = ep->aux = smalloc(sizeof(Qio)*2);
2035906943f9SDavid du Colombier 		io[OREAD].debug = io[OWRITE].debug = ep->debug;
2036906943f9SDavid du Colombier 		usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
2037906943f9SDavid du Colombier 		if(ep->mode != OREAD){
2038906943f9SDavid du Colombier 			if(ep->toggle[OWRITE] != 0)
2039906943f9SDavid du Colombier 				io[OWRITE].toggle = Tddata1;
2040906943f9SDavid du Colombier 			else
2041906943f9SDavid du Colombier 				io[OWRITE].toggle = Tddata0;
2042906943f9SDavid du Colombier 			io[OWRITE].tok = Tdtokout;
2043906943f9SDavid du Colombier 			io[OWRITE].usbid = usbid;
2044906943f9SDavid du Colombier 			io[OWRITE].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */
2045906943f9SDavid du Colombier 			newed(ctlr, ep, io+OWRITE, "epw");
2046906943f9SDavid du Colombier 		}
2047906943f9SDavid du Colombier 		if(ep->mode != OWRITE){
2048906943f9SDavid du Colombier 			if(ep->toggle[OREAD] != 0)
2049906943f9SDavid du Colombier 				io[OREAD].toggle = Tddata1;
2050906943f9SDavid du Colombier 			else
2051906943f9SDavid du Colombier 				io[OREAD].toggle = Tddata0;
2052906943f9SDavid du Colombier 			io[OREAD].tok = Tdtokin;
2053906943f9SDavid du Colombier 			io[OREAD].usbid = usbid;
2054906943f9SDavid du Colombier 			io[OREAD].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */
2055906943f9SDavid du Colombier 			newed(ctlr, ep, io+OREAD, "epr");
2056906943f9SDavid du Colombier 		}
2057906943f9SDavid du Colombier 		break;
2058906943f9SDavid du Colombier 	}
2059906943f9SDavid du Colombier 	deprint("ohci: epopen done:\n");
2060906943f9SDavid du Colombier 	if(debug || ep->debug)
2061906943f9SDavid du Colombier 		dump(ep->hp);
2062906943f9SDavid du Colombier 	poperror();
2063906943f9SDavid du Colombier }
2064906943f9SDavid du Colombier 
2065906943f9SDavid du Colombier static void
cancelio(Ep * ep,Qio * io)2066906943f9SDavid du Colombier cancelio(Ep *ep, Qio *io)
2067906943f9SDavid du Colombier {
2068906943f9SDavid du Colombier 	Ed *ed;
2069906943f9SDavid du Colombier 	Ctlr *ctlr;
2070906943f9SDavid du Colombier 
2071906943f9SDavid du Colombier 	ctlr = ep->hp->aux;
2072906943f9SDavid du Colombier 
2073906943f9SDavid du Colombier 	ilock(ctlr);
2074906943f9SDavid du Colombier 	if(io == nil || io->state == Qclose){
2075767ad8bbSDavid du Colombier 		assert(io == nil || io->ed == nil);
2076906943f9SDavid du Colombier 		iunlock(ctlr);
2077906943f9SDavid du Colombier 		return;
2078906943f9SDavid du Colombier 	}
2079767ad8bbSDavid du Colombier 	ed = io->ed;
2080906943f9SDavid du Colombier 	io->state = Qclose;
2081906943f9SDavid du Colombier 	io->err = Eio;
2082906943f9SDavid du Colombier 	aborttds(io);
2083906943f9SDavid du Colombier 	iunlock(ctlr);
2084906943f9SDavid du Colombier 	if(!waserror()){
2085906943f9SDavid du Colombier 		tsleep(&up->sleep, return0, 0, Abortdelay);
2086906943f9SDavid du Colombier 		poperror();
2087906943f9SDavid du Colombier 	}
2088906943f9SDavid du Colombier 
2089906943f9SDavid du Colombier 	wakeup(io);
2090906943f9SDavid du Colombier 	qlock(io);
2091906943f9SDavid du Colombier 	/* wait for epio if running */
2092906943f9SDavid du Colombier 	qunlock(io);
2093906943f9SDavid du Colombier 
2094906943f9SDavid du Colombier 	ilock(ctlr);
2095906943f9SDavid du Colombier 	switch(ep->ttype){
2096906943f9SDavid du Colombier 	case Tctl:
2097906943f9SDavid du Colombier 		unlinkctl(ctlr, ed);
2098906943f9SDavid du Colombier 		break;
2099906943f9SDavid du Colombier 	case Tbulk:
2100906943f9SDavid du Colombier 		unlinkbulk(ctlr, ed);
2101906943f9SDavid du Colombier 		break;
2102906943f9SDavid du Colombier 	case Tintr:
2103906943f9SDavid du Colombier 	case Tiso:
2104906943f9SDavid du Colombier 		unschedq(ctlr, io);
2105906943f9SDavid du Colombier 		break;
2106906943f9SDavid du Colombier 	default:
2107906943f9SDavid du Colombier 		panic("ohci cancelio: bad ttype");
2108906943f9SDavid du Colombier 	}
2109906943f9SDavid du Colombier 	iunlock(ctlr);
2110906943f9SDavid du Colombier 	edfree(io->ed);
2111906943f9SDavid du Colombier 	io->ed = nil;
2112906943f9SDavid du Colombier }
2113906943f9SDavid du Colombier 
2114906943f9SDavid du Colombier static void
epclose(Ep * ep)2115906943f9SDavid du Colombier epclose(Ep *ep)
2116906943f9SDavid du Colombier {
2117906943f9SDavid du Colombier 	Ctlio *cio;
2118906943f9SDavid du Colombier 	Isoio *iso;
2119906943f9SDavid du Colombier 	Qio *io;
2120906943f9SDavid du Colombier 
2121906943f9SDavid du Colombier 	deprint("ohci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
2122906943f9SDavid du Colombier 	if(ep->aux == nil)
2123906943f9SDavid du Colombier 		panic("ohci: epclose called with closed ep");
2124906943f9SDavid du Colombier 	switch(ep->ttype){
2125906943f9SDavid du Colombier 	case Tctl:
2126906943f9SDavid du Colombier 		cio = ep->aux;
2127906943f9SDavid du Colombier 		cancelio(ep, cio);
2128906943f9SDavid du Colombier 		free(cio->data);
2129906943f9SDavid du Colombier 		cio->data = nil;
2130906943f9SDavid du Colombier 		break;
2131906943f9SDavid du Colombier 	case Tbulk:
2132906943f9SDavid du Colombier 	case Tintr:
2133906943f9SDavid du Colombier 		io = ep->aux;
2134906943f9SDavid du Colombier 		if(ep->mode != OWRITE){
2135906943f9SDavid du Colombier 			cancelio(ep, &io[OREAD]);
2136906943f9SDavid du Colombier 			if(io[OREAD].toggle == Tddata1)
2137906943f9SDavid du Colombier 				ep->toggle[OREAD] = 1;
2138906943f9SDavid du Colombier 		}
2139906943f9SDavid du Colombier 		if(ep->mode != OREAD){
2140906943f9SDavid du Colombier 			cancelio(ep, &io[OWRITE]);
2141906943f9SDavid du Colombier 			if(io[OWRITE].toggle == Tddata1)
2142906943f9SDavid du Colombier 				ep->toggle[OWRITE] = 1;
2143906943f9SDavid du Colombier 		}
2144906943f9SDavid du Colombier 		break;
2145906943f9SDavid du Colombier 	case Tiso:
2146906943f9SDavid du Colombier 		iso = ep->aux;
2147906943f9SDavid du Colombier 		cancelio(ep, iso);
2148906943f9SDavid du Colombier 		break;
2149906943f9SDavid du Colombier 	default:
2150906943f9SDavid du Colombier 		panic("epclose: bad ttype %d", ep->ttype);
2151906943f9SDavid du Colombier 	}
2152906943f9SDavid du Colombier 
2153906943f9SDavid du Colombier 	deprint("ohci: epclose ep%d.%d: done\n", ep->dev->nb, ep->nb);
2154906943f9SDavid du Colombier 	free(ep->aux);
2155906943f9SDavid du Colombier 	ep->aux = nil;
2156906943f9SDavid du Colombier }
2157906943f9SDavid du Colombier 
2158906943f9SDavid du Colombier static int
portreset(Hci * hp,int port,int on)2159906943f9SDavid du Colombier portreset(Hci *hp, int port, int on)
2160906943f9SDavid du Colombier {
2161906943f9SDavid du Colombier 	Ctlr *ctlr;
216239dc1420SDavid du Colombier 	Ohci *ohci;
2163906943f9SDavid du Colombier 
2164906943f9SDavid du Colombier 	if(on == 0)
2165906943f9SDavid du Colombier 		return 0;
2166906943f9SDavid du Colombier 
2167906943f9SDavid du Colombier 	ctlr = hp->aux;
2168906943f9SDavid du Colombier 	qlock(&ctlr->resetl);
2169906943f9SDavid du Colombier 	if(waserror()){
2170906943f9SDavid du Colombier 		qunlock(&ctlr->resetl);
2171906943f9SDavid du Colombier 		nexterror();
2172906943f9SDavid du Colombier 	}
2173906943f9SDavid du Colombier 	ilock(ctlr);
217439dc1420SDavid du Colombier 	ohci = ctlr->ohci;
217539dc1420SDavid du Colombier 	ohci->rhportsts[port - 1] = Spp;
217639dc1420SDavid du Colombier 	if((ohci->rhportsts[port - 1] & Ccs) == 0){
217739dc1420SDavid du Colombier 		iunlock(ctlr);
217839dc1420SDavid du Colombier 		error("port not connected");
217939dc1420SDavid du Colombier 	}
218039dc1420SDavid du Colombier 	ohci->rhportsts[port - 1] = Spr;
218139dc1420SDavid du Colombier 	while((ohci->rhportsts[port - 1] & Prsc) == 0){
2182906943f9SDavid du Colombier 		iunlock(ctlr);
2183906943f9SDavid du Colombier 		dprint("ohci: portreset, wait for reset complete\n");
2184906943f9SDavid du Colombier 		ilock(ctlr);
2185906943f9SDavid du Colombier 	}
218639dc1420SDavid du Colombier 	ohci->rhportsts[port - 1] = Prsc;
2187906943f9SDavid du Colombier 	iunlock(ctlr);
2188906943f9SDavid du Colombier 	poperror();
2189906943f9SDavid du Colombier 	qunlock(&ctlr->resetl);
2190906943f9SDavid du Colombier 	return 0;
2191906943f9SDavid du Colombier }
2192906943f9SDavid du Colombier 
2193906943f9SDavid du Colombier static int
portenable(Hci * hp,int port,int on)2194906943f9SDavid du Colombier portenable(Hci *hp, int port, int on)
2195906943f9SDavid du Colombier {
2196906943f9SDavid du Colombier 	Ctlr *ctlr;
2197906943f9SDavid du Colombier 
2198906943f9SDavid du Colombier 	ctlr = hp->aux;
2199906943f9SDavid du Colombier 	dprint("ohci: %#p port %d enable=%d\n", ctlr->ohci, port, on);
2200906943f9SDavid du Colombier 	qlock(&ctlr->resetl);
2201906943f9SDavid du Colombier 	if(waserror()){
2202906943f9SDavid du Colombier 		qunlock(&ctlr->resetl);
2203906943f9SDavid du Colombier 		nexterror();
2204906943f9SDavid du Colombier 	}
2205906943f9SDavid du Colombier 	ilock(ctlr);
2206906943f9SDavid du Colombier 	if(on)
2207906943f9SDavid du Colombier 		ctlr->ohci->rhportsts[port - 1] = Spe | Spp;
2208906943f9SDavid du Colombier 	else
2209906943f9SDavid du Colombier 		ctlr->ohci->rhportsts[port - 1] = Cpe;
2210906943f9SDavid du Colombier 	iunlock(ctlr);
2211906943f9SDavid du Colombier 	tsleep(&up->sleep, return0, 0, Enabledelay);
2212906943f9SDavid du Colombier 	poperror();
2213906943f9SDavid du Colombier 	qunlock(&ctlr->resetl);
2214906943f9SDavid du Colombier 	return 0;
2215906943f9SDavid du Colombier }
2216906943f9SDavid du Colombier 
2217906943f9SDavid du Colombier static int
portstatus(Hci * hp,int port)2218906943f9SDavid du Colombier portstatus(Hci *hp, int port)
2219906943f9SDavid du Colombier {
2220906943f9SDavid du Colombier 	int v;
2221906943f9SDavid du Colombier 	Ctlr *ub;
2222906943f9SDavid du Colombier 	ulong ohcistatus;
2223906943f9SDavid du Colombier 
2224906943f9SDavid du Colombier 	/*
2225906943f9SDavid du Colombier 	 * We must return status bits as a
2226906943f9SDavid du Colombier 	 * get port status hub request would do.
2227906943f9SDavid du Colombier 	 */
2228906943f9SDavid du Colombier 	ub = hp->aux;
2229906943f9SDavid du Colombier 	ohcistatus = ub->ohci->rhportsts[port - 1];
2230906943f9SDavid du Colombier 	v = 0;
2231906943f9SDavid du Colombier 	if(ohcistatus & Ccs)
2232906943f9SDavid du Colombier 		v |= HPpresent;
2233906943f9SDavid du Colombier 	if(ohcistatus & Pes)
2234906943f9SDavid du Colombier 		v |= HPenable;
2235906943f9SDavid du Colombier 	if(ohcistatus & Pss)
2236906943f9SDavid du Colombier 		v |= HPsuspend;
2237906943f9SDavid du Colombier 	if(ohcistatus & Prs)
2238906943f9SDavid du Colombier 		v |= HPreset;
2239906943f9SDavid du Colombier 	else {
2240906943f9SDavid du Colombier 		/* port is not in reset; these potential writes are ok */
2241906943f9SDavid du Colombier 		if(ohcistatus & Csc){
2242906943f9SDavid du Colombier 			v |= HPstatuschg;
2243906943f9SDavid du Colombier 			ub->ohci->rhportsts[port - 1] = Csc;
2244906943f9SDavid du Colombier 		}
2245906943f9SDavid du Colombier 		if(ohcistatus & Pesc){
2246906943f9SDavid du Colombier 			v |= HPchange;
2247906943f9SDavid du Colombier 			ub->ohci->rhportsts[port - 1] = Pesc;
22486a5dc222SDavid du Colombier 		}
22496a5dc222SDavid du Colombier 	}
2250906943f9SDavid du Colombier 	if(ohcistatus & Lsda)
2251906943f9SDavid du Colombier 		v |= HPslow;
2252906943f9SDavid du Colombier 	if(v & (HPstatuschg|HPchange))
2253906943f9SDavid du Colombier 		ddprint("ohci port %d sts %#ulx hub sts %#x\n", port, ohcistatus, v);
2254906943f9SDavid du Colombier 	return v;
2255906943f9SDavid du Colombier }
2256906943f9SDavid du Colombier 
2257906943f9SDavid du Colombier static void
dumpohci(Ctlr * ctlr)2258906943f9SDavid du Colombier dumpohci(Ctlr *ctlr)
2259906943f9SDavid du Colombier {
2260906943f9SDavid du Colombier 	int i;
2261906943f9SDavid du Colombier 	ulong *ohci;
2262906943f9SDavid du Colombier 
2263906943f9SDavid du Colombier 	ohci = &ctlr->ohci->revision;
2264906943f9SDavid du Colombier 	print("ohci registers: \n");
2265906943f9SDavid du Colombier 	for(i = 0; i < sizeof(Ohci)/sizeof(ulong); i++)
2266906943f9SDavid du Colombier 		if(i < 3 || ohci[i] != 0)
2267906943f9SDavid du Colombier 			print("\t[%#2.2x]\t%#8.8ulx\n", i * 4, ohci[i]);
2268906943f9SDavid du Colombier 	print("\n");
2269906943f9SDavid du Colombier }
2270906943f9SDavid du Colombier 
2271906943f9SDavid du Colombier static void
init(Hci * hp)2272906943f9SDavid du Colombier init(Hci *hp)
2273906943f9SDavid du Colombier {
2274906943f9SDavid du Colombier 	Ctlr *ctlr;
2275906943f9SDavid du Colombier 	Ohci *ohci;
2276906943f9SDavid du Colombier 	int i;
227739dc1420SDavid du Colombier 	ulong ival, ctrl, fmi;
2278906943f9SDavid du Colombier 
2279906943f9SDavid du Colombier 	ctlr = hp->aux;
2280906943f9SDavid du Colombier 	dprint("ohci %#p init\n", ctlr->ohci);
2281906943f9SDavid du Colombier 	ohci = ctlr->ohci;
2282906943f9SDavid du Colombier 
2283906943f9SDavid du Colombier 	fmi =  ctlr->ohci->fminterval;
2284906943f9SDavid du Colombier 	ctlr->ohci->cmdsts = Shcr;         /* reset the block */
2285906943f9SDavid du Colombier 	while(ctlr->ohci->cmdsts & Shcr)
2286906943f9SDavid du Colombier 		delay(1);  /* wait till reset complete, Ohci says 10us max. */
2287906943f9SDavid du Colombier 	ctlr->ohci->fminterval = fmi;
2288906943f9SDavid du Colombier 
2289906943f9SDavid du Colombier 	/*
2290906943f9SDavid du Colombier 	 * now that soft reset is done we are in suspend state.
2291906943f9SDavid du Colombier 	 * Setup registers which take in suspend state
2292906943f9SDavid du Colombier 	 * (will only be here for 2ms).
2293906943f9SDavid du Colombier 	 */
2294906943f9SDavid du Colombier 
2295906943f9SDavid du Colombier 	ctlr->ohci->hcca = ptr2pa(ctlr->hcca);
2296906943f9SDavid du Colombier 	setctlhd(ctlr, nil);
2297906943f9SDavid du Colombier 	ctlr->ohci->ctlcurred = 0;
2298906943f9SDavid du Colombier 	setbulkhd(ctlr, nil);
2299906943f9SDavid du Colombier 	ctlr->ohci->bulkcurred = 0;
2300906943f9SDavid du Colombier 
2301906943f9SDavid du Colombier 	ohci->intrenable = Mie | Wdh | Ue;
2302906943f9SDavid du Colombier 	ohci->control |= Ccle | Cble | Cple | Cie | Cfsoper;
2303906943f9SDavid du Colombier 
2304906943f9SDavid du Colombier 	/* set frame after operational */
2305906943f9SDavid du Colombier 	ohci->rhdesca = Nps;	/* no power switching */
2306906943f9SDavid du Colombier 	if(ohci->rhdesca & Nps){
2307906943f9SDavid du Colombier 		dprint("ohci: ports are not power switched\n");
2308906943f9SDavid du Colombier 	}else{
2309906943f9SDavid du Colombier 		dprint("ohci: ports are power switched\n");
2310906943f9SDavid du Colombier 		ohci->rhdesca &= ~Psm;
2311906943f9SDavid du Colombier 		ohci->rhsts &= ~Lpsc;
2312906943f9SDavid du Colombier 	}
2313906943f9SDavid du Colombier 	for(i = 0; i < ctlr->nports; i++)	/* paranoia */
2314906943f9SDavid du Colombier 		ohci->rhportsts[i] = 0;		/* this has no effect */
2315906943f9SDavid du Colombier 	delay(50);
2316906943f9SDavid du Colombier 
231739dc1420SDavid du Colombier 	for(i = 0; i < ctlr->nports; i++){
231839dc1420SDavid du Colombier 		ohci->rhportsts[i] =  Spp;
231939dc1420SDavid du Colombier 		if((ohci->rhportsts[i] & Ccs) != 0)
232039dc1420SDavid du Colombier 			ohci->rhportsts[i] |= Spr;
232139dc1420SDavid du Colombier 	}
2322906943f9SDavid du Colombier 	delay(100);
2323906943f9SDavid du Colombier 
2324906943f9SDavid du Colombier 	ctrl = ohci->control;
2325906943f9SDavid du Colombier 	if((ctrl & Cfsmask) != Cfsoper){
2326906943f9SDavid du Colombier 		ctrl = (ctrl & ~Cfsmask) | Cfsoper;
2327906943f9SDavid du Colombier 		ohci->control = ctrl;
2328c8cbc0e9SDavid du Colombier 		ohci->rhsts = Lpsc;
2329906943f9SDavid du Colombier 	}
2330c8cbc0e9SDavid du Colombier 	ival = ohci->fminterval & ~(Fmaxpktmask << Fmaxpktshift);
2331c8cbc0e9SDavid du Colombier 	ohci->fminterval = ival | (5120 << Fmaxpktshift);
2332906943f9SDavid du Colombier 
2333906943f9SDavid du Colombier 	if(debug > 1)
2334906943f9SDavid du Colombier 		dumpohci(ctlr);
2335906943f9SDavid du Colombier }
2336906943f9SDavid du Colombier 
2337906943f9SDavid du Colombier static void
scanpci(void)2338906943f9SDavid du Colombier scanpci(void)
2339906943f9SDavid du Colombier {
2340906943f9SDavid du Colombier 	ulong mem;
2341906943f9SDavid du Colombier 	Ctlr *ctlr;
2342906943f9SDavid du Colombier 	Pcidev *p;
2343906943f9SDavid du Colombier 	int i;
2344906943f9SDavid du Colombier 	static int already = 0;
2345906943f9SDavid du Colombier 
2346906943f9SDavid du Colombier 	if(already)
2347906943f9SDavid du Colombier 		return;
2348906943f9SDavid du Colombier 	already = 1;
2349906943f9SDavid du Colombier 	p = nil;
2350906943f9SDavid du Colombier 	while(p = pcimatch(p, 0, 0)) {
2351906943f9SDavid du Colombier 		/*
2352906943f9SDavid du Colombier 		 * Find Ohci controllers (Programming Interface = 0x10).
2353906943f9SDavid du Colombier 		 */
2354906943f9SDavid du Colombier 		if(p->ccrb != Pcibcserial || p->ccru != Pciscusb ||
2355906943f9SDavid du Colombier 		    p->ccrp != 0x10)
2356906943f9SDavid du Colombier 			continue;
2357906943f9SDavid du Colombier 		mem = p->mem[0].bar & ~0x0F;
2358906943f9SDavid du Colombier 		dprint("ohci: %x/%x port 0x%lux size 0x%x irq %d\n",
2359906943f9SDavid du Colombier 			p->vid, p->did, mem, p->mem[0].size, p->intl);
2360906943f9SDavid du Colombier 		if(mem == 0){
2361906943f9SDavid du Colombier 			print("ohci: failed to map registers\n");
2362906943f9SDavid du Colombier 			continue;
2363906943f9SDavid du Colombier 		}
2364906943f9SDavid du Colombier 		if(p->intl == 0xFF || p->intl == 0) {
2365906943f9SDavid du Colombier 			print("ohci: no irq assigned for port %#lux\n", mem);
2366906943f9SDavid du Colombier 			continue;
2367906943f9SDavid du Colombier 		}
2368906943f9SDavid du Colombier 
236906d03f7fSDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
237006d03f7fSDavid du Colombier 		if (ctlr == nil)
237106d03f7fSDavid du Colombier 			panic("ohci: out of memory");
2372906943f9SDavid du Colombier 		ctlr->pcidev = p;
2373906943f9SDavid du Colombier 		ctlr->ohci = vmap(mem, p->mem[0].size);
2374906943f9SDavid du Colombier 		dprint("scanpci: ctlr %#p, ohci %#p\n", ctlr, ctlr->ohci);
2375906943f9SDavid du Colombier 		pcisetbme(p);
2376906943f9SDavid du Colombier 		pcisetpms(p, 0);
2377906943f9SDavid du Colombier 		for(i = 0; i < Nhcis; i++)
2378906943f9SDavid du Colombier 			if(ctlrs[i] == nil){
2379906943f9SDavid du Colombier 				ctlrs[i] = ctlr;
2380906943f9SDavid du Colombier 				break;
2381906943f9SDavid du Colombier 			}
2382906943f9SDavid du Colombier 		if(i == Nhcis)
2383906943f9SDavid du Colombier 			print("ohci: bug: no more controllers\n");
2384906943f9SDavid du Colombier 	}
2385906943f9SDavid du Colombier }
2386906943f9SDavid du Colombier 
2387906943f9SDavid du Colombier static void
usbdebug(Hci *,int d)2388906943f9SDavid du Colombier usbdebug(Hci*, int d)
2389906943f9SDavid du Colombier {
2390906943f9SDavid du Colombier 	debug = d;
23916a5dc222SDavid du Colombier }
23926a5dc222SDavid du Colombier 
23936a5dc222SDavid du Colombier /*
23946a5dc222SDavid du Colombier  * build the periodic scheduling tree:
23956a5dc222SDavid du Colombier  * framesize must be a multiple of the tree size
23966a5dc222SDavid du Colombier  */
2397906943f9SDavid du Colombier static void
mkqhtree(Ctlr * ctlr)2398906943f9SDavid du Colombier mkqhtree(Ctlr *ctlr)
23996a5dc222SDavid du Colombier {
24006a5dc222SDavid du Colombier 	int i, n, d, o, leaf0, depth;
2401906943f9SDavid du Colombier 	Ed **tree;
2402906943f9SDavid du Colombier 	Qtree *qt;
24036a5dc222SDavid du Colombier 
24046a5dc222SDavid du Colombier 	depth = flog2(32);
24056a5dc222SDavid du Colombier 	n = (1 << (depth+1)) - 1;
24066a5dc222SDavid du Colombier 	qt = mallocz(sizeof(*qt), 1);
24076a5dc222SDavid du Colombier 	if(qt == nil)
2408906943f9SDavid du Colombier 		panic("usb: can't allocate scheduling tree");
24096a5dc222SDavid du Colombier 	qt->nel = n;
24106a5dc222SDavid du Colombier 	qt->depth = depth;
24116a5dc222SDavid du Colombier 	qt->bw = mallocz(n * sizeof(qt->bw), 1);
2412906943f9SDavid du Colombier 	qt->root = tree = mallocz(n * sizeof(Ed *), 1);
2413906943f9SDavid du Colombier 	if(qt->bw == nil || qt->root == nil)
2414906943f9SDavid du Colombier 		panic("usb: can't allocate scheduling tree");
2415906943f9SDavid du Colombier 	for(i = 0; i < n; i++){
2416906943f9SDavid du Colombier 		if((tree[i] = edalloc()) == nil)
2417906943f9SDavid du Colombier 			panic("mkqhtree");
2418906943f9SDavid du Colombier 		tree[i]->ctrl = (8 << Edmpsshift);	/* not needed */
2419906943f9SDavid du Colombier 		tree[i]->ctrl |= Edskip;
2420906943f9SDavid du Colombier 
2421906943f9SDavid du Colombier 		if(i > 0)
2422906943f9SDavid du Colombier 			edlinked(tree[i], tree[(i-1)/2]);
2423906943f9SDavid du Colombier 		else
2424906943f9SDavid du Colombier 			edlinked(tree[i], nil);
24256a5dc222SDavid du Colombier 	}
2426906943f9SDavid du Colombier 	ctlr->ntree = i;
2427906943f9SDavid du Colombier 	dprint("ohci: tree: %d endpoints allocated\n", i);
24286a5dc222SDavid du Colombier 
24296a5dc222SDavid du Colombier 	/* distribute leaves evenly round the frame list */
24306a5dc222SDavid du Colombier 	leaf0 = n / 2;
24316a5dc222SDavid du Colombier 	for(i = 0; i < 32; i++){
24326a5dc222SDavid du Colombier 		o = 0;
24336a5dc222SDavid du Colombier 		for(d = 0; d < depth; d++){
24346a5dc222SDavid du Colombier 			o <<= 1;
24356a5dc222SDavid du Colombier 			if(i & (1 << d))
24366a5dc222SDavid du Colombier 				o |= 1;
24376a5dc222SDavid du Colombier 		}
24386a5dc222SDavid du Colombier 		if(leaf0 + o >= n){
24396a5dc222SDavid du Colombier 			print("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n);
24406a5dc222SDavid du Colombier 			break;
24416a5dc222SDavid du Colombier 		}
2442906943f9SDavid du Colombier 		ctlr->hcca->intrtable[i] = ptr2pa(tree[leaf0 + o]);
24436a5dc222SDavid du Colombier 	}
2444906943f9SDavid du Colombier 	ctlr->tree = qt;
24456a5dc222SDavid du Colombier }
24466a5dc222SDavid du Colombier 
24476a5dc222SDavid du Colombier static void
ohcimeminit(Ctlr * ctlr)2448906943f9SDavid du Colombier ohcimeminit(Ctlr *ctlr)
24496a5dc222SDavid du Colombier {
2450906943f9SDavid du Colombier 	Hcca *hcca;
24516a5dc222SDavid du Colombier 
2452906943f9SDavid du Colombier 	edfree(edalloc());	/* allocate pools now */
2453906943f9SDavid du Colombier 	tdfree(tdalloc());
2454906943f9SDavid du Colombier 
2455906943f9SDavid du Colombier 	hcca = xspanalloc(sizeof(Hcca), 256, 0);
2456906943f9SDavid du Colombier 	if(hcca == nil)
2457906943f9SDavid du Colombier 		panic("usbhreset: no memory for Hcca");
2458906943f9SDavid du Colombier 	memset(hcca, 0, sizeof(*hcca));
2459906943f9SDavid du Colombier 	ctlr->hcca = hcca;
2460906943f9SDavid du Colombier 
2461906943f9SDavid du Colombier 	mkqhtree(ctlr);
24626a5dc222SDavid du Colombier }
2463906943f9SDavid du Colombier 
2464906943f9SDavid du Colombier static void
ohcireset(Ctlr * ctlr)2465906943f9SDavid du Colombier ohcireset(Ctlr *ctlr)
2466906943f9SDavid du Colombier {
24676a5dc222SDavid du Colombier 	ilock(ctlr);
2468906943f9SDavid du Colombier 	dprint("ohci %#p reset\n", ctlr->ohci);
24696a5dc222SDavid du Colombier 
24706a5dc222SDavid du Colombier 	/*
24716a5dc222SDavid du Colombier 	 * usually enter here in reset, wait till its through,
24726a5dc222SDavid du Colombier 	 * then do our own so we are on known timing conditions.
2473906943f9SDavid du Colombier 	 * Is this needed?
24746a5dc222SDavid du Colombier 	 */
24756a5dc222SDavid du Colombier 	delay(100);
2476906943f9SDavid du Colombier 	ctlr->ohci->control = 0;
2477906943f9SDavid du Colombier 	delay(100);
24786a5dc222SDavid du Colombier 
24796a5dc222SDavid du Colombier 	/* legacy support register: turn off lunacy mode */
2480906943f9SDavid du Colombier 	pcicfgw16(ctlr->pcidev, 0xc0, 0x2000);
24816a5dc222SDavid du Colombier 
2482906943f9SDavid du Colombier 	iunlock(ctlr);
24836a5dc222SDavid du Colombier }
24846a5dc222SDavid du Colombier 
2485c8cbc0e9SDavid du Colombier static void
shutdown(Hci * hp)2486c8cbc0e9SDavid du Colombier shutdown(Hci *hp)
2487c8cbc0e9SDavid du Colombier {
2488c8cbc0e9SDavid du Colombier 	Ctlr *ctlr;
2489c8cbc0e9SDavid du Colombier 
2490c8cbc0e9SDavid du Colombier 	ctlr = hp->aux;
2491c8cbc0e9SDavid du Colombier 
2492c8cbc0e9SDavid du Colombier 	ilock(ctlr);
2493*9d244d11SDavid du Colombier 	ctlr->ohci->intrdisable = Mie;
2494c8cbc0e9SDavid du Colombier 	ctlr->ohci->control = 0;
2495*9d244d11SDavid du Colombier 	coherence();
2496c8cbc0e9SDavid du Colombier 	delay(100);
2497c8cbc0e9SDavid du Colombier 	iunlock(ctlr);
2498c8cbc0e9SDavid du Colombier }
2499c8cbc0e9SDavid du Colombier 
2500906943f9SDavid du Colombier static int
reset(Hci * hp)2501906943f9SDavid du Colombier reset(Hci *hp)
2502906943f9SDavid du Colombier {
2503906943f9SDavid du Colombier 	int i;
2504906943f9SDavid du Colombier 	Ctlr *ctlr;
2505906943f9SDavid du Colombier 	Pcidev *p;
250639dc1420SDavid du Colombier 	static Lock resetlck;
25076a5dc222SDavid du Colombier 
2508906943f9SDavid du Colombier 	if(getconf("*nousbohci"))
25096a5dc222SDavid du Colombier 		return -1;
2510906943f9SDavid du Colombier 	ilock(&resetlck);
2511906943f9SDavid du Colombier 	scanpci();
25126a5dc222SDavid du Colombier 
25136a5dc222SDavid du Colombier 	/*
2514906943f9SDavid du Colombier 	 * Any adapter matches if no hp->port is supplied,
2515906943f9SDavid du Colombier 	 * otherwise the ports must match.
25166a5dc222SDavid du Colombier 	 */
2517906943f9SDavid du Colombier 	ctlr = nil;
2518906943f9SDavid du Colombier 	for(i = 0; i < Nhcis && ctlrs[i] != nil; i++){
2519906943f9SDavid du Colombier 		ctlr = ctlrs[i];
2520906943f9SDavid du Colombier 		if(ctlr->active == 0)
2521906943f9SDavid du Colombier 		if(hp->port == 0 || hp->port == (uintptr)ctlr->ohci){
2522906943f9SDavid du Colombier 			ctlr->active = 1;
2523906943f9SDavid du Colombier 			break;
25246a5dc222SDavid du Colombier 		}
2525906943f9SDavid du Colombier 	}
2526906943f9SDavid du Colombier 	iunlock(&resetlck);
2527906943f9SDavid du Colombier 	if(ctlrs[i] == nil || i == Nhcis)
2528906943f9SDavid du Colombier 		return -1;
2529906943f9SDavid du Colombier 	if(ctlr->ohci->control == ~0)
2530906943f9SDavid du Colombier 		return -1;
2531906943f9SDavid du Colombier 
25326a5dc222SDavid du Colombier 
25336a5dc222SDavid du Colombier 	p = ctlr->pcidev;
2534906943f9SDavid du Colombier 	hp->aux = ctlr;
2535906943f9SDavid du Colombier 	hp->port = (uintptr)ctlr->ohci;
2536906943f9SDavid du Colombier 	hp->irq = p->intl;
2537906943f9SDavid du Colombier 	hp->tbdf = p->tbdf;
2538906943f9SDavid du Colombier 	ctlr->nports = hp->nports = ctlr->ohci->rhdesca & 0xff;
2539906943f9SDavid du Colombier 
2540906943f9SDavid du Colombier 	ohcireset(ctlr);
2541906943f9SDavid du Colombier 	ohcimeminit(ctlr);
25426a5dc222SDavid du Colombier 
25436a5dc222SDavid du Colombier 	/*
2544906943f9SDavid du Colombier 	 * Linkage to the generic HCI driver.
25456a5dc222SDavid du Colombier 	 */
2546906943f9SDavid du Colombier 	hp->init = init;
2547906943f9SDavid du Colombier 	hp->dump = dump;
2548906943f9SDavid du Colombier 	hp->interrupt = interrupt;
2549906943f9SDavid du Colombier 	hp->epopen = epopen;
2550906943f9SDavid du Colombier 	hp->epclose = epclose;
2551906943f9SDavid du Colombier 	hp->epread = epread;
2552906943f9SDavid du Colombier 	hp->epwrite = epwrite;
2553906943f9SDavid du Colombier 	hp->seprintep = seprintep;
2554906943f9SDavid du Colombier 	hp->portenable = portenable;
2555906943f9SDavid du Colombier 	hp->portreset = portreset;
2556906943f9SDavid du Colombier 	hp->portstatus = portstatus;
2557c8cbc0e9SDavid du Colombier 	hp->shutdown = shutdown;
2558906943f9SDavid du Colombier 	hp->debug = usbdebug;
2559906943f9SDavid du Colombier 	hp->type = "ohci";
25606a5dc222SDavid du Colombier 	return 0;
25616a5dc222SDavid du Colombier }
25626a5dc222SDavid du Colombier 
25636a5dc222SDavid du Colombier void
usbohcilink(void)25646a5dc222SDavid du Colombier usbohcilink(void)
25656a5dc222SDavid du Colombier {
2566906943f9SDavid du Colombier 	addhcitype("ohci", reset);
25676a5dc222SDavid du Colombier }
2568