xref: /plan9-contrib/sys/src/9/port/usbehci.c (revision a587111c8770e522e3667ff2b63cba8a77811dd9)
184860c5dSDavid du Colombier /*
284860c5dSDavid du Colombier  * USB Enhanced Host Controller Interface (EHCI) driver
384860c5dSDavid du Colombier  * High speed USB 2.0.
484860c5dSDavid du Colombier  *
584860c5dSDavid du Colombier  * Note that all of our unlock routines call coherence.
684860c5dSDavid du Colombier  *
784860c5dSDavid du Colombier  * BUGS:
884860c5dSDavid du Colombier  * - Too many delays and ilocks.
984860c5dSDavid du Colombier  * - bandwidth admission control must be done per-frame.
1084860c5dSDavid du Colombier  * - requires polling (some controllers miss interrupts).
1184860c5dSDavid du Colombier  * - must warn of power overruns.
1284860c5dSDavid du Colombier  */
1384860c5dSDavid du Colombier 
1484860c5dSDavid du Colombier #include	"u.h"
1584860c5dSDavid du Colombier #include	"../port/lib.h"
1684860c5dSDavid du Colombier #include	"mem.h"
1784860c5dSDavid du Colombier #include	"dat.h"
1884860c5dSDavid du Colombier #include	"fns.h"
1984860c5dSDavid du Colombier #include	"io.h"
2084860c5dSDavid du Colombier #include	"../port/error.h"
2184860c5dSDavid du Colombier #include	"../port/usb.h"
22*5bdeb9a2SDavid du Colombier #include	"../port/portusbehci.h"
2384860c5dSDavid du Colombier #include	"usbehci.h"
2484860c5dSDavid du Colombier #include	"uncached.h"
2584860c5dSDavid du Colombier 
2684860c5dSDavid du Colombier #define diprint		if(ehcidebug || iso->debug)print
2784860c5dSDavid du Colombier #define ddiprint	if(ehcidebug>1 || iso->debug>1)print
2884860c5dSDavid du Colombier #define dqprint		if(ehcidebug || (qh->io && qh->io->debug))print
2984860c5dSDavid du Colombier #define ddqprint	if(ehcidebug>1 || (qh->io && qh->io->debug>1))print
3084860c5dSDavid du Colombier 
3184860c5dSDavid du Colombier #define TRUNC(x, sz)	((x) & ((sz)-1))
3284860c5dSDavid du Colombier #define LPTR(q)		((ulong*)KADDR((q) & ~0x1F))
3384860c5dSDavid du Colombier 
3484860c5dSDavid du Colombier typedef struct Ctlio Ctlio;
3584860c5dSDavid du Colombier typedef union Ed Ed;
3684860c5dSDavid du Colombier typedef struct Edpool Edpool;
3784860c5dSDavid du Colombier typedef struct Itd Itd;
3884860c5dSDavid du Colombier typedef struct Qio Qio;
3984860c5dSDavid du Colombier typedef struct Qtd Qtd;
4084860c5dSDavid du Colombier typedef struct Sitd Sitd;
4184860c5dSDavid du Colombier typedef struct Td Td;
4284860c5dSDavid du Colombier 
4384860c5dSDavid du Colombier /*
4484860c5dSDavid du Colombier  * EHCI interface registers and bits
4584860c5dSDavid du Colombier  */
4684860c5dSDavid du Colombier enum
4784860c5dSDavid du Colombier {
4884860c5dSDavid du Colombier 	/* Queue states (software) */
4984860c5dSDavid du Colombier 	Qidle		= 0,
5084860c5dSDavid du Colombier 	Qinstall,
5184860c5dSDavid du Colombier 	Qrun,
5284860c5dSDavid du Colombier 	Qdone,
5384860c5dSDavid du Colombier 	Qclose,
5484860c5dSDavid du Colombier 	Qfree,
5584860c5dSDavid du Colombier 
5684860c5dSDavid du Colombier 	Enabledelay	= 100,		/* waiting for a port to enable */
5784860c5dSDavid du Colombier 	Abortdelay	= 5,		/* delay after cancelling Tds (ms) */
5884860c5dSDavid du Colombier 
5984860c5dSDavid du Colombier 	Incr		= 64,		/* for pools of Tds, Qhs, etc. */
6084860c5dSDavid du Colombier 	Align		= 128,		/* in bytes for all those descriptors */
6184860c5dSDavid du Colombier 
6284860c5dSDavid du Colombier 	/* Keep them as a power of 2, lower than ctlr->nframes */
6384860c5dSDavid du Colombier 	/* Also, keep Nisoframes >= Nintrleafs */
6484860c5dSDavid du Colombier 	Nintrleafs	= 32,		/* nb. of leaf frames in intr. tree */
6584860c5dSDavid du Colombier 	Nisoframes	= 64,		/* nb. of iso frames (in window) */
6684860c5dSDavid du Colombier 
6784860c5dSDavid du Colombier 	/*
6884860c5dSDavid du Colombier 	 * HW constants
6984860c5dSDavid du Colombier 	 */
7084860c5dSDavid du Colombier 
7184860c5dSDavid du Colombier 	/* Itd bits (csw[]) */
7284860c5dSDavid du Colombier 	Itdactive	= 0x80000000,	/* execution enabled */
7384860c5dSDavid du Colombier 	Itddberr	= 0x40000000,	/* data buffer error */
7484860c5dSDavid du Colombier 	Itdbabble	= 0x20000000,	/* babble error */
7584860c5dSDavid du Colombier 	Itdtrerr	= 0x10000000,	/* transaction error */
7684860c5dSDavid du Colombier 	Itdlenshift	= 16,		/* transaction length */
7784860c5dSDavid du Colombier 	Itdlenmask	= 0xFFF,
7884860c5dSDavid du Colombier 	Itdioc		= 0x00008000,	/* interrupt on complete */
7984860c5dSDavid du Colombier 	Itdpgshift	= 12,		/* page select field */
8084860c5dSDavid du Colombier 	Itdoffshift	= 0,		/* transaction offset */
8184860c5dSDavid du Colombier 	/* Itd bits, buffer[] */
8284860c5dSDavid du Colombier 	Itdepshift	= 8,		/* endpoint address (buffer[0]) */
8384860c5dSDavid du Colombier 	Itddevshift	= 0,		/* device address (buffer[0]) */
8484860c5dSDavid du Colombier 	Itdin		= 0x800,	/* is input (buffer[1]) */
8584860c5dSDavid du Colombier 	Itdout		= 0,
8684860c5dSDavid du Colombier 	Itdmaxpktshift	= 0,		/* max packet (buffer[1]) */
8784860c5dSDavid du Colombier 	Itdntdsshift	= 0,		/* nb. of tds per µframe (buffer[2]) */
8884860c5dSDavid du Colombier 
8984860c5dSDavid du Colombier 	Itderrors	= Itddberr|Itdbabble|Itdtrerr,
9084860c5dSDavid du Colombier 
9184860c5dSDavid du Colombier 	/* Sitd bits (epc) */
9284860c5dSDavid du Colombier 	Stdin		= 0x80000000,	/* input direction */
9384860c5dSDavid du Colombier 	Stdportshift	= 24,		/* hub port number */
9484860c5dSDavid du Colombier 	Stdhubshift	= 16,		/* hub address */
9584860c5dSDavid du Colombier 	Stdepshift	= 8,		/* endpoint address */
9684860c5dSDavid du Colombier 	Stddevshift	= 0,		/* device address */
9784860c5dSDavid du Colombier 	/* Sitd bits (mfs) */
9884860c5dSDavid du Colombier 	Stdssmshift	= 0,		/* split start mask */
9984860c5dSDavid du Colombier 	Stdscmshift	= 8,		/* split complete mask */
10084860c5dSDavid du Colombier 	/* Sitd bits (csw) */
10184860c5dSDavid du Colombier 	Stdioc		= 0x80000000,	/* interrupt on complete */
10284860c5dSDavid du Colombier 	Stdpg		= 0x40000000,	/* page select */
10384860c5dSDavid du Colombier 	Stdlenshift	= 16,		/* total bytes to transfer */
10484860c5dSDavid du Colombier 	Stdlenmask	= 0x3FF,
10584860c5dSDavid du Colombier 	Stdactive	= 0x00000080,	/* active */
10684860c5dSDavid du Colombier 	Stderr		= 0x00000040,	/* tr. translator error */
10784860c5dSDavid du Colombier 	Stddberr	= 0x00000020,	/* data buffer error */
10884860c5dSDavid du Colombier 	Stdbabble	= 0x00000010,	/* babble error */
10984860c5dSDavid du Colombier 	Stdtrerr	= 0x00000008,	/* transaction error */
11084860c5dSDavid du Colombier 	Stdmmf		= 0x00000004,	/* missed µframe */
11184860c5dSDavid du Colombier 	Stddcs		= 0x00000002,	/* do complete split */
11284860c5dSDavid du Colombier 
11384860c5dSDavid du Colombier 	Stderrors	= Stderr|Stddberr|Stdbabble|Stdtrerr|Stdmmf,
11484860c5dSDavid du Colombier 
11584860c5dSDavid du Colombier 	/* Sitd bits buffer[1] */
11684860c5dSDavid du Colombier 	Stdtpall	= 0x00000000,	/* all payload here (188 bytes) */
11784860c5dSDavid du Colombier 	Stdtpbegin	= 0x00000008,	/* first payload for fs trans. */
11884860c5dSDavid du Colombier 	Stdtcntmask	= 0x00000007,	/* T-count */
11984860c5dSDavid du Colombier 
12084860c5dSDavid du Colombier 	/* Td bits (csw) */
12184860c5dSDavid du Colombier 	Tddata1		= 0x80000000,	/* data toggle 1 */
12284860c5dSDavid du Colombier 	Tddata0		= 0x00000000,	/* data toggle 0 */
12384860c5dSDavid du Colombier 	Tdlenshift	= 16,		/* total bytes to transfer */
12484860c5dSDavid du Colombier 	Tdlenmask	= 0x7FFF,
12584860c5dSDavid du Colombier 	Tdmaxpkt	= 0x5000,	/* max buffer for a Td */
12684860c5dSDavid du Colombier 	Tdioc		= 0x00008000,	/* interrupt on complete */
12784860c5dSDavid du Colombier 	Tdpgshift	= 12,		/* current page */
12884860c5dSDavid du Colombier 	Tdpgmask	= 7,
12984860c5dSDavid du Colombier 	Tderr1		= 0x00000400,	/* bit 0 of error counter */
13084860c5dSDavid du Colombier 	Tderr2		= 0x00000800,	/* bit 1 of error counter */
13184860c5dSDavid du Colombier 	Tdtokout	= 0x00000000,	/* direction out */
13284860c5dSDavid du Colombier 	Tdtokin		= 0x00000100,	/* direction in */
13384860c5dSDavid du Colombier 	Tdtoksetup	= 0x00000200,	/* setup packet */
13484860c5dSDavid du Colombier 	Tdtok		= 0x00000300,	/* token bits */
13584860c5dSDavid du Colombier 	Tdactive		= 0x00000080,	/* active */
13684860c5dSDavid du Colombier 	Tdhalt		= 0x00000040,	/* halted */
13784860c5dSDavid du Colombier 	Tddberr		= 0x00000020,	/* data buffer error */
13884860c5dSDavid du Colombier 	Tdbabble	= 0x00000010,	/* babble error */
13984860c5dSDavid du Colombier 	Tdtrerr		= 0x00000008,	/* transaction error */
14084860c5dSDavid du Colombier 	Tdmmf		= 0x00000004,	/* missed µframe */
14184860c5dSDavid du Colombier 	Tddcs		= 0x00000002,	/* do complete split */
14284860c5dSDavid du Colombier 	Tdping		= 0x00000001,	/* do ping */
14384860c5dSDavid du Colombier 
14484860c5dSDavid du Colombier 	Tderrors	= Tdhalt|Tddberr|Tdbabble|Tdtrerr|Tdmmf,
14584860c5dSDavid du Colombier 
14684860c5dSDavid du Colombier 	/* Qh bits (eps0) */
14784860c5dSDavid du Colombier 	Qhrlcmask	= 0xF,		/* nak reload count */
14884860c5dSDavid du Colombier 	Qhrlcshift	= 28,		/* nak reload count */
14984860c5dSDavid du Colombier 	Qhnhctl		= 0x08000000,	/* not-high speed ctl */
15084860c5dSDavid du Colombier 	Qhmplmask	= 0x7FF,	/* max packet */
15184860c5dSDavid du Colombier 	Qhmplshift	= 16,
15284860c5dSDavid du Colombier 	Qhhrl		= 0x00008000,	/* head of reclamation list */
15384860c5dSDavid du Colombier 	Qhdtc		= 0x00004000,	/* data toggle ctl. */
15484860c5dSDavid du Colombier 	Qhint		= 0x00000080,	/* inactivate on next transition */
15584860c5dSDavid du Colombier 	Qhspeedmask	= 0x00003000,	/* speed bits */
15684860c5dSDavid du Colombier 	Qhfull		= 0x00000000,	/* full speed */
15784860c5dSDavid du Colombier 	Qhlow		= 0x00001000,	/* low speed */
15884860c5dSDavid du Colombier 	Qhhigh		= 0x00002000,	/* high speed */
15984860c5dSDavid du Colombier 
16084860c5dSDavid du Colombier 	/* Qh bits (eps1) */
16184860c5dSDavid du Colombier 	Qhmultshift	= 30,		/* multiple tds per µframe */
16284860c5dSDavid du Colombier 	Qhmultmask	= 3,
16384860c5dSDavid du Colombier 	Qhportshift	= 23,		/* hub port number */
16484860c5dSDavid du Colombier 	Qhhubshift	= 16,		/* hub address */
16584860c5dSDavid du Colombier 	Qhscmshift	= 8,		/* split completion mask bits */
16684860c5dSDavid du Colombier 	Qhismshift	= 0,		/* interrupt sched. mask bits */
16784860c5dSDavid du Colombier };
16884860c5dSDavid du Colombier 
16984860c5dSDavid du Colombier /*
17084860c5dSDavid du Colombier  * Endpoint tree (software)
17184860c5dSDavid du Colombier  */
17284860c5dSDavid du Colombier struct Qtree
17384860c5dSDavid du Colombier {
17484860c5dSDavid du Colombier 	int	nel;
17584860c5dSDavid du Colombier 	int	depth;
17684860c5dSDavid du Colombier 	ulong*	bw;
17784860c5dSDavid du Colombier 	Qh**	root;
17884860c5dSDavid du Colombier };
17984860c5dSDavid du Colombier 
18084860c5dSDavid du Colombier /*
18184860c5dSDavid du Colombier  * One per endpoint per direction, to control I/O.
18284860c5dSDavid du Colombier  */
18384860c5dSDavid du Colombier struct Qio
18484860c5dSDavid du Colombier {
18584860c5dSDavid du Colombier 	QLock;			/* for the entire I/O process */
18684860c5dSDavid du Colombier 	Rendez;			/* wait for completion */
18784860c5dSDavid du Colombier 	Qh*	qh;		/* Td list (field const after init) */
18884860c5dSDavid du Colombier 	int	usbid;		/* usb address for endpoint/device */
18984860c5dSDavid du Colombier 	int	toggle;		/* Tddata0/Tddata1 */
19084860c5dSDavid du Colombier 	int	tok;		/* Tdtoksetup, Tdtokin, Tdtokout */
19184860c5dSDavid du Colombier 	ulong	iotime;		/* last I/O time; to hold interrupt polls */
19284860c5dSDavid du Colombier 	int	debug;		/* debug flag from the endpoint */
19384860c5dSDavid du Colombier 	char*	err;		/* error string */
19484860c5dSDavid du Colombier 	char*	tag;		/* debug (no room in Qh for this) */
19584860c5dSDavid du Colombier 	ulong	bw;
19684860c5dSDavid du Colombier };
19784860c5dSDavid du Colombier 
19884860c5dSDavid du Colombier struct Ctlio
19984860c5dSDavid du Colombier {
20084860c5dSDavid du Colombier 	Qio;			/* a single Qio for each RPC */
20184860c5dSDavid du Colombier 	uchar*	data;		/* read from last ctl req. */
20284860c5dSDavid du Colombier 	int	ndata;		/* number of bytes read */
20384860c5dSDavid du Colombier };
20484860c5dSDavid du Colombier 
20584860c5dSDavid du Colombier struct Isoio
20684860c5dSDavid du Colombier {
20784860c5dSDavid du Colombier 	QLock;
20884860c5dSDavid du Colombier 	Rendez;			/* wait for space/completion/errors */
20984860c5dSDavid du Colombier 	int	usbid;		/* address used for device/endpoint */
21084860c5dSDavid du Colombier 	int	tok;		/* Tdtokin or Tdtokout */
21184860c5dSDavid du Colombier 	int	state;		/* Qrun -> Qdone -> Qrun... -> Qclose */
21284860c5dSDavid du Colombier 	int	nframes;	/* number of frames ([S]Itds) used */
21384860c5dSDavid du Colombier 	uchar*	data;		/* iso data buffers if not embedded */
21484860c5dSDavid du Colombier 	char*	err;		/* error string */
21584860c5dSDavid du Colombier 	int	nerrs;		/* nb of consecutive I/O errors */
21684860c5dSDavid du Colombier 	ulong	maxsize;	/* ntds * ep->maxpkt */
21784860c5dSDavid du Colombier 	long	nleft;		/* number of bytes left from last write */
21884860c5dSDavid du Colombier 	int	debug;		/* debug flag from the endpoint */
21984860c5dSDavid du Colombier 	int	hs;		/* is high speed? */
22084860c5dSDavid du Colombier 	Isoio*	next;		/* in list of active Isoios */
22184860c5dSDavid du Colombier 	ulong	td0frno;	/* first frame used in ctlr */
22284860c5dSDavid du Colombier 	union{
22384860c5dSDavid du Colombier 		Itd*	tdi;	/* next td processed by interrupt */
22484860c5dSDavid du Colombier 		Sitd*	stdi;
22584860c5dSDavid du Colombier 	};
22684860c5dSDavid du Colombier 	union{
22784860c5dSDavid du Colombier 		Itd*	tdu;	/* next td for user I/O in tdps */
22884860c5dSDavid du Colombier 		Sitd*	stdu;
22984860c5dSDavid du Colombier 	};
23084860c5dSDavid du Colombier 	union{
23184860c5dSDavid du Colombier 		Itd**	itdps;	/* itdps[i]: ptr to Itd for i-th frame or nil */
23284860c5dSDavid du Colombier 		Sitd**	sitdps;	/* sitdps[i]: ptr to Sitd for i-th frame or nil */
23384860c5dSDavid du Colombier 		ulong**	tdps;	/* same thing, as seen by hw */
23484860c5dSDavid du Colombier 	};
23584860c5dSDavid du Colombier };
23684860c5dSDavid du Colombier 
23784860c5dSDavid du Colombier struct Edpool
23884860c5dSDavid du Colombier {
23984860c5dSDavid du Colombier 	Lock;
24084860c5dSDavid du Colombier 	Ed*	free;
24184860c5dSDavid du Colombier 	int	nalloc;
24284860c5dSDavid du Colombier 	int	ninuse;
24384860c5dSDavid du Colombier 	int	nfree;
24484860c5dSDavid du Colombier };
24584860c5dSDavid du Colombier 
24684860c5dSDavid du Colombier /*
24784860c5dSDavid du Colombier  * We use the 64-bit version for Itd, Sitd, Td, and Qh.
24884860c5dSDavid du Colombier  * If the ehci is 64-bit capable it assumes we are using those
24984860c5dSDavid du Colombier  * structures even when the system is 32 bits.
25084860c5dSDavid du Colombier  */
25184860c5dSDavid du Colombier 
25284860c5dSDavid du Colombier /*
25384860c5dSDavid du Colombier  * Iso transfer descriptor.  hw: 92 bytes, 108 bytes total
25484860c5dSDavid du Colombier  * aligned to 32.
25584860c5dSDavid du Colombier  */
25684860c5dSDavid du Colombier struct Itd
25784860c5dSDavid du Colombier {
25884860c5dSDavid du Colombier 	ulong	link;		/* to next hw struct */
25984860c5dSDavid du Colombier 	ulong	csw[8];		/* sts/length/pg/off. updated by hw */
26084860c5dSDavid du Colombier 	ulong	buffer[7];	/* buffer pointers, addrs, maxsz */
26184860c5dSDavid du Colombier 	ulong	xbuffer[7];	/* high 32 bits of buffer for 64-bits */
26284860c5dSDavid du Colombier 
26384860c5dSDavid du Colombier 	ulong	_pad0;		/* pad to next cache line */
26484860c5dSDavid du Colombier 	/* cache-line boundary here */
26584860c5dSDavid du Colombier 
26684860c5dSDavid du Colombier 	/* software */
26784860c5dSDavid du Colombier 	Itd*	next;
26884860c5dSDavid du Colombier 	ulong	ndata;		/* number of bytes in data */
26984860c5dSDavid du Colombier 	ulong	mdata;		/* max number of bytes in data */
27084860c5dSDavid du Colombier 	uchar*	data;
27184860c5dSDavid du Colombier };
27284860c5dSDavid du Colombier 
27384860c5dSDavid du Colombier /*
27484860c5dSDavid du Colombier  * Split transaction iso transfer descriptor.
27584860c5dSDavid du Colombier  * hw: 36 bytes, 52 bytes total. aligned to 32.
27684860c5dSDavid du Colombier  */
27784860c5dSDavid du Colombier struct Sitd
27884860c5dSDavid du Colombier {
27984860c5dSDavid du Colombier 	ulong	link;		/* to next hw struct */
28084860c5dSDavid du Colombier 	ulong	epc;		/* static endpoint state. addrs */
28184860c5dSDavid du Colombier 	ulong	mfs;		/* static endpoint state. µ-frame sched. */
28284860c5dSDavid du Colombier 	ulong	csw;		/* transfer state. updated by hw */
28384860c5dSDavid du Colombier 	ulong	buffer[2];	/* buf. ptr/offset. offset updated by hw */
28484860c5dSDavid du Colombier 				/* buf ptr/TP/Tcnt. TP/Tcnt updated by hw */
28584860c5dSDavid du Colombier 	ulong	blink;		/* back pointer */
28684860c5dSDavid du Colombier 	/* cache-line boundary after xbuffer[0] */
28784860c5dSDavid du Colombier 	ulong	xbuffer[2];	/* high 32 bits of buffer for 64-bits */
28884860c5dSDavid du Colombier 
28984860c5dSDavid du Colombier 	/* software */
29084860c5dSDavid du Colombier 	Sitd*	next;
29184860c5dSDavid du Colombier 	ulong	ndata;		/* number of bytes in data */
29284860c5dSDavid du Colombier 	ulong	mdata;		/* max number of bytes in data */
29384860c5dSDavid du Colombier 	uchar*	data;
29484860c5dSDavid du Colombier };
29584860c5dSDavid du Colombier 
29684860c5dSDavid du Colombier /*
29784860c5dSDavid du Colombier  * Queue element transfer descriptor.
29884860c5dSDavid du Colombier  * hw: first 52 bytes, total 68+sbuff bytes.  aligned to 32 bytes.
29984860c5dSDavid du Colombier  */
30084860c5dSDavid du Colombier struct Td
30184860c5dSDavid du Colombier {
30284860c5dSDavid du Colombier 	ulong	nlink;		/* to next Td */
30384860c5dSDavid du Colombier 	ulong	alink;		/* alternate link to next Td */
30484860c5dSDavid du Colombier 	ulong	csw;		/* cmd/sts. updated by hw */
30584860c5dSDavid du Colombier 	ulong	buffer[5];	/* buf ptrs. offset updated by hw */
30684860c5dSDavid du Colombier 	/* cache-line boundary here */
30784860c5dSDavid du Colombier 	ulong	xbuffer[5];	/* high 32 bits of buffer for 64-bits */
30884860c5dSDavid du Colombier 
30984860c5dSDavid du Colombier 	/* software */
31084860c5dSDavid du Colombier 	Td*	next;		/* in qh or Isoio or free list */
31184860c5dSDavid du Colombier 	ulong	ndata;		/* bytes available/used at data */
31284860c5dSDavid du Colombier 	uchar*	data;		/* pointer to actual data */
31384860c5dSDavid du Colombier 	uchar*	buff;		/* allocated data buffer or nil */
31484860c5dSDavid du Colombier 	uchar	sbuff[1];	/* first byte of embedded buffer */
31584860c5dSDavid du Colombier };
31684860c5dSDavid du Colombier 
31784860c5dSDavid du Colombier /*
31884860c5dSDavid du Colombier  * Queue head. Aligned to 32 bytes.
31984860c5dSDavid du Colombier  * hw: first 68 bytes, 92 total.
32084860c5dSDavid du Colombier  */
32184860c5dSDavid du Colombier struct Qh
32284860c5dSDavid du Colombier {
32384860c5dSDavid du Colombier 	ulong	link;		/* to next Qh in round robin */
32484860c5dSDavid du Colombier 	ulong	eps0;		/* static endpoint state. addrs */
32584860c5dSDavid du Colombier 	ulong	eps1;		/* static endpoint state. µ-frame sched. */
32684860c5dSDavid du Colombier 
32784860c5dSDavid du Colombier 	/* updated by hw */
32884860c5dSDavid du Colombier 	ulong	tclink;		/* current Td (No Term bit here!) */
32984860c5dSDavid du Colombier 	ulong	nlink;		/* to next Td */
33084860c5dSDavid du Colombier 	ulong	alink;		/* alternate link to next Td */
33184860c5dSDavid du Colombier 	ulong	csw;		/* cmd/sts. updated by hw */
33284860c5dSDavid du Colombier 	/* cache-line boundary after buffer[0] */
33384860c5dSDavid du Colombier 	ulong	buffer[5];	/* buf ptrs. offset updated by hw */
33484860c5dSDavid du Colombier 	ulong	xbuffer[5];	/* high 32 bits of buffer for 64-bits */
33584860c5dSDavid du Colombier 
33684860c5dSDavid du Colombier 	/* software */
33784860c5dSDavid du Colombier 	Qh*	next;		/* in controller list/tree of Qhs */
33884860c5dSDavid du Colombier 	int	state;		/* Qidle -> Qinstall -> Qrun -> Qdone | Qclose */
33984860c5dSDavid du Colombier 	Qio*	io;		/* for this queue */
34084860c5dSDavid du Colombier 	Td*	tds;		/* for this queue */
34184860c5dSDavid du Colombier 	int	sched;		/* slot for for intr. Qhs */
34284860c5dSDavid du Colombier 	Qh*	inext;		/* next in list of intr. qhs */
34384860c5dSDavid du Colombier };
34484860c5dSDavid du Colombier 
34584860c5dSDavid du Colombier /*
34684860c5dSDavid du Colombier  * We can avoid frame span traversal nodes if we don't span frames.
34784860c5dSDavid du Colombier  * Just schedule transfers that can fit on the current frame and
34884860c5dSDavid du Colombier  * wait a little bit otherwise.
34984860c5dSDavid du Colombier  */
35084860c5dSDavid du Colombier 
35184860c5dSDavid du Colombier /*
35284860c5dSDavid du Colombier  * Software. Ehci descriptors provided by pool.
35384860c5dSDavid du Colombier  * There are soo few because we avoid using Fstn.
35484860c5dSDavid du Colombier  */
35584860c5dSDavid du Colombier union Ed
35684860c5dSDavid du Colombier {
35784860c5dSDavid du Colombier 	Ed*	next;		/* in free list */
35884860c5dSDavid du Colombier 	Qh	qh;
35984860c5dSDavid du Colombier 	Td	td;
36084860c5dSDavid du Colombier 	Itd	itd;
36184860c5dSDavid du Colombier 	Sitd	sitd;
36284860c5dSDavid du Colombier 	uchar	align[Align];
36384860c5dSDavid du Colombier };
36484860c5dSDavid du Colombier 
3655a5ba3fdSDavid du Colombier int ehcidebug = 0;
36684860c5dSDavid du Colombier 
36784860c5dSDavid du Colombier static Edpool edpool;
36884860c5dSDavid du Colombier static char* qhsname[] = { "idle", "install", "run", "done", "close", "FREE" };
36984860c5dSDavid du Colombier 
37084860c5dSDavid du Colombier Ecapio* ehcidebugcapio;
37184860c5dSDavid du Colombier int ehcidebugport;
37284860c5dSDavid du Colombier 
37384860c5dSDavid du Colombier void
ehcirun(Ctlr * ctlr,int on)37484860c5dSDavid du Colombier ehcirun(Ctlr *ctlr, int on)
37584860c5dSDavid du Colombier {
37684860c5dSDavid du Colombier 	int i;
37784860c5dSDavid du Colombier 	Eopio *opio;
37884860c5dSDavid du Colombier 
37984860c5dSDavid du Colombier 	ddprint("ehci %#p %s\n", ctlr->capio, on ? "starting" : "halting");
38084860c5dSDavid du Colombier 	opio = ctlr->opio;
38184860c5dSDavid du Colombier 	if(on)
38284860c5dSDavid du Colombier 		opio->cmd |= Crun;
38384860c5dSDavid du Colombier 	else
38484860c5dSDavid du Colombier 		opio->cmd = Cstop;
38584860c5dSDavid du Colombier 	coherence();
38684860c5dSDavid du Colombier 	for(i = 0; i < 100; i++)
38784860c5dSDavid du Colombier 		if(on == 0 && (opio->sts & Shalted) != 0)
38884860c5dSDavid du Colombier 			break;
38984860c5dSDavid du Colombier 		else if(on != 0 && (opio->sts & Shalted) == 0)
39084860c5dSDavid du Colombier 			break;
39184860c5dSDavid du Colombier 		else
39284860c5dSDavid du Colombier 			delay(1);
39384860c5dSDavid du Colombier 	if(i == 100)
39484860c5dSDavid du Colombier 		print("ehci %#p %s cmd timed out\n",
39584860c5dSDavid du Colombier 			ctlr->capio, on ? "run" : "halt");
39684860c5dSDavid du Colombier 	ddprint("ehci %#p cmd %#lux sts %#lux\n",
39784860c5dSDavid du Colombier 		ctlr->capio, opio->cmd, opio->sts);
39884860c5dSDavid du Colombier }
39984860c5dSDavid du Colombier 
40084860c5dSDavid du Colombier static void*
edalloc(void)40184860c5dSDavid du Colombier edalloc(void)
40284860c5dSDavid du Colombier {
40384860c5dSDavid du Colombier 	Ed *ed, *pool;
40484860c5dSDavid du Colombier 	int i;
40584860c5dSDavid du Colombier 
40684860c5dSDavid du Colombier 	lock(&edpool);
40784860c5dSDavid du Colombier 	if(edpool.free == nil){
40884860c5dSDavid du Colombier 		pool = xspanalloc(Incr*sizeof(Ed), Align, 0);
40984860c5dSDavid du Colombier 		if(pool == nil)
41084860c5dSDavid du Colombier 			panic("edalloc");
41184860c5dSDavid du Colombier 		for(i=Incr; --i>=0;){
41284860c5dSDavid du Colombier 			pool[i].next = edpool.free;
41384860c5dSDavid du Colombier 			edpool.free = &pool[i];
41484860c5dSDavid du Colombier 		}
41584860c5dSDavid du Colombier 		edpool.nalloc += Incr;
41684860c5dSDavid du Colombier 		edpool.nfree += Incr;
41784860c5dSDavid du Colombier 		dprint("ehci: edalloc: %d eds\n", edpool.nalloc);
41884860c5dSDavid du Colombier 	}
41984860c5dSDavid du Colombier 	ed = edpool.free;
42084860c5dSDavid du Colombier 	edpool.free = ed->next;
42184860c5dSDavid du Colombier 	edpool.ninuse++;
42284860c5dSDavid du Colombier 	edpool.nfree--;
42384860c5dSDavid du Colombier 	unlock(&edpool);
42484860c5dSDavid du Colombier 
42584860c5dSDavid du Colombier 	memset(ed, 0, sizeof(Ed));	/* safety */
42684860c5dSDavid du Colombier 	assert(((ulong)ed & 0xF) == 0);
42784860c5dSDavid du Colombier 	return ed;
42884860c5dSDavid du Colombier }
42984860c5dSDavid du Colombier 
43084860c5dSDavid du Colombier static void
edfree(void * a)43184860c5dSDavid du Colombier edfree(void *a)
43284860c5dSDavid du Colombier {
43384860c5dSDavid du Colombier 	Ed *ed;
43484860c5dSDavid du Colombier 
43584860c5dSDavid du Colombier 	ed = a;
43684860c5dSDavid du Colombier 	lock(&edpool);
43784860c5dSDavid du Colombier 	ed->next = edpool.free;
43884860c5dSDavid du Colombier 	edpool.free = ed;
43984860c5dSDavid du Colombier 	edpool.ninuse--;
44084860c5dSDavid du Colombier 	edpool.nfree++;
44184860c5dSDavid du Colombier 	unlock(&edpool);
44284860c5dSDavid du Colombier }
44384860c5dSDavid du Colombier 
44484860c5dSDavid du Colombier /*
44584860c5dSDavid du Colombier  * Allocate and do some initialization.
44684860c5dSDavid du Colombier  * Free after releasing buffers used.
44784860c5dSDavid du Colombier  */
44884860c5dSDavid du Colombier 
44984860c5dSDavid du Colombier static Itd*
itdalloc(void)45084860c5dSDavid du Colombier itdalloc(void)
45184860c5dSDavid du Colombier {
45284860c5dSDavid du Colombier 	Itd *td;
45384860c5dSDavid du Colombier 
45484860c5dSDavid du Colombier 	td = edalloc();
45584860c5dSDavid du Colombier 	td->link = Lterm;
45684860c5dSDavid du Colombier 	return td;
45784860c5dSDavid du Colombier }
45884860c5dSDavid du Colombier 
45984860c5dSDavid du Colombier static void
itdfree(Itd * td)46084860c5dSDavid du Colombier itdfree(Itd *td)
46184860c5dSDavid du Colombier {
46284860c5dSDavid du Colombier 	edfree(td);
46384860c5dSDavid du Colombier }
46484860c5dSDavid du Colombier 
46584860c5dSDavid du Colombier static Sitd*
sitdalloc(void)46684860c5dSDavid du Colombier sitdalloc(void)
46784860c5dSDavid du Colombier {
46884860c5dSDavid du Colombier 	Sitd *td;
46984860c5dSDavid du Colombier 
47084860c5dSDavid du Colombier 	td = edalloc();
47184860c5dSDavid du Colombier 	td->link = td->blink = Lterm;
47284860c5dSDavid du Colombier 	return td;
47384860c5dSDavid du Colombier }
47484860c5dSDavid du Colombier 
47584860c5dSDavid du Colombier static void
sitdfree(Sitd * td)47684860c5dSDavid du Colombier sitdfree(Sitd *td)
47784860c5dSDavid du Colombier {
47884860c5dSDavid du Colombier 	edfree(td);
47984860c5dSDavid du Colombier }
48084860c5dSDavid du Colombier 
48184860c5dSDavid du Colombier static Td*
tdalloc(void)48284860c5dSDavid du Colombier tdalloc(void)
48384860c5dSDavid du Colombier {
48484860c5dSDavid du Colombier 	Td *td;
48584860c5dSDavid du Colombier 
48684860c5dSDavid du Colombier 	td = edalloc();
48784860c5dSDavid du Colombier 	td->nlink = td->alink = Lterm;
48884860c5dSDavid du Colombier 	return td;
48984860c5dSDavid du Colombier }
49084860c5dSDavid du Colombier 
49184860c5dSDavid du Colombier static void
tdfree(Td * td)49284860c5dSDavid du Colombier tdfree(Td *td)
49384860c5dSDavid du Colombier {
49484860c5dSDavid du Colombier 	if(td == nil)
49584860c5dSDavid du Colombier 		return;
49684860c5dSDavid du Colombier 	free(td->buff);
49784860c5dSDavid du Colombier 	edfree(td);
49884860c5dSDavid du Colombier }
49984860c5dSDavid du Colombier 
50084860c5dSDavid du Colombier static void
tdlinktd(Td * td,Td * next)50184860c5dSDavid du Colombier tdlinktd(Td *td, Td *next)
50284860c5dSDavid du Colombier {
50384860c5dSDavid du Colombier 	td->next = next;
50484860c5dSDavid du Colombier 	td->alink = Lterm;
50584860c5dSDavid du Colombier 	if(next == nil)
50684860c5dSDavid du Colombier 		td->nlink = Lterm;
50784860c5dSDavid du Colombier 	else
50884860c5dSDavid du Colombier 		td->nlink = PADDR(next);
50984860c5dSDavid du Colombier 	coherence();
51084860c5dSDavid du Colombier }
51184860c5dSDavid du Colombier 
51284860c5dSDavid du Colombier static Qh*
qhlinkqh(Qh * qh,Qh * next)51384860c5dSDavid du Colombier qhlinkqh(Qh *qh, Qh *next)
51484860c5dSDavid du Colombier {
51584860c5dSDavid du Colombier 	qh->next = next;
5162a782499SDavid du Colombier 	if(next == nil)
5172a782499SDavid du Colombier 		qh->link = Lterm;
5182a782499SDavid du Colombier 	else
51984860c5dSDavid du Colombier 		qh->link = PADDR(next)|Lqh;
52084860c5dSDavid du Colombier 	coherence();
52184860c5dSDavid du Colombier 	return qh;
52284860c5dSDavid du Colombier }
52384860c5dSDavid du Colombier 
52484860c5dSDavid du Colombier static void
qhsetaddr(Qh * qh,ulong addr)52584860c5dSDavid du Colombier qhsetaddr(Qh *qh, ulong addr)
52684860c5dSDavid du Colombier {
52784860c5dSDavid du Colombier 	ulong eps0;
52884860c5dSDavid du Colombier 
52984860c5dSDavid du Colombier 	eps0 = qh->eps0 & ~((Epmax<<8)|Devmax);
53084860c5dSDavid du Colombier 	qh->eps0 = eps0 | addr & Devmax | ((addr >> 7) & Epmax) << 8;
53184860c5dSDavid du Colombier 	coherence();
53284860c5dSDavid du Colombier }
53384860c5dSDavid du Colombier 
53484860c5dSDavid du Colombier /*
5352a782499SDavid du Colombier  * return largest power of 2 <= n
53684860c5dSDavid du Colombier  */
53784860c5dSDavid du Colombier static int
flog2lower(int n)53884860c5dSDavid du Colombier flog2lower(int n)
53984860c5dSDavid du Colombier {
54084860c5dSDavid du Colombier 	int i;
54184860c5dSDavid du Colombier 
54284860c5dSDavid du Colombier 	for(i = 0; (1 << (i + 1)) <= n; i++)
54384860c5dSDavid du Colombier 		;
54484860c5dSDavid du Colombier 	return i;
54584860c5dSDavid du Colombier }
54684860c5dSDavid du Colombier 
54784860c5dSDavid du Colombier static int
pickschedq(Qtree * qt,int pollival,ulong bw,ulong limit)54884860c5dSDavid du Colombier pickschedq(Qtree *qt, int pollival, ulong bw, ulong limit)
54984860c5dSDavid du Colombier {
55084860c5dSDavid du Colombier 	int i, j, d, upperb, q;
55184860c5dSDavid du Colombier 	ulong best, worst, total;
55284860c5dSDavid du Colombier 
55384860c5dSDavid du Colombier 	d = flog2lower(pollival);
55484860c5dSDavid du Colombier 	if(d > qt->depth)
55584860c5dSDavid du Colombier 		d = qt->depth;
55684860c5dSDavid du Colombier 	q = -1;
55784860c5dSDavid du Colombier 	worst = 0;
55884860c5dSDavid du Colombier 	best = ~0;
55984860c5dSDavid du Colombier 	upperb = (1 << (d+1)) - 1;
56084860c5dSDavid du Colombier 	for(i = (1 << d) - 1; i < upperb; i++){
56184860c5dSDavid du Colombier 		total = qt->bw[0];
56284860c5dSDavid du Colombier 		for(j = i; j > 0; j = (j - 1) / 2)
56384860c5dSDavid du Colombier 			total += qt->bw[j];
56484860c5dSDavid du Colombier 		if(total < best){
56584860c5dSDavid du Colombier 			best = total;
56684860c5dSDavid du Colombier 			q = i;
56784860c5dSDavid du Colombier 		}
56884860c5dSDavid du Colombier 		if(total > worst)
56984860c5dSDavid du Colombier 			worst = total;
57084860c5dSDavid du Colombier 	}
57184860c5dSDavid du Colombier 	if(worst + bw >= limit)
57284860c5dSDavid du Colombier 		return -1;
57384860c5dSDavid du Colombier 	return q;
57484860c5dSDavid du Colombier }
57584860c5dSDavid du Colombier 
57684860c5dSDavid du Colombier static int
schedq(Ctlr * ctlr,Qh * qh,int pollival)57784860c5dSDavid du Colombier schedq(Ctlr *ctlr, Qh *qh, int pollival)
57884860c5dSDavid du Colombier {
57984860c5dSDavid du Colombier 	int q;
58084860c5dSDavid du Colombier 	Qh *tqh;
58184860c5dSDavid du Colombier 	ulong bw;
58284860c5dSDavid du Colombier 
58384860c5dSDavid du Colombier 	bw = qh->io->bw;
58484860c5dSDavid du Colombier 	q = pickschedq(ctlr->tree, pollival, 0, ~0);
58584860c5dSDavid du Colombier 	ddqprint("ehci: sched %#p q %d, ival %d, bw %uld\n",
58684860c5dSDavid du Colombier 		qh->io, q, pollival, bw);
58784860c5dSDavid du Colombier 	if(q < 0){
58884860c5dSDavid du Colombier 		print("ehci: no room for ed\n");
58984860c5dSDavid du Colombier 		return -1;
59084860c5dSDavid du Colombier 	}
59184860c5dSDavid du Colombier 	ctlr->tree->bw[q] += bw;
59284860c5dSDavid du Colombier 	tqh = ctlr->tree->root[q];
59384860c5dSDavid du Colombier 	qh->sched = q;
59484860c5dSDavid du Colombier 	qhlinkqh(qh, tqh->next);
59584860c5dSDavid du Colombier 	qhlinkqh(tqh, qh);
59684860c5dSDavid du Colombier 	coherence();
59784860c5dSDavid du Colombier 	qh->inext = ctlr->intrqhs;
59884860c5dSDavid du Colombier 	ctlr->intrqhs = qh;
59984860c5dSDavid du Colombier 	coherence();
60084860c5dSDavid du Colombier 	return 0;
60184860c5dSDavid du Colombier }
60284860c5dSDavid du Colombier 
60384860c5dSDavid du Colombier static void
unschedq(Ctlr * ctlr,Qh * qh)60484860c5dSDavid du Colombier unschedq(Ctlr *ctlr, Qh *qh)
60584860c5dSDavid du Colombier {
60684860c5dSDavid du Colombier 	int q;
60784860c5dSDavid du Colombier 	Qh *prev, *this, *next;
60884860c5dSDavid du Colombier 	Qh **l;
60984860c5dSDavid du Colombier 	ulong bw;
61084860c5dSDavid du Colombier 
61184860c5dSDavid du Colombier 	bw = qh->io->bw;
61284860c5dSDavid du Colombier 	q = qh->sched;
61384860c5dSDavid du Colombier 	if(q < 0)
61484860c5dSDavid du Colombier 		return;
61584860c5dSDavid du Colombier 	ctlr->tree->bw[q] -= bw;
61684860c5dSDavid du Colombier 
61784860c5dSDavid du Colombier 	prev = ctlr->tree->root[q];
61884860c5dSDavid du Colombier 	this = prev->next;
61984860c5dSDavid du Colombier 	while(this != nil && this != qh){
62084860c5dSDavid du Colombier 		prev = this;
62184860c5dSDavid du Colombier 		this = this->next;
62284860c5dSDavid du Colombier 	}
62384860c5dSDavid du Colombier 	if(this == nil)
62484860c5dSDavid du Colombier 		print("ehci: unschedq %d: not found\n", q);
62584860c5dSDavid du Colombier 	else{
62684860c5dSDavid du Colombier 		next = this->next;
62784860c5dSDavid du Colombier 		qhlinkqh(prev, next);
62884860c5dSDavid du Colombier 	}
62984860c5dSDavid du Colombier 	for(l = &ctlr->intrqhs; *l != nil; l = &(*l)->inext)
63084860c5dSDavid du Colombier 		if(*l == qh){
63184860c5dSDavid du Colombier 			*l = (*l)->inext;
63284860c5dSDavid du Colombier 			return;
63384860c5dSDavid du Colombier 		}
63484860c5dSDavid du Colombier 	print("ehci: unschedq: qh %#p not found\n", qh);
63584860c5dSDavid du Colombier }
63684860c5dSDavid du Colombier 
63784860c5dSDavid du Colombier static ulong
qhmaxpkt(Qh * qh)63884860c5dSDavid du Colombier qhmaxpkt(Qh *qh)
63984860c5dSDavid du Colombier {
64084860c5dSDavid du Colombier 	return (qh->eps0 >> Qhmplshift) & Qhmplmask;
64184860c5dSDavid du Colombier }
64284860c5dSDavid du Colombier 
64384860c5dSDavid du Colombier static void
qhsetmaxpkt(Qh * qh,int maxpkt)64484860c5dSDavid du Colombier qhsetmaxpkt(Qh *qh, int maxpkt)
64584860c5dSDavid du Colombier {
64684860c5dSDavid du Colombier 	ulong eps0;
64784860c5dSDavid du Colombier 
64884860c5dSDavid du Colombier 	eps0 = qh->eps0 & ~(Qhmplmask << Qhmplshift);
64984860c5dSDavid du Colombier 	qh->eps0 = eps0 | (maxpkt & Qhmplmask) << Qhmplshift;
65084860c5dSDavid du Colombier 	coherence();
65184860c5dSDavid du Colombier }
65284860c5dSDavid du Colombier 
65384860c5dSDavid du Colombier /*
65484860c5dSDavid du Colombier  * Initialize the round-robin circular list of ctl/bulk Qhs
65584860c5dSDavid du Colombier  * if ep is nil. Otherwise, allocate and link a new Qh in the ctlr.
65684860c5dSDavid du Colombier  */
65784860c5dSDavid du Colombier static Qh*
qhalloc(Ctlr * ctlr,Ep * ep,Qio * io,char * tag)65884860c5dSDavid du Colombier qhalloc(Ctlr *ctlr, Ep *ep, Qio *io, char* tag)
65984860c5dSDavid du Colombier {
66084860c5dSDavid du Colombier 	Qh *qh;
66184860c5dSDavid du Colombier 	int ttype;
66284860c5dSDavid du Colombier 
66384860c5dSDavid du Colombier 	qh = edalloc();
66484860c5dSDavid du Colombier 	qh->nlink = Lterm;
66584860c5dSDavid du Colombier 	qh->alink = Lterm;
66684860c5dSDavid du Colombier 	qh->csw = Tdhalt;
66784860c5dSDavid du Colombier 	qh->state = Qidle;
66884860c5dSDavid du Colombier 	qh->sched = -1;
66984860c5dSDavid du Colombier 	qh->io = io;
67084860c5dSDavid du Colombier 	if(ep != nil){
67184860c5dSDavid du Colombier 		qh->eps0 = 0;
67284860c5dSDavid du Colombier 		qhsetmaxpkt(qh, ep->maxpkt);
67384860c5dSDavid du Colombier 		if(ep->dev->speed == Lowspeed)
67484860c5dSDavid du Colombier 			qh->eps0 |= Qhlow;
67584860c5dSDavid du Colombier 		if(ep->dev->speed == Highspeed)
67684860c5dSDavid du Colombier 			qh->eps0 |= Qhhigh;
67784860c5dSDavid du Colombier 		else if(ep->ttype == Tctl)
67884860c5dSDavid du Colombier 			qh->eps0 |= Qhnhctl;
67984860c5dSDavid du Colombier 		qh->eps0 |= Qhdtc | 8 << Qhrlcshift;	/* 8 naks max */
68084860c5dSDavid du Colombier 		coherence();
68184860c5dSDavid du Colombier 		qhsetaddr(qh, io->usbid);
68284860c5dSDavid du Colombier 		qh->eps1 = (ep->ntds & Qhmultmask) << Qhmultshift;
68384860c5dSDavid du Colombier 		qh->eps1 |= ep->dev->port << Qhportshift;
68484860c5dSDavid du Colombier 		qh->eps1 |= ep->dev->hub << Qhhubshift;
68584860c5dSDavid du Colombier 		qh->eps1 |= 034 << Qhscmshift;
68684860c5dSDavid du Colombier 		if(ep->ttype == Tintr)
68784860c5dSDavid du Colombier 			qh->eps1 |= 1 << Qhismshift;	/* intr. start µf. */
68884860c5dSDavid du Colombier 		coherence();
68984860c5dSDavid du Colombier 		if(io != nil)
69084860c5dSDavid du Colombier 			io->tag = tag;
69184860c5dSDavid du Colombier 	}
69284860c5dSDavid du Colombier 	ilock(ctlr);
69384860c5dSDavid du Colombier 	ttype = Tctl;
69484860c5dSDavid du Colombier 	if(ep != nil)
69584860c5dSDavid du Colombier 		ttype = ep->ttype;
69684860c5dSDavid du Colombier 	switch(ttype){
69784860c5dSDavid du Colombier 	case Tctl:
69884860c5dSDavid du Colombier 	case Tbulk:
69984860c5dSDavid du Colombier 		if(ctlr->qhs == nil){
70084860c5dSDavid du Colombier 			ctlr->qhs = qhlinkqh(qh, qh);
70184860c5dSDavid du Colombier 			qh->eps0 |= Qhhigh | Qhhrl;
70284860c5dSDavid du Colombier 			coherence();
70384860c5dSDavid du Colombier 			ctlr->opio->link = PADDR(qh)|Lqh;
70484860c5dSDavid du Colombier 			coherence();
70584860c5dSDavid du Colombier 		}else{
70684860c5dSDavid du Colombier 			qhlinkqh(qh, ctlr->qhs->next);
70784860c5dSDavid du Colombier 			qhlinkqh(ctlr->qhs, qh);
70884860c5dSDavid du Colombier 		}
70984860c5dSDavid du Colombier 		break;
71084860c5dSDavid du Colombier 	case Tintr:
71184860c5dSDavid du Colombier 		schedq(ctlr, qh, ep->pollival);
71284860c5dSDavid du Colombier 		break;
71384860c5dSDavid du Colombier 	default:
71484860c5dSDavid du Colombier 		print("ehci: qhalloc called for ttype != ctl/bulk\n");
71584860c5dSDavid du Colombier 	}
71684860c5dSDavid du Colombier 	iunlock(ctlr);
71784860c5dSDavid du Colombier 	return qh;
71884860c5dSDavid du Colombier }
71984860c5dSDavid du Colombier 
72084860c5dSDavid du Colombier static int
qhadvanced(void * a)72184860c5dSDavid du Colombier qhadvanced(void *a)
72284860c5dSDavid du Colombier {
72384860c5dSDavid du Colombier 	Ctlr *ctlr;
72484860c5dSDavid du Colombier 
72584860c5dSDavid du Colombier 	ctlr = a;
72684860c5dSDavid du Colombier 	return (ctlr->opio->cmd & Ciasync) == 0;
72784860c5dSDavid du Colombier }
72884860c5dSDavid du Colombier 
72984860c5dSDavid du Colombier /*
73084860c5dSDavid du Colombier  * called when a qh is removed, to be sure the hw is not
73184860c5dSDavid du Colombier  * keeping pointers into it.
73284860c5dSDavid du Colombier  */
73384860c5dSDavid du Colombier static void
qhcoherency(Ctlr * ctlr)73484860c5dSDavid du Colombier qhcoherency(Ctlr *ctlr)
73584860c5dSDavid du Colombier {
73684860c5dSDavid du Colombier 	int i;
73784860c5dSDavid du Colombier 
73884860c5dSDavid du Colombier 	qlock(&ctlr->portlck);
73984860c5dSDavid du Colombier 	ctlr->opio->cmd |= Ciasync;	/* ask for intr. on async advance */
74084860c5dSDavid du Colombier 	coherence();
74184860c5dSDavid du Colombier 	for(i = 0; i < 3 && qhadvanced(ctlr) == 0; i++)
74284860c5dSDavid du Colombier 		if(!waserror()){
74384860c5dSDavid du Colombier 			tsleep(ctlr, qhadvanced, ctlr, Abortdelay);
74484860c5dSDavid du Colombier 			poperror();
74584860c5dSDavid du Colombier 		}
74684860c5dSDavid du Colombier 	dprint("ehci: qhcoherency: doorbell %d\n", qhadvanced(ctlr));
74784860c5dSDavid du Colombier 	if(i == 3)
74884860c5dSDavid du Colombier 		print("ehci: async advance doorbell did not ring\n");
74984860c5dSDavid du Colombier 	ctlr->opio->cmd &= ~Ciasync;	/* try to clean */
75084860c5dSDavid du Colombier 	qunlock(&ctlr->portlck);
75184860c5dSDavid du Colombier }
75284860c5dSDavid du Colombier 
75384860c5dSDavid du Colombier static void
qhfree(Ctlr * ctlr,Qh * qh)75484860c5dSDavid du Colombier qhfree(Ctlr *ctlr, Qh *qh)
75584860c5dSDavid du Colombier {
75684860c5dSDavid du Colombier 	Td *td, *ltd;
75784860c5dSDavid du Colombier 	Qh *q;
75884860c5dSDavid du Colombier 
75984860c5dSDavid du Colombier 	if(qh == nil)
76084860c5dSDavid du Colombier 		return;
76184860c5dSDavid du Colombier 	ilock(ctlr);
76284860c5dSDavid du Colombier 	if(qh->sched < 0){
76384860c5dSDavid du Colombier 		for(q = ctlr->qhs; q != nil; q = q->next)
76484860c5dSDavid du Colombier 			if(q->next == qh)
76584860c5dSDavid du Colombier 				break;
76684860c5dSDavid du Colombier 		if(q == nil)
76784860c5dSDavid du Colombier 			panic("qhfree: nil q");
76884860c5dSDavid du Colombier 		q->next = qh->next;
76984860c5dSDavid du Colombier 		q->link = qh->link;
77084860c5dSDavid du Colombier 		coherence();
77184860c5dSDavid du Colombier 	}else
77284860c5dSDavid du Colombier 		unschedq(ctlr, qh);
77384860c5dSDavid du Colombier 	iunlock(ctlr);
77484860c5dSDavid du Colombier 
77584860c5dSDavid du Colombier 	qhcoherency(ctlr);
77684860c5dSDavid du Colombier 
77784860c5dSDavid du Colombier 	for(td = qh->tds; td != nil; td = ltd){
77884860c5dSDavid du Colombier 		ltd = td->next;
77984860c5dSDavid du Colombier 		tdfree(td);
78084860c5dSDavid du Colombier 	}
78184860c5dSDavid du Colombier 
78284860c5dSDavid du Colombier 	edfree(qh);
78384860c5dSDavid du Colombier }
78484860c5dSDavid du Colombier 
78584860c5dSDavid du Colombier static void
qhlinktd(Qh * qh,Td * td)78684860c5dSDavid du Colombier qhlinktd(Qh *qh, Td *td)
78784860c5dSDavid du Colombier {
78884860c5dSDavid du Colombier 	ulong csw;
78984860c5dSDavid du Colombier 	int i;
79084860c5dSDavid du Colombier 
79184860c5dSDavid du Colombier 	csw = qh->csw;
79284860c5dSDavid du Colombier 	qh->tds = td;
79384860c5dSDavid du Colombier 	if(td == nil)
79484860c5dSDavid du Colombier 		qh->csw = (csw & ~Tdactive) | Tdhalt;
79584860c5dSDavid du Colombier 	else{
79684860c5dSDavid du Colombier 		csw &= Tddata1 | Tdping;	/* save */
79784860c5dSDavid du Colombier 		qh->csw = Tdhalt;
79884860c5dSDavid du Colombier 		coherence();
79984860c5dSDavid du Colombier 		qh->tclink = 0;
80084860c5dSDavid du Colombier 		qh->alink = Lterm;
80184860c5dSDavid du Colombier 		qh->nlink = PADDR(td);
80284860c5dSDavid du Colombier 		for(i = 0; i < nelem(qh->buffer); i++)
80384860c5dSDavid du Colombier 			qh->buffer[i] = 0;
80484860c5dSDavid du Colombier 		coherence();
80584860c5dSDavid du Colombier 		qh->csw = csw & ~(Tdhalt|Tdactive);	/* activate next */
80684860c5dSDavid du Colombier 	}
80784860c5dSDavid du Colombier 	coherence();
80884860c5dSDavid du Colombier }
80984860c5dSDavid du Colombier 
81084860c5dSDavid du Colombier static char*
seprintlink(char * s,char * se,char * name,ulong l,int typed)81184860c5dSDavid du Colombier seprintlink(char *s, char *se, char *name, ulong l, int typed)
81284860c5dSDavid du Colombier {
81384860c5dSDavid du Colombier 	s = seprint(s, se, "%s %ulx", name, l);
81484860c5dSDavid du Colombier 	if((l & Lterm) != 0)
81584860c5dSDavid du Colombier 		return seprint(s, se, "T");
81684860c5dSDavid du Colombier 	if(typed == 0)
81784860c5dSDavid du Colombier 		return s;
81884860c5dSDavid du Colombier 	switch(l & (3<<1)){
81984860c5dSDavid du Colombier 	case Litd:
82084860c5dSDavid du Colombier 		return seprint(s, se, "I");
82184860c5dSDavid du Colombier 	case Lqh:
82284860c5dSDavid du Colombier 		return seprint(s, se, "Q");
82384860c5dSDavid du Colombier 	case Lsitd:
82484860c5dSDavid du Colombier 		return seprint(s, se, "S");
82584860c5dSDavid du Colombier 	default:
82684860c5dSDavid du Colombier 		return seprint(s, se, "F");
82784860c5dSDavid du Colombier 	}
82884860c5dSDavid du Colombier }
82984860c5dSDavid du Colombier 
83084860c5dSDavid du Colombier static char*
seprintitd(char * s,char * se,Itd * td)83184860c5dSDavid du Colombier seprintitd(char *s, char *se, Itd *td)
83284860c5dSDavid du Colombier {
83384860c5dSDavid du Colombier 	int i;
83484860c5dSDavid du Colombier 	ulong b0, b1;
83584860c5dSDavid du Colombier 	char flags[6];
83684860c5dSDavid du Colombier 	char *rw;
83784860c5dSDavid du Colombier 
83884860c5dSDavid du Colombier 	if(td == nil)
83984860c5dSDavid du Colombier 		return seprint(s, se, "<nil itd>\n");
84084860c5dSDavid du Colombier 	b0 = td->buffer[0];
84184860c5dSDavid du Colombier 	b1 = td->buffer[1];
84284860c5dSDavid du Colombier 
84384860c5dSDavid du Colombier 	s = seprint(s, se, "itd %#p", td);
84484860c5dSDavid du Colombier 	rw = (b1 & Itdin) ? "in" : "out";
84584860c5dSDavid du Colombier 	s = seprint(s, se, " %s ep %uld dev %uld max %uld mult %uld",
84684860c5dSDavid du Colombier 		rw, (b0>>8)&Epmax, (b0&Devmax),
84784860c5dSDavid du Colombier 		td->buffer[1] & 0x7ff, b1 & 3);
84884860c5dSDavid du Colombier 	s = seprintlink(s, se, " link", td->link, 1);
84984860c5dSDavid du Colombier 	s = seprint(s, se, "\n");
85084860c5dSDavid du Colombier 	for(i = 0; i < nelem(td->csw); i++){
85184860c5dSDavid du Colombier 		memset(flags, '-', 5);
85284860c5dSDavid du Colombier 		if((td->csw[i] & Itdactive) != 0)
85384860c5dSDavid du Colombier 			flags[0] = 'a';
85484860c5dSDavid du Colombier 		if((td->csw[i] & Itdioc) != 0)
85584860c5dSDavid du Colombier 			flags[1] = 'i';
85684860c5dSDavid du Colombier 		if((td->csw[i] & Itddberr) != 0)
85784860c5dSDavid du Colombier 			flags[2] = 'd';
85884860c5dSDavid du Colombier 		if((td->csw[i] & Itdbabble) != 0)
85984860c5dSDavid du Colombier 			flags[3] = 'b';
86084860c5dSDavid du Colombier 		if((td->csw[i] & Itdtrerr) != 0)
86184860c5dSDavid du Colombier 			flags[4] = 't';
86284860c5dSDavid du Colombier 		flags[5] = 0;
86384860c5dSDavid du Colombier 		s = seprint(s, se, "\ttd%d %s", i, flags);
86484860c5dSDavid du Colombier 		s = seprint(s, se, " len %uld", (td->csw[i] >> 16) & 0x7ff);
86584860c5dSDavid du Colombier 		s = seprint(s, se, " pg %uld", (td->csw[i] >> 12) & 0x7);
86684860c5dSDavid du Colombier 		s = seprint(s, se, " off %uld\n", td->csw[i] & 0xfff);
86784860c5dSDavid du Colombier 	}
86884860c5dSDavid du Colombier 	s = seprint(s, se, "\tbuffs:");
86984860c5dSDavid du Colombier 	for(i = 0; i < nelem(td->buffer); i++)
87084860c5dSDavid du Colombier 		s = seprint(s, se, " %#lux", td->buffer[i] >> 12);
87184860c5dSDavid du Colombier 	return seprint(s, se, "\n");
87284860c5dSDavid du Colombier }
87384860c5dSDavid du Colombier 
87484860c5dSDavid du Colombier static char*
seprintsitd(char * s,char * se,Sitd * td)87584860c5dSDavid du Colombier seprintsitd(char *s, char *se, Sitd *td)
87684860c5dSDavid du Colombier {
87784860c5dSDavid du Colombier 	char rw, pg, ss;
87884860c5dSDavid du Colombier 	char flags[8];
87984860c5dSDavid du Colombier 	static char pc[4] = { 'a', 'b', 'm', 'e' };
88084860c5dSDavid du Colombier 
88184860c5dSDavid du Colombier 	if(td == nil)
88284860c5dSDavid du Colombier 		return seprint(s, se, "<nil sitd>\n");
88384860c5dSDavid du Colombier 	s = seprint(s, se, "sitd %#p", td);
88484860c5dSDavid du Colombier 	rw = (td->epc & Stdin) ? 'r' : 'w';
88584860c5dSDavid du Colombier 	s = seprint(s, se, " %c ep %uld dev %uld",
88684860c5dSDavid du Colombier 		rw, (td->epc>>8)&0xf, td->epc&0x7f);
88784860c5dSDavid du Colombier 	s = seprint(s, se, " max %uld", (td->csw >> 16) & 0x3ff);
88884860c5dSDavid du Colombier 	s = seprint(s, se, " hub %uld", (td->epc >> 16) & 0x7f);
88984860c5dSDavid du Colombier 	s = seprint(s, se, " port %uld\n", (td->epc >> 24) & 0x7f);
89084860c5dSDavid du Colombier 	memset(flags, '-', 7);
89184860c5dSDavid du Colombier 	if((td->csw & Stdactive) != 0)
89284860c5dSDavid du Colombier 		flags[0] = 'a';
89384860c5dSDavid du Colombier 	if((td->csw & Stdioc) != 0)
89484860c5dSDavid du Colombier 		flags[1] = 'i';
89584860c5dSDavid du Colombier 	if((td->csw & Stderr) != 0)
89684860c5dSDavid du Colombier 		flags[2] = 'e';
89784860c5dSDavid du Colombier 	if((td->csw & Stddberr) != 0)
89884860c5dSDavid du Colombier 		flags[3] = 'd';
89984860c5dSDavid du Colombier 	if((td->csw & Stdbabble) != 0)
90084860c5dSDavid du Colombier 		flags[4] = 'b';
90184860c5dSDavid du Colombier 	if((td->csw & Stdtrerr) != 0)
90284860c5dSDavid du Colombier 		flags[5] = 't';
90384860c5dSDavid du Colombier 	if((td->csw & Stdmmf) != 0)
90484860c5dSDavid du Colombier 		flags[6] = 'n';
90584860c5dSDavid du Colombier 	flags[7] = 0;
90684860c5dSDavid du Colombier 	ss = (td->csw & Stddcs) ? 'c' : 's';
90784860c5dSDavid du Colombier 	pg = (td->csw & Stdpg) ? '1' : '0';
90884860c5dSDavid du Colombier 	s = seprint(s, se, "\t%s %cs pg%c", flags, ss, pg);
90984860c5dSDavid du Colombier 	s = seprint(s, se, " b0 %#lux b1 %#lux off %uld\n",
91084860c5dSDavid du Colombier 		td->buffer[0] >> 12, td->buffer[1] >> 12, td->buffer[0] & 0xfff);
91184860c5dSDavid du Colombier 	s = seprint(s, se, "\ttpos %c tcnt %uld",
91284860c5dSDavid du Colombier 		pc[(td->buffer[0]>>3)&3], td->buffer[1] & 7);
91384860c5dSDavid du Colombier 	s = seprint(s, se, " ssm %#lux csm %#lux cspm %#lux",
91484860c5dSDavid du Colombier 		td->mfs & 0xff, (td->mfs>>8) & 0xff, (td->csw>>8) & 0xff);
91584860c5dSDavid du Colombier 	s = seprintlink(s, se, " link", td->link, 1);
91684860c5dSDavid du Colombier 	s = seprintlink(s, se, " blink", td->blink, 0);
91784860c5dSDavid du Colombier 	return seprint(s, se, "\n");
91884860c5dSDavid du Colombier }
91984860c5dSDavid du Colombier 
92084860c5dSDavid du Colombier static long
maxtdlen(Td * td)92184860c5dSDavid du Colombier maxtdlen(Td *td)
92284860c5dSDavid du Colombier {
92384860c5dSDavid du Colombier 	return (td->csw >> Tdlenshift) & Tdlenmask;
92484860c5dSDavid du Colombier }
92584860c5dSDavid du Colombier 
92684860c5dSDavid du Colombier static long
tdlen(Td * td)92784860c5dSDavid du Colombier tdlen(Td *td)
92884860c5dSDavid du Colombier {
92984860c5dSDavid du Colombier 	if(td->data == nil)
93084860c5dSDavid du Colombier 		return 0;
93184860c5dSDavid du Colombier 	return td->ndata - maxtdlen(td);
93284860c5dSDavid du Colombier }
93384860c5dSDavid du Colombier 
93484860c5dSDavid du Colombier static char*
seprinttd(char * s,char * se,Td * td,char * tag)93584860c5dSDavid du Colombier seprinttd(char *s, char *se, Td *td, char *tag)
93684860c5dSDavid du Colombier {
93784860c5dSDavid du Colombier 	int i;
93884860c5dSDavid du Colombier 	char t, ss;
93984860c5dSDavid du Colombier 	char flags[9];
94084860c5dSDavid du Colombier 	static char *tok[4] = { "out", "in", "setup", "BUG" };
94184860c5dSDavid du Colombier 
94284860c5dSDavid du Colombier 	if(td == nil)
94384860c5dSDavid du Colombier 		return seprint(s, se, "%s <nil td>\n", tag);
94484860c5dSDavid du Colombier 	s = seprint(s, se, "%s %#p", tag, td);
94584860c5dSDavid du Colombier 	s = seprintlink(s, se, " nlink", td->nlink, 0);
94684860c5dSDavid du Colombier 	s = seprintlink(s, se, " alink", td->alink, 0);
94784860c5dSDavid du Colombier 	s = seprint(s, se, " %s", tok[(td->csw & Tdtok) >> 8]);
94884860c5dSDavid du Colombier 	if((td->csw & Tdping) != 0)
94984860c5dSDavid du Colombier 		s = seprint(s, se, " png");
95084860c5dSDavid du Colombier 	memset(flags, '-', 8);
95184860c5dSDavid du Colombier 	if((td->csw & Tdactive) != 0)
95284860c5dSDavid du Colombier 		flags[0] = 'a';
95384860c5dSDavid du Colombier 	if((td->csw & Tdioc) != 0)
95484860c5dSDavid du Colombier 		flags[1] = 'i';
95584860c5dSDavid du Colombier 	if((td->csw & Tdhalt) != 0)
95684860c5dSDavid du Colombier 		flags[2] = 'h';
95784860c5dSDavid du Colombier 	if((td->csw & Tddberr) != 0)
95884860c5dSDavid du Colombier 		flags[3] = 'd';
95984860c5dSDavid du Colombier 	if((td->csw & Tdbabble) != 0)
96084860c5dSDavid du Colombier 		flags[4] = 'b';
96184860c5dSDavid du Colombier 	if((td->csw & Tdtrerr) != 0)
96284860c5dSDavid du Colombier 		flags[5] = 't';
96384860c5dSDavid du Colombier 	if((td->csw & Tdmmf) != 0)
96484860c5dSDavid du Colombier 		flags[6] = 'n';
96584860c5dSDavid du Colombier 	if((td->csw & (Tderr2|Tderr1)) == 0)
96684860c5dSDavid du Colombier 		flags[7] = 'z';
96784860c5dSDavid du Colombier 	flags[8] = 0;
96884860c5dSDavid du Colombier 	t = (td->csw & Tddata1) ? '1' : '0';
96984860c5dSDavid du Colombier 	ss = (td->csw & Tddcs) ? 'c' : 's';
97084860c5dSDavid du Colombier 	s = seprint(s, se, "\n\td%c %s %cs", t, flags, ss);
97184860c5dSDavid du Colombier 	s = seprint(s, se, " max %uld", maxtdlen(td));
97284860c5dSDavid du Colombier 	s = seprint(s, se, " pg %uld off %#lux\n",
97384860c5dSDavid du Colombier 		(td->csw >> Tdpgshift) & Tdpgmask, td->buffer[0] & 0xFFF);
97484860c5dSDavid du Colombier 	s = seprint(s, se, "\tbuffs:");
97584860c5dSDavid du Colombier 	for(i = 0; i < nelem(td->buffer); i++)
97684860c5dSDavid du Colombier 		s = seprint(s, se, " %#lux", td->buffer[i]>>12);
97784860c5dSDavid du Colombier 	if(td->data != nil)
97884860c5dSDavid du Colombier 		s = seprintdata(s, se, td->data, td->ndata);
97984860c5dSDavid du Colombier 	return seprint(s, se, "\n");
98084860c5dSDavid du Colombier }
98184860c5dSDavid du Colombier 
98284860c5dSDavid du Colombier static void
dumptd(Td * td,char * pref)98384860c5dSDavid du Colombier dumptd(Td *td, char *pref)
98484860c5dSDavid du Colombier {
98584860c5dSDavid du Colombier 	char buf[256];
98684860c5dSDavid du Colombier 	char *se;
98784860c5dSDavid du Colombier 	int i;
98884860c5dSDavid du Colombier 
98984860c5dSDavid du Colombier 	i = 0;
99084860c5dSDavid du Colombier 	se = buf+sizeof(buf);
99184860c5dSDavid du Colombier 	for(; td != nil; td = td->next){
99284860c5dSDavid du Colombier 		seprinttd(buf, se, td, pref);
99384860c5dSDavid du Colombier 		print("%s", buf);
99484860c5dSDavid du Colombier 		if(i++ > 20){
99584860c5dSDavid du Colombier 			print("...more tds...\n");
99684860c5dSDavid du Colombier 			break;
99784860c5dSDavid du Colombier 		}
99884860c5dSDavid du Colombier 	}
99984860c5dSDavid du Colombier }
100084860c5dSDavid du Colombier 
100184860c5dSDavid du Colombier static void
qhdump(Qh * qh)100284860c5dSDavid du Colombier qhdump(Qh *qh)
100384860c5dSDavid du Colombier {
100484860c5dSDavid du Colombier 	char buf[256];
100584860c5dSDavid du Colombier 	char *s, *se, *tag;
100684860c5dSDavid du Colombier 	Td td;
100784860c5dSDavid du Colombier 	static char *speed[] = {"full", "low", "high", "BUG"};
100884860c5dSDavid du Colombier 
100984860c5dSDavid du Colombier 	if(qh == nil){
101084860c5dSDavid du Colombier 		print("<nil qh>\n");
101184860c5dSDavid du Colombier 		return;
101284860c5dSDavid du Colombier 	}
101384860c5dSDavid du Colombier 	if(qh->io == nil)
101484860c5dSDavid du Colombier 		tag = "qh";
101584860c5dSDavid du Colombier 	else
101684860c5dSDavid du Colombier 		tag = qh->io->tag;
101784860c5dSDavid du Colombier 	se = buf+sizeof(buf);
101884860c5dSDavid du Colombier 	s = seprint(buf, se, "%s %#p", tag, qh);
101984860c5dSDavid du Colombier 	s = seprint(s, se, " ep %uld dev %uld",
102084860c5dSDavid du Colombier 		(qh->eps0>>8)&0xf, qh->eps0&0x7f);
102184860c5dSDavid du Colombier 	s = seprint(s, se, " hub %uld", (qh->eps1 >> 16) & 0x7f);
102284860c5dSDavid du Colombier 	s = seprint(s, se, " port %uld", (qh->eps1 >> 23) & 0x7f);
102384860c5dSDavid du Colombier 	s = seprintlink(s, se, " link", qh->link, 1);
102484860c5dSDavid du Colombier 	seprint(s, se, "  clink %#lux", qh->tclink);
102584860c5dSDavid du Colombier 	print("%s\n", buf);
102684860c5dSDavid du Colombier 	s = seprint(buf, se, "\tnrld %uld", (qh->eps0 >> Qhrlcshift) & Qhrlcmask);
102784860c5dSDavid du Colombier 	s = seprint(s, se, " nak %uld", (qh->alink >> 1) & 0xf);
102884860c5dSDavid du Colombier 	s = seprint(s, se, " max %uld ", qhmaxpkt(qh));
102984860c5dSDavid du Colombier 	if((qh->eps0 & Qhnhctl) != 0)
103084860c5dSDavid du Colombier 		s = seprint(s, se, "c");
103184860c5dSDavid du Colombier 	if((qh->eps0 & Qhhrl) != 0)
103284860c5dSDavid du Colombier 		s = seprint(s, se, "h");
103384860c5dSDavid du Colombier 	if((qh->eps0 & Qhdtc) != 0)
103484860c5dSDavid du Colombier 		s = seprint(s, se, "d");
103584860c5dSDavid du Colombier 	if((qh->eps0 & Qhint) != 0)
103684860c5dSDavid du Colombier 		s = seprint(s, se, "i");
103784860c5dSDavid du Colombier 	s = seprint(s, se, " %s", speed[(qh->eps0 >> 12) & 3]);
103884860c5dSDavid du Colombier 	s = seprint(s, se, " mult %uld", (qh->eps1 >> Qhmultshift) & Qhmultmask);
103984860c5dSDavid du Colombier 	seprint(s, se, " scm %#lux ism %#lux\n",
104084860c5dSDavid du Colombier 		(qh->eps1 >> 8 & 0xff), qh->eps1 & 0xff);
104184860c5dSDavid du Colombier 	print("%s\n", buf);
104284860c5dSDavid du Colombier 	memset(&td, 0, sizeof(td));
104384860c5dSDavid du Colombier 	memmove(&td, &qh->nlink, 32);	/* overlay area */
104484860c5dSDavid du Colombier 	seprinttd(buf, se, &td, "\tovl");
104584860c5dSDavid du Colombier 	print("%s", buf);
104684860c5dSDavid du Colombier }
104784860c5dSDavid du Colombier 
104884860c5dSDavid du Colombier static void
isodump(Isoio * iso,int all)104984860c5dSDavid du Colombier isodump(Isoio* iso, int all)
105084860c5dSDavid du Colombier {
105184860c5dSDavid du Colombier 	Itd *td, *tdi, *tdu;
105284860c5dSDavid du Colombier 	Sitd *std, *stdi, *stdu;
105384860c5dSDavid du Colombier 	char buf[256];
105484860c5dSDavid du Colombier 	int i;
105584860c5dSDavid du Colombier 
105684860c5dSDavid du Colombier 	if(iso == nil){
105784860c5dSDavid du Colombier 		print("<nil iso>\n");
105884860c5dSDavid du Colombier 		return;
105984860c5dSDavid du Colombier 	}
106084860c5dSDavid du Colombier 	print("iso %#p %s %s speed state %d nframes %d maxsz %uld",
106184860c5dSDavid du Colombier 		iso, iso->tok == Tdtokin ? "in" : "out",
106284860c5dSDavid du Colombier 		iso->hs ? "high" : "full",
106384860c5dSDavid du Colombier 		iso->state, iso->nframes, iso->maxsize);
106484860c5dSDavid du Colombier 	print(" td0 %uld tdi %#p tdu %#p data %#p\n",
106584860c5dSDavid du Colombier 		iso->td0frno, iso->tdi, iso->tdu, iso->data);
106684860c5dSDavid du Colombier 	if(iso->err != nil)
106784860c5dSDavid du Colombier 		print("\terr %s\n", iso->err);
106884860c5dSDavid du Colombier 	if(iso->err != nil)
106984860c5dSDavid du Colombier 		print("\terr='%s'\n", iso->err);
107084860c5dSDavid du Colombier 	if(all == 0)
107184860c5dSDavid du Colombier 		if(iso->hs != 0){
107284860c5dSDavid du Colombier 			tdi = iso->tdi;
107384860c5dSDavid du Colombier 			seprintitd(buf, buf+sizeof(buf), tdi);
107484860c5dSDavid du Colombier 			print("\ttdi %s\n", buf);
107584860c5dSDavid du Colombier 			tdu = iso->tdu;
107684860c5dSDavid du Colombier 			seprintitd(buf, buf+sizeof(buf), tdu);
107784860c5dSDavid du Colombier 			print("\ttdu %s\n", buf);
107884860c5dSDavid du Colombier 		}else{
107984860c5dSDavid du Colombier 			stdi = iso->stdi;
108084860c5dSDavid du Colombier 			seprintsitd(buf, buf+sizeof(buf), stdi);
108184860c5dSDavid du Colombier 			print("\tstdi %s\n", buf);
108284860c5dSDavid du Colombier 			stdu = iso->stdu;
108384860c5dSDavid du Colombier 			seprintsitd(buf, buf+sizeof(buf), stdu);
108484860c5dSDavid du Colombier 			print("\tstdu %s\n", buf);
108584860c5dSDavid du Colombier 		}
108684860c5dSDavid du Colombier 	else
108784860c5dSDavid du Colombier 		for(i = 0; i < Nisoframes; i++)
108884860c5dSDavid du Colombier 			if(iso->tdps[i] != nil)
108984860c5dSDavid du Colombier 				if(iso->hs != 0){
109084860c5dSDavid du Colombier 					td = iso->itdps[i];
109184860c5dSDavid du Colombier 					seprintitd(buf, buf+sizeof(buf), td);
109284860c5dSDavid du Colombier 					if(td == iso->tdi)
109384860c5dSDavid du Colombier 						print("i->");
109484860c5dSDavid du Colombier 					if(td == iso->tdu)
109584860c5dSDavid du Colombier 						print("i->");
109684860c5dSDavid du Colombier 					print("[%d]\t%s", i, buf);
109784860c5dSDavid du Colombier 				}else{
109884860c5dSDavid du Colombier 					std = iso->sitdps[i];
109984860c5dSDavid du Colombier 					seprintsitd(buf, buf+sizeof(buf), std);
110084860c5dSDavid du Colombier 					if(std == iso->stdi)
110184860c5dSDavid du Colombier 						print("i->");
110284860c5dSDavid du Colombier 					if(std == iso->stdu)
110384860c5dSDavid du Colombier 						print("u->");
110484860c5dSDavid du Colombier 					print("[%d]\t%s", i, buf);
110584860c5dSDavid du Colombier 				}
110684860c5dSDavid du Colombier }
110784860c5dSDavid du Colombier 
110884860c5dSDavid du Colombier static void
dump(Hci * hp)110984860c5dSDavid du Colombier dump(Hci *hp)
111084860c5dSDavid du Colombier {
111184860c5dSDavid du Colombier 	int i;
111284860c5dSDavid du Colombier 	char *s, *se;
111384860c5dSDavid du Colombier 	char buf[128];
111484860c5dSDavid du Colombier 	Ctlr *ctlr;
111584860c5dSDavid du Colombier 	Eopio *opio;
111684860c5dSDavid du Colombier 	Isoio *iso;
111784860c5dSDavid du Colombier 	Qh *qh;
111884860c5dSDavid du Colombier 
111984860c5dSDavid du Colombier 	ctlr = hp->aux;
112084860c5dSDavid du Colombier 	opio = ctlr->opio;
112184860c5dSDavid du Colombier 	ilock(ctlr);
112284860c5dSDavid du Colombier 	print("ehci port %#p frames %#p (%d fr.) nintr %d ntdintr %d",
112384860c5dSDavid du Colombier 		ctlr->capio, ctlr->frames, ctlr->nframes,
112484860c5dSDavid du Colombier 		ctlr->nintr, ctlr->ntdintr);
112584860c5dSDavid du Colombier 	print(" nqhintr %d nisointr %d\n", ctlr->nqhintr, ctlr->nisointr);
112684860c5dSDavid du Colombier 	print("\tcmd %#lux sts %#lux intr %#lux frno %uld",
112784860c5dSDavid du Colombier 		opio->cmd, opio->sts, opio->intr, opio->frno);
112884860c5dSDavid du Colombier 	print(" base %#lux link %#lux fr0 %#lux\n",
112984860c5dSDavid du Colombier 		opio->frbase, opio->link, ctlr->frames[0]);
113084860c5dSDavid du Colombier 	se = buf+sizeof(buf);
113184860c5dSDavid du Colombier 	s = seprint(buf, se, "\t");
113284860c5dSDavid du Colombier 	for(i = 0; i < hp->nports; i++){
113384860c5dSDavid du Colombier 		s = seprint(s, se, "p%d %#lux ", i, opio->portsc[i]);
113484860c5dSDavid du Colombier 		if(hp->nports > 4 && i == hp->nports/2 - 1)
113584860c5dSDavid du Colombier 			s = seprint(s, se, "\n\t");
113684860c5dSDavid du Colombier 	}
113784860c5dSDavid du Colombier 	print("%s\n", buf);
113884860c5dSDavid du Colombier 	qh = ctlr->qhs;
113984860c5dSDavid du Colombier 	i = 0;
114084860c5dSDavid du Colombier 	do{
114184860c5dSDavid du Colombier 		qhdump(qh);
114284860c5dSDavid du Colombier 		qh = qh->next;
114384860c5dSDavid du Colombier 	}while(qh != ctlr->qhs && i++ < 100);
114484860c5dSDavid du Colombier 	if(i > 100)
114584860c5dSDavid du Colombier 		print("...too many Qhs...\n");
114684860c5dSDavid du Colombier 	if(ctlr->intrqhs != nil)
114784860c5dSDavid du Colombier 		print("intr qhs:\n");
114884860c5dSDavid du Colombier 	for(qh = ctlr->intrqhs; qh != nil; qh = qh->inext)
114984860c5dSDavid du Colombier 		qhdump(qh);
115084860c5dSDavid du Colombier 	if(ctlr->iso != nil)
115184860c5dSDavid du Colombier 		print("iso:\n");
115284860c5dSDavid du Colombier 	for(iso = ctlr->iso; iso != nil; iso = iso->next)
115384860c5dSDavid du Colombier 		isodump(ctlr->iso, 0);
115484860c5dSDavid du Colombier 	print("%d eds in tree\n", ctlr->ntree);
115584860c5dSDavid du Colombier 	iunlock(ctlr);
115684860c5dSDavid du Colombier 	lock(&edpool);
115784860c5dSDavid du Colombier 	print("%d eds allocated = %d in use + %d free\n",
115884860c5dSDavid du Colombier 		edpool.nalloc, edpool.ninuse, edpool.nfree);
115984860c5dSDavid du Colombier 	unlock(&edpool);
116084860c5dSDavid du Colombier }
116184860c5dSDavid du Colombier 
116284860c5dSDavid du Colombier static char*
errmsg(int err)116384860c5dSDavid du Colombier errmsg(int err)
116484860c5dSDavid du Colombier {
116584860c5dSDavid du Colombier 	if(err == 0)
116684860c5dSDavid du Colombier 		return "ok";
116784860c5dSDavid du Colombier 	if(err & Tddberr)
116884860c5dSDavid du Colombier 		return "data buffer error";
116984860c5dSDavid du Colombier 	if(err & Tdbabble)
117084860c5dSDavid du Colombier 		return "babble detected";
117184860c5dSDavid du Colombier 	if(err & Tdtrerr)
117284860c5dSDavid du Colombier 		return "transaction error";
117384860c5dSDavid du Colombier 	if(err & Tdmmf)
117484860c5dSDavid du Colombier 		return "missed µframe";
117584860c5dSDavid du Colombier 	if(err & Tdhalt)
117684860c5dSDavid du Colombier 		return Estalled;	/* [uo]hci report this error */
117784860c5dSDavid du Colombier 	return Eio;
117884860c5dSDavid du Colombier }
117984860c5dSDavid du Colombier 
118084860c5dSDavid du Colombier static char*
ierrmsg(int err)118184860c5dSDavid du Colombier ierrmsg(int err)
118284860c5dSDavid du Colombier {
118384860c5dSDavid du Colombier 	if(err == 0)
118484860c5dSDavid du Colombier 		return "ok";
118584860c5dSDavid du Colombier 	if(err & Itddberr)
118684860c5dSDavid du Colombier 		return "data buffer error";
118784860c5dSDavid du Colombier 	if(err & Itdbabble)
118884860c5dSDavid du Colombier 		return "babble detected";
118984860c5dSDavid du Colombier 	if(err & Itdtrerr)
119084860c5dSDavid du Colombier 		return "transaction error";
119184860c5dSDavid du Colombier 	return Eio;
119284860c5dSDavid du Colombier }
119384860c5dSDavid du Colombier 
119484860c5dSDavid du Colombier static char*
serrmsg(int err)119584860c5dSDavid du Colombier serrmsg(int err)
119684860c5dSDavid du Colombier {
119784860c5dSDavid du Colombier 	if(err & Stderr)
119884860c5dSDavid du Colombier 		return "translation translator error";
119984860c5dSDavid du Colombier 	/* other errors have same numbers than Td errors */
120084860c5dSDavid du Colombier 	return errmsg(err);
120184860c5dSDavid du Colombier }
120284860c5dSDavid du Colombier 
120384860c5dSDavid du Colombier static int
isocanread(void * a)120484860c5dSDavid du Colombier isocanread(void *a)
120584860c5dSDavid du Colombier {
120684860c5dSDavid du Colombier 	Isoio *iso;
120784860c5dSDavid du Colombier 
120884860c5dSDavid du Colombier 	iso = a;
120984860c5dSDavid du Colombier 	if(iso->state == Qclose)
121084860c5dSDavid du Colombier 		return 1;
121184860c5dSDavid du Colombier 	if(iso->state == Qrun && iso->tok == Tdtokin){
121284860c5dSDavid du Colombier 		if(iso->hs != 0 && iso->tdi != iso->tdu)
121384860c5dSDavid du Colombier 			return 1;
121484860c5dSDavid du Colombier 		if(iso->hs == 0 && iso->stdi != iso->stdu)
121584860c5dSDavid du Colombier 			return 1;
121684860c5dSDavid du Colombier 	}
121784860c5dSDavid du Colombier 	return 0;
121884860c5dSDavid du Colombier }
121984860c5dSDavid du Colombier 
122084860c5dSDavid du Colombier static int
isocanwrite(void * a)122184860c5dSDavid du Colombier isocanwrite(void *a)
122284860c5dSDavid du Colombier {
122384860c5dSDavid du Colombier 	Isoio *iso;
122484860c5dSDavid du Colombier 
122584860c5dSDavid du Colombier 	iso = a;
122684860c5dSDavid du Colombier 	if(iso->state == Qclose)
122784860c5dSDavid du Colombier 		return 1;
122884860c5dSDavid du Colombier 	if(iso->state == Qrun && iso->tok == Tdtokout){
122984860c5dSDavid du Colombier 		if(iso->hs != 0 && iso->tdu->next != iso->tdi)
123084860c5dSDavid du Colombier 			return 1;
123184860c5dSDavid du Colombier 		if(iso->hs == 0 && iso->stdu->next != iso->stdi)
123284860c5dSDavid du Colombier 			return 1;
123384860c5dSDavid du Colombier 	}
123484860c5dSDavid du Colombier 	return 0;
123584860c5dSDavid du Colombier }
123684860c5dSDavid du Colombier 
123784860c5dSDavid du Colombier static void
itdinit(Isoio * iso,Itd * td)123884860c5dSDavid du Colombier itdinit(Isoio *iso, Itd *td)
123984860c5dSDavid du Colombier {
124084860c5dSDavid du Colombier 	int p, t;
124184860c5dSDavid du Colombier 	ulong pa, tsize, size;
124284860c5dSDavid du Colombier 
124384860c5dSDavid du Colombier 	/*
124484860c5dSDavid du Colombier 	 * BUG: This does not put an integral number of samples
124584860c5dSDavid du Colombier 	 * on each µframe unless samples per packet % 8 == 0
124684860c5dSDavid du Colombier 	 * Also, all samples are packed early on each frame.
124784860c5dSDavid du Colombier 	 */
124884860c5dSDavid du Colombier 	p = 0;
124984860c5dSDavid du Colombier 	size = td->ndata = td->mdata;
125084860c5dSDavid du Colombier 	pa = PADDR(td->data);
125184860c5dSDavid du Colombier 	for(t = 0; size > 0 && t < 8; t++){
125284860c5dSDavid du Colombier 		tsize = size;
125384860c5dSDavid du Colombier 		if(tsize > iso->maxsize)
125484860c5dSDavid du Colombier 			tsize = iso->maxsize;
125584860c5dSDavid du Colombier 		size -= tsize;
125684860c5dSDavid du Colombier 		assert(p < nelem(td->buffer));
125784860c5dSDavid du Colombier 		td->csw[t] = tsize << Itdlenshift | p << Itdpgshift |
125884860c5dSDavid du Colombier 			(pa & 0xFFF) << Itdoffshift | Itdactive | Itdioc;
125984860c5dSDavid du Colombier 		coherence();
126084860c5dSDavid du Colombier 		if(((pa+tsize) & ~0xFFF) != (pa & ~0xFFF))
126184860c5dSDavid du Colombier 			p++;
126284860c5dSDavid du Colombier 		pa += tsize;
126384860c5dSDavid du Colombier 	}
126484860c5dSDavid du Colombier }
126584860c5dSDavid du Colombier 
126684860c5dSDavid du Colombier static void
sitdinit(Isoio * iso,Sitd * td)126784860c5dSDavid du Colombier sitdinit(Isoio *iso, Sitd *td)
126884860c5dSDavid du Colombier {
126984860c5dSDavid du Colombier 	td->ndata = td->mdata & Stdlenmask;
127084860c5dSDavid du Colombier 	td->buffer[0] = PADDR(td->data);
127184860c5dSDavid du Colombier 	td->buffer[1] = (td->buffer[0] & ~0xFFF) + 0x1000;
127284860c5dSDavid du Colombier 	if(iso->tok == Tdtokin || td->ndata <= 188)
127384860c5dSDavid du Colombier 		td->buffer[1] |= Stdtpall;
127484860c5dSDavid du Colombier 	else
127584860c5dSDavid du Colombier 		td->buffer[1] |= Stdtpbegin;
127684860c5dSDavid du Colombier 	if(iso->tok == Tdtokin)
127784860c5dSDavid du Colombier 		td->buffer[1] |= 1;
127884860c5dSDavid du Colombier 	else
127984860c5dSDavid du Colombier 		td->buffer[1] |= ((td->ndata + 187) / 188) & Stdtcntmask;
128084860c5dSDavid du Colombier 	coherence();
128184860c5dSDavid du Colombier 	td->csw = td->ndata << Stdlenshift | Stdactive | Stdioc;
128284860c5dSDavid du Colombier 	coherence();
128384860c5dSDavid du Colombier }
128484860c5dSDavid du Colombier 
128584860c5dSDavid du Colombier static int
itdactive(Itd * td)128684860c5dSDavid du Colombier itdactive(Itd *td)
128784860c5dSDavid du Colombier {
128884860c5dSDavid du Colombier 	int i;
128984860c5dSDavid du Colombier 
129084860c5dSDavid du Colombier 	for(i = 0; i < nelem(td->csw); i++)
129184860c5dSDavid du Colombier 		if((td->csw[i] & Itdactive) != 0)
129284860c5dSDavid du Colombier 			return 1;
129384860c5dSDavid du Colombier 	return 0;
129484860c5dSDavid du Colombier }
129584860c5dSDavid du Colombier 
129684860c5dSDavid du Colombier static int
isohsinterrupt(Ctlr * ctlr,Isoio * iso)129784860c5dSDavid du Colombier isohsinterrupt(Ctlr *ctlr, Isoio *iso)
129884860c5dSDavid du Colombier {
129984860c5dSDavid du Colombier 	int err, i, nframes, t;
130084860c5dSDavid du Colombier 	Itd *tdi;
130184860c5dSDavid du Colombier 
130284860c5dSDavid du Colombier 	tdi = iso->tdi;
130384860c5dSDavid du Colombier 	assert(tdi != nil);
130484860c5dSDavid du Colombier 	if(itdactive(tdi))			/* not all tds are done */
130584860c5dSDavid du Colombier 		return 0;
130684860c5dSDavid du Colombier 	ctlr->nisointr++;
130784860c5dSDavid du Colombier 	ddiprint("isohsintr: iso %#p: tdi %#p tdu %#p\n", iso, tdi, iso->tdu);
130884860c5dSDavid du Colombier 	if(iso->state != Qrun && iso->state != Qdone)
130984860c5dSDavid du Colombier 		panic("isofsintr: iso state");
131084860c5dSDavid du Colombier 	if(ehcidebug > 1 || iso->debug > 1)
131184860c5dSDavid du Colombier 		isodump(iso, 0);
131284860c5dSDavid du Colombier 
131384860c5dSDavid du Colombier 	nframes = iso->nframes / 2;		/* limit how many we look */
131484860c5dSDavid du Colombier 	if(nframes > Nisoframes)
131584860c5dSDavid du Colombier 		nframes = Nisoframes;
131684860c5dSDavid du Colombier 
131784860c5dSDavid du Colombier 	if(iso->tok == Tdtokin)
131884860c5dSDavid du Colombier 		tdi->ndata = 0;
131984860c5dSDavid du Colombier 	/* else, it has the number of bytes transferred */
132084860c5dSDavid du Colombier 
132184860c5dSDavid du Colombier 	for(i = 0; i < nframes && itdactive(tdi) == 0; i++){
132284860c5dSDavid du Colombier 		if(iso->tok == Tdtokin)
132384860c5dSDavid du Colombier 			tdi->ndata += (tdi->csw[i] >> Itdlenshift) & Itdlenmask;
132484860c5dSDavid du Colombier 		err = 0;
132584860c5dSDavid du Colombier 		coherence();
132684860c5dSDavid du Colombier 		for(t = 0; t < nelem(tdi->csw); t++){
132784860c5dSDavid du Colombier 			tdi->csw[t] &= ~Itdioc;
132884860c5dSDavid du Colombier 			coherence();
132984860c5dSDavid du Colombier 			err |= tdi->csw[t] & Itderrors;
133084860c5dSDavid du Colombier 		}
133184860c5dSDavid du Colombier 		if(err == 0)
133284860c5dSDavid du Colombier 			iso->nerrs = 0;
133384860c5dSDavid du Colombier 		else if(iso->nerrs++ > iso->nframes/2){
133484860c5dSDavid du Colombier 			if(iso->err == nil){
133584860c5dSDavid du Colombier 				iso->err = ierrmsg(err);
133684860c5dSDavid du Colombier 				diprint("isohsintr: tdi %#p error %#ux %s\n",
133784860c5dSDavid du Colombier 					tdi, err, iso->err);
133884860c5dSDavid du Colombier 				diprint("ctlr load %uld\n", ctlr->load);
133984860c5dSDavid du Colombier 			}
134084860c5dSDavid du Colombier 			tdi->ndata = 0;
134184860c5dSDavid du Colombier 		}else
134284860c5dSDavid du Colombier 			tdi->ndata = 0;
134384860c5dSDavid du Colombier 		if(tdi->next == iso->tdu || tdi->next->next == iso->tdu){
134484860c5dSDavid du Colombier 			memset(iso->tdu->data, 0, iso->tdu->mdata);
134584860c5dSDavid du Colombier 			itdinit(iso, iso->tdu);
134684860c5dSDavid du Colombier 			iso->tdu = iso->tdu->next;
134784860c5dSDavid du Colombier 			iso->nleft = 0;
134884860c5dSDavid du Colombier 		}
134984860c5dSDavid du Colombier 		tdi = tdi->next;
135084860c5dSDavid du Colombier 		coherence();
135184860c5dSDavid du Colombier 	}
135284860c5dSDavid du Colombier 	ddiprint("isohsintr: %d frames processed\n", nframes);
135384860c5dSDavid du Colombier 	if(i == nframes){
135484860c5dSDavid du Colombier 		tdi->csw[0] |= Itdioc;
135584860c5dSDavid du Colombier 		coherence();
135684860c5dSDavid du Colombier 	}
135784860c5dSDavid du Colombier 	iso->tdi = tdi;
135884860c5dSDavid du Colombier 	coherence();
135984860c5dSDavid du Colombier 	if(isocanwrite(iso) || isocanread(iso)){
136084860c5dSDavid du Colombier 		diprint("wakeup iso %#p tdi %#p tdu %#p\n", iso,
136184860c5dSDavid du Colombier 			iso->tdi, iso->tdu);
136284860c5dSDavid du Colombier 		wakeup(iso);
136384860c5dSDavid du Colombier 	}
136484860c5dSDavid du Colombier 	return 1;
136584860c5dSDavid du Colombier }
136684860c5dSDavid du Colombier 
136784860c5dSDavid du Colombier static int
isofsinterrupt(Ctlr * ctlr,Isoio * iso)136884860c5dSDavid du Colombier isofsinterrupt(Ctlr *ctlr, Isoio *iso)
136984860c5dSDavid du Colombier {
137084860c5dSDavid du Colombier 	int err, i, nframes;
137184860c5dSDavid du Colombier 	Sitd *stdi;
137284860c5dSDavid du Colombier 
137384860c5dSDavid du Colombier 	stdi = iso->stdi;
137484860c5dSDavid du Colombier 	assert(stdi != nil);
137584860c5dSDavid du Colombier 	if((stdi->csw & Stdactive) != 0)		/* nothing new done */
137684860c5dSDavid du Colombier 		return 0;
137784860c5dSDavid du Colombier 	ctlr->nisointr++;
137884860c5dSDavid du Colombier 	ddiprint("isofsintr: iso %#p: tdi %#p tdu %#p\n", iso, stdi, iso->stdu);
137984860c5dSDavid du Colombier 	if(iso->state != Qrun && iso->state != Qdone)
138084860c5dSDavid du Colombier 		panic("isofsintr: iso state");
138184860c5dSDavid du Colombier 	if(ehcidebug > 1 || iso->debug > 1)
138284860c5dSDavid du Colombier 		isodump(iso, 0);
138384860c5dSDavid du Colombier 
138484860c5dSDavid du Colombier 	nframes = iso->nframes / 2;		/* limit how many we look */
138584860c5dSDavid du Colombier 	if(nframes > Nisoframes)
138684860c5dSDavid du Colombier 		nframes = Nisoframes;
138784860c5dSDavid du Colombier 
138884860c5dSDavid du Colombier 	for(i = 0; i < nframes && (stdi->csw & Stdactive) == 0; i++){
138984860c5dSDavid du Colombier 		stdi->csw &= ~Stdioc;
139084860c5dSDavid du Colombier 		/* write back csw and see if it produces errors */
139184860c5dSDavid du Colombier 		coherence();
139284860c5dSDavid du Colombier 		err = stdi->csw & Stderrors;
139384860c5dSDavid du Colombier 		if(err == 0){
139484860c5dSDavid du Colombier 			iso->nerrs = 0;
139584860c5dSDavid du Colombier 			if(iso->tok == Tdtokin)
139684860c5dSDavid du Colombier 				stdi->ndata = (stdi->csw>>Stdlenshift)&Stdlenmask;
139784860c5dSDavid du Colombier 			/* else len is assumed correct */
139884860c5dSDavid du Colombier 		}else if(iso->nerrs++ > iso->nframes/2){
139984860c5dSDavid du Colombier 			if(iso->err == nil){
140084860c5dSDavid du Colombier 				iso->err = serrmsg(err);
140184860c5dSDavid du Colombier 				diprint("isofsintr: tdi %#p error %#ux %s\n",
140284860c5dSDavid du Colombier 					stdi, err, iso->err);
140384860c5dSDavid du Colombier 				diprint("ctlr load %uld\n", ctlr->load);
140484860c5dSDavid du Colombier 			}
140584860c5dSDavid du Colombier 			stdi->ndata = 0;
140684860c5dSDavid du Colombier 		}else
140784860c5dSDavid du Colombier 			stdi->ndata = 0;
140884860c5dSDavid du Colombier 
140984860c5dSDavid du Colombier 		if(stdi->next == iso->stdu || stdi->next->next == iso->stdu){
141084860c5dSDavid du Colombier 			memset(iso->stdu->data, 0, iso->stdu->mdata);
141184860c5dSDavid du Colombier 			coherence();
141284860c5dSDavid du Colombier 			sitdinit(iso, iso->stdu);
141384860c5dSDavid du Colombier 			iso->stdu = iso->stdu->next;
141484860c5dSDavid du Colombier 			iso->nleft = 0;
141584860c5dSDavid du Colombier 		}
141684860c5dSDavid du Colombier 		coherence();
141784860c5dSDavid du Colombier 		stdi = stdi->next;
141884860c5dSDavid du Colombier 	}
141984860c5dSDavid du Colombier 	ddiprint("isofsintr: %d frames processed\n", nframes);
142084860c5dSDavid du Colombier 	if(i == nframes){
142184860c5dSDavid du Colombier 		stdi->csw |= Stdioc;
142284860c5dSDavid du Colombier 		coherence();
142384860c5dSDavid du Colombier 	}
142484860c5dSDavid du Colombier 	iso->stdi = stdi;
142584860c5dSDavid du Colombier 	coherence();
142684860c5dSDavid du Colombier 	if(isocanwrite(iso) || isocanread(iso)){
142784860c5dSDavid du Colombier 		diprint("wakeup iso %#p tdi %#p tdu %#p\n", iso,
142884860c5dSDavid du Colombier 			iso->stdi, iso->stdu);
142984860c5dSDavid du Colombier 		wakeup(iso);
143084860c5dSDavid du Colombier 	}
143184860c5dSDavid du Colombier 	return 1;
143284860c5dSDavid du Colombier }
143384860c5dSDavid du Colombier 
143484860c5dSDavid du Colombier static int
qhinterrupt(Ctlr * ctlr,Qh * qh)143584860c5dSDavid du Colombier qhinterrupt(Ctlr *ctlr, Qh *qh)
143684860c5dSDavid du Colombier {
143784860c5dSDavid du Colombier 	Td *td;
143884860c5dSDavid du Colombier 	int err;
143984860c5dSDavid du Colombier 
144084860c5dSDavid du Colombier 	if(qh->state != Qrun)
144184860c5dSDavid du Colombier 		panic("qhinterrupt: qh state");
144284860c5dSDavid du Colombier 	td = qh->tds;
144384860c5dSDavid du Colombier 	if(td == nil)
144484860c5dSDavid du Colombier 		panic("qhinterrupt: no tds");
144584860c5dSDavid du Colombier 	if((td->csw & Tdactive) == 0)
144684860c5dSDavid du Colombier 		ddqprint("qhinterrupt port %#p qh %#p\n", ctlr->capio, qh);
144784860c5dSDavid du Colombier 	for(; td != nil; td = td->next){
144884860c5dSDavid du Colombier 		if(td->csw & Tdactive)
144984860c5dSDavid du Colombier 			return 0;
145084860c5dSDavid du Colombier 		err = td->csw & Tderrors;
145184860c5dSDavid du Colombier 		if(err != 0){
145284860c5dSDavid du Colombier 			if(qh->io->err == nil){
145384860c5dSDavid du Colombier 				qh->io->err = errmsg(err);
145484860c5dSDavid du Colombier 				dqprint("qhintr: td %#p csw %#lux error %#ux %s\n",
145584860c5dSDavid du Colombier 					td, td->csw, err, qh->io->err);
145684860c5dSDavid du Colombier 			}
145784860c5dSDavid du Colombier 			break;
145884860c5dSDavid du Colombier 		}
145984860c5dSDavid du Colombier 		td->ndata = tdlen(td);
146084860c5dSDavid du Colombier 		coherence();
146184860c5dSDavid du Colombier 		if(td->ndata < maxtdlen(td)){	/* EOT */
146284860c5dSDavid du Colombier 			td = td->next;
146384860c5dSDavid du Colombier 			break;
146484860c5dSDavid du Colombier 		}
146584860c5dSDavid du Colombier 	}
146684860c5dSDavid du Colombier 	/*
146784860c5dSDavid du Colombier 	 * Done. Make void the Tds not used (errors or EOT) and wakeup epio.
146884860c5dSDavid du Colombier 	 */
146984860c5dSDavid du Colombier 	for(; td != nil; td = td->next)
147084860c5dSDavid du Colombier 		td->ndata = 0;
147184860c5dSDavid du Colombier 	coherence();
147284860c5dSDavid du Colombier 	qh->state = Qdone;
147384860c5dSDavid du Colombier 	coherence();
147484860c5dSDavid du Colombier 	wakeup(qh->io);
147584860c5dSDavid du Colombier 	return 1;
147684860c5dSDavid du Colombier }
147784860c5dSDavid du Colombier 
147884860c5dSDavid du Colombier static int
ehciintr(Hci * hp)147984860c5dSDavid du Colombier ehciintr(Hci *hp)
148084860c5dSDavid du Colombier {
148184860c5dSDavid du Colombier 	Ctlr *ctlr;
148284860c5dSDavid du Colombier 	Eopio *opio;
148384860c5dSDavid du Colombier 	Isoio *iso;
148484860c5dSDavid du Colombier 	ulong sts;
148584860c5dSDavid du Colombier 	Qh *qh;
148684860c5dSDavid du Colombier 	int i, some;
148784860c5dSDavid du Colombier 
148884860c5dSDavid du Colombier 	ctlr = hp->aux;
148984860c5dSDavid du Colombier 	opio = ctlr->opio;
149084860c5dSDavid du Colombier 
149184860c5dSDavid du Colombier 	/*
149284860c5dSDavid du Colombier 	 * Will we know in USB 3.0 who the interrupt was for?.
149384860c5dSDavid du Colombier 	 * Do they still teach indexing in CS?
149484860c5dSDavid du Colombier 	 * This is Intel's doing.
149584860c5dSDavid du Colombier 	 */
149684860c5dSDavid du Colombier 	ilock(ctlr);
149784860c5dSDavid du Colombier 	ctlr->nintr++;
149884860c5dSDavid du Colombier 	sts = opio->sts & Sintrs;
149984860c5dSDavid du Colombier 	if(sts == 0){		/* not ours; shared intr. */
150084860c5dSDavid du Colombier 		iunlock(ctlr);
150184860c5dSDavid du Colombier 		return 0;
150284860c5dSDavid du Colombier 	}
150384860c5dSDavid du Colombier 	opio->sts = sts;
150484860c5dSDavid du Colombier 	coherence();
150584860c5dSDavid du Colombier 	if((sts & Sherr) != 0)
150684860c5dSDavid du Colombier 		print("ehci: port %#p fatal host system error\n", ctlr->capio);
150784860c5dSDavid du Colombier 	if((sts & Shalted) != 0)
150884860c5dSDavid du Colombier 		print("ehci: port %#p: halted\n", ctlr->capio);
150984860c5dSDavid du Colombier 	if((sts & Sasync) != 0){
151084860c5dSDavid du Colombier 		dprint("ehci: doorbell\n");
151184860c5dSDavid du Colombier 		wakeup(ctlr);
151284860c5dSDavid du Colombier 	}
151384860c5dSDavid du Colombier 	/*
151484860c5dSDavid du Colombier 	 * We enter always this if, even if it seems the
151584860c5dSDavid du Colombier 	 * interrupt does not report anything done/failed.
151684860c5dSDavid du Colombier 	 * Some controllers don't post interrupts right.
151784860c5dSDavid du Colombier 	 */
151884860c5dSDavid du Colombier 	some = 0;
151984860c5dSDavid du Colombier 	if((sts & (Serrintr|Sintr)) != 0){
152084860c5dSDavid du Colombier 		ctlr->ntdintr++;
152184860c5dSDavid du Colombier 		if(ehcidebug > 1){
152284860c5dSDavid du Colombier 			print("ehci port %#p frames %#p nintr %d ntdintr %d",
152384860c5dSDavid du Colombier 				ctlr->capio, ctlr->frames,
152484860c5dSDavid du Colombier 				ctlr->nintr, ctlr->ntdintr);
152584860c5dSDavid du Colombier 			print(" nqhintr %d nisointr %d\n",
152684860c5dSDavid du Colombier 				ctlr->nqhintr, ctlr->nisointr);
152784860c5dSDavid du Colombier 			print("\tcmd %#lux sts %#lux intr %#lux frno %uld",
152884860c5dSDavid du Colombier 				opio->cmd, opio->sts, opio->intr, opio->frno);
152984860c5dSDavid du Colombier 		}
153084860c5dSDavid du Colombier 
153184860c5dSDavid du Colombier 		/* process the Iso transfers */
153284860c5dSDavid du Colombier 		for(iso = ctlr->iso; iso != nil; iso = iso->next)
153384860c5dSDavid du Colombier 			if(iso->state == Qrun || iso->state == Qdone)
153484860c5dSDavid du Colombier 				if(iso->hs != 0)
153584860c5dSDavid du Colombier 					some += isohsinterrupt(ctlr, iso);
153684860c5dSDavid du Colombier 				else
153784860c5dSDavid du Colombier 					some += isofsinterrupt(ctlr, iso);
153884860c5dSDavid du Colombier 
153984860c5dSDavid du Colombier 		/* process the qhs in the periodic tree */
154084860c5dSDavid du Colombier 		for(qh = ctlr->intrqhs; qh != nil; qh = qh->inext)
154184860c5dSDavid du Colombier 			if(qh->state == Qrun)
154284860c5dSDavid du Colombier 				some += qhinterrupt(ctlr, qh);
154384860c5dSDavid du Colombier 
154484860c5dSDavid du Colombier 		/* process the async Qh circular list */
154584860c5dSDavid du Colombier 		qh = ctlr->qhs;
154684860c5dSDavid du Colombier 		i = 0;
154784860c5dSDavid du Colombier 		do{
154884860c5dSDavid du Colombier 			if (qh == nil)
154984860c5dSDavid du Colombier 				panic("ehciintr: nil qh");
155084860c5dSDavid du Colombier 			if(qh->state == Qrun)
155184860c5dSDavid du Colombier 				some += qhinterrupt(ctlr, qh);
155284860c5dSDavid du Colombier 			qh = qh->next;
155384860c5dSDavid du Colombier 		}while(qh != ctlr->qhs && i++ < 100);
155484860c5dSDavid du Colombier 		if(i > 100)
155584860c5dSDavid du Colombier 			print("echi: interrupt: qh loop?\n");
155684860c5dSDavid du Colombier 	}
155784860c5dSDavid du Colombier //	if (some == 0)
155884860c5dSDavid du Colombier //		panic("ehciintr: no work");
155984860c5dSDavid du Colombier 	iunlock(ctlr);
156084860c5dSDavid du Colombier 	return some;
156184860c5dSDavid du Colombier }
156284860c5dSDavid du Colombier 
156384860c5dSDavid du Colombier static void
interrupt(Ureg *,void * a)156484860c5dSDavid du Colombier interrupt(Ureg*, void* a)
156584860c5dSDavid du Colombier {
156684860c5dSDavid du Colombier 	ehciintr(a);
156784860c5dSDavid du Colombier }
156884860c5dSDavid du Colombier 
156984860c5dSDavid du Colombier static int
portenable(Hci * hp,int port,int on)157084860c5dSDavid du Colombier portenable(Hci *hp, int port, int on)
157184860c5dSDavid du Colombier {
157284860c5dSDavid du Colombier 	Ctlr *ctlr;
157384860c5dSDavid du Colombier 	Eopio *opio;
157484860c5dSDavid du Colombier 	int s;
157584860c5dSDavid du Colombier 
157684860c5dSDavid du Colombier 	ctlr = hp->aux;
157784860c5dSDavid du Colombier 	opio = ctlr->opio;
157884860c5dSDavid du Colombier 	s = opio->portsc[port-1];
157984860c5dSDavid du Colombier 	qlock(&ctlr->portlck);
158084860c5dSDavid du Colombier 	if(waserror()){
158184860c5dSDavid du Colombier 		qunlock(&ctlr->portlck);
158284860c5dSDavid du Colombier 		nexterror();
158384860c5dSDavid du Colombier 	}
158484860c5dSDavid du Colombier 	dprint("ehci %#p port %d enable=%d; sts %#x\n",
158584860c5dSDavid du Colombier 		ctlr->capio, port, on, s);
158684860c5dSDavid du Colombier 	ilock(ctlr);
158784860c5dSDavid du Colombier 	if(s & (Psstatuschg | Pschange))
158884860c5dSDavid du Colombier 		opio->portsc[port-1] = s;
158984860c5dSDavid du Colombier 	if(on)
159084860c5dSDavid du Colombier 		opio->portsc[port-1] |= Psenable;
159184860c5dSDavid du Colombier 	else
159284860c5dSDavid du Colombier 		opio->portsc[port-1] &= ~Psenable;
159384860c5dSDavid du Colombier 	coherence();
159484860c5dSDavid du Colombier 	microdelay(64);
159584860c5dSDavid du Colombier 	iunlock(ctlr);
159684860c5dSDavid du Colombier 	tsleep(&up->sleep, return0, 0, Enabledelay);
159784860c5dSDavid du Colombier 	dprint("ehci %#p port %d enable=%d: sts %#lux\n",
159884860c5dSDavid du Colombier 		ctlr->capio, port, on, opio->portsc[port-1]);
159984860c5dSDavid du Colombier 	qunlock(&ctlr->portlck);
160084860c5dSDavid du Colombier 	poperror();
160184860c5dSDavid du Colombier 	return 0;
160284860c5dSDavid du Colombier }
160384860c5dSDavid du Colombier 
160484860c5dSDavid du Colombier /*
160584860c5dSDavid du Colombier  * If we detect during status that the port is low-speed or
160684860c5dSDavid du Colombier  * during reset that it's full-speed, the device is not for
160784860c5dSDavid du Colombier  * ourselves. The companion controller will take care.
160884860c5dSDavid du Colombier  * Low-speed devices will not be seen by usbd. Full-speed
160984860c5dSDavid du Colombier  * ones are seen because it's only after reset that we know what
161084860c5dSDavid du Colombier  * they are (usbd may notice a device not enabled in this case).
161184860c5dSDavid du Colombier  */
161284860c5dSDavid du Colombier static void
portlend(Ctlr * ctlr,int port,char * ss)161384860c5dSDavid du Colombier portlend(Ctlr *ctlr, int port, char *ss)
161484860c5dSDavid du Colombier {
161584860c5dSDavid du Colombier 	Eopio *opio;
161684860c5dSDavid du Colombier 	ulong s;
161784860c5dSDavid du Colombier 
161884860c5dSDavid du Colombier 	opio = ctlr->opio;
161984860c5dSDavid du Colombier 
162084860c5dSDavid du Colombier 	dprint("ehci %#p port %d: %s speed device: no longer owned\n",
162184860c5dSDavid du Colombier 		ctlr->capio, port, ss);
162284860c5dSDavid du Colombier 	s = opio->portsc[port-1] & ~(Pschange|Psstatuschg);
162384860c5dSDavid du Colombier 	opio->portsc[port-1] = s | Psowner;
162484860c5dSDavid du Colombier 	coherence();
162584860c5dSDavid du Colombier }
162684860c5dSDavid du Colombier 
162784860c5dSDavid du Colombier static int
portreset(Hci * hp,int port,int on)162884860c5dSDavid du Colombier portreset(Hci *hp, int port, int on)
162984860c5dSDavid du Colombier {
16305a5ba3fdSDavid du Colombier 	ulong *portscp;
163184860c5dSDavid du Colombier 	Eopio *opio;
163284860c5dSDavid du Colombier 	Ctlr *ctlr;
163384860c5dSDavid du Colombier 	int i;
163484860c5dSDavid du Colombier 
163584860c5dSDavid du Colombier 	if(on == 0)
163684860c5dSDavid du Colombier 		return 0;
163784860c5dSDavid du Colombier 
163884860c5dSDavid du Colombier 	ctlr = hp->aux;
163984860c5dSDavid du Colombier 	opio = ctlr->opio;
164084860c5dSDavid du Colombier 	qlock(&ctlr->portlck);
164184860c5dSDavid du Colombier 	if(waserror()){
164284860c5dSDavid du Colombier 		iunlock(ctlr);
164384860c5dSDavid du Colombier 		qunlock(&ctlr->portlck);
164484860c5dSDavid du Colombier 		nexterror();
164584860c5dSDavid du Colombier 	}
16465a5ba3fdSDavid du Colombier 	portscp = &opio->portsc[port-1];
16475a5ba3fdSDavid du Colombier 	dprint("ehci %#p port %d reset; sts %#lux\n", ctlr->capio, port, *portscp);
164884860c5dSDavid du Colombier 	ilock(ctlr);
16495a5ba3fdSDavid du Colombier 	/* Shalted must be zero, else Psreset will stay set */
16505a5ba3fdSDavid du Colombier 	if (opio->sts & Shalted)
16515a5ba3fdSDavid du Colombier 		iprint("ehci %#p: halted yet trying to reset port\n",
16525a5ba3fdSDavid du Colombier 			ctlr->capio);
16535a5ba3fdSDavid du Colombier 	*portscp = (*portscp & ~Psenable) | Psreset;	/* initiate reset */
165484860c5dSDavid du Colombier 	coherence();
165584860c5dSDavid du Colombier 
16565a5ba3fdSDavid du Colombier 	/*
16575a5ba3fdSDavid du Colombier 	 * usb 2 spec: reset must finish within 20 ms.
16585a5ba3fdSDavid du Colombier 	 * linux says spec says it can take 50 ms. for hubs.
16595a5ba3fdSDavid du Colombier 	 */
1660afa28516SDavid du Colombier 	for(i = 0; *portscp & Psreset && i < 50; i++)
166184860c5dSDavid du Colombier 		delay(10);
16625a5ba3fdSDavid du Colombier 	if (*portscp & Psreset)
16635a5ba3fdSDavid du Colombier 		iprint("ehci %#p: port %d didn't reset within %d ms; sts %#lux\n",
16645a5ba3fdSDavid du Colombier 			ctlr->capio, port, i * 10, *portscp);
16655a5ba3fdSDavid du Colombier 	*portscp &= ~Psreset;		/* force appearance of reset done */
166684860c5dSDavid du Colombier 	coherence();
16675a5ba3fdSDavid du Colombier 	delay(10);			/* ehci spec: enable within 2 ms. */
166884860c5dSDavid du Colombier 
16695a5ba3fdSDavid du Colombier 	if((*portscp & Psenable) == 0)
167084860c5dSDavid du Colombier 		portlend(ctlr, port, "full");
167184860c5dSDavid du Colombier 
167284860c5dSDavid du Colombier 	iunlock(ctlr);
167384860c5dSDavid du Colombier 	dprint("ehci %#p after port %d reset; sts %#lux\n",
16745a5ba3fdSDavid du Colombier 		ctlr->capio, port, *portscp);
167584860c5dSDavid du Colombier 	qunlock(&ctlr->portlck);
167684860c5dSDavid du Colombier 	poperror();
167784860c5dSDavid du Colombier 	return 0;
167884860c5dSDavid du Colombier }
167984860c5dSDavid du Colombier 
168084860c5dSDavid du Colombier static int
portstatus(Hci * hp,int port)168184860c5dSDavid du Colombier portstatus(Hci *hp, int port)
168284860c5dSDavid du Colombier {
168384860c5dSDavid du Colombier 	int s, r;
168484860c5dSDavid du Colombier 	Eopio *opio;
168584860c5dSDavid du Colombier 	Ctlr *ctlr;
168684860c5dSDavid du Colombier 
168784860c5dSDavid du Colombier 	ctlr = hp->aux;
168884860c5dSDavid du Colombier 	opio = ctlr->opio;
168984860c5dSDavid du Colombier 	qlock(&ctlr->portlck);
169084860c5dSDavid du Colombier 	if(waserror()){
169184860c5dSDavid du Colombier 		iunlock(ctlr);
169284860c5dSDavid du Colombier 		qunlock(&ctlr->portlck);
169384860c5dSDavid du Colombier 		nexterror();
169484860c5dSDavid du Colombier 	}
169584860c5dSDavid du Colombier 	ilock(ctlr);
169684860c5dSDavid du Colombier 	s = opio->portsc[port-1];
169784860c5dSDavid du Colombier 	if(s & (Psstatuschg | Pschange)){
169884860c5dSDavid du Colombier 		opio->portsc[port-1] = s;
169984860c5dSDavid du Colombier 		coherence();
170084860c5dSDavid du Colombier 		ddprint("ehci %#p port %d status %#x\n", ctlr->capio, port, s);
170184860c5dSDavid du Colombier 	}
170284860c5dSDavid du Colombier 	/*
170384860c5dSDavid du Colombier 	 * If the port is a low speed port we yield ownership now
170484860c5dSDavid du Colombier 	 * to the [uo]hci companion controller and pretend it's not here.
170584860c5dSDavid du Colombier 	 */
170684860c5dSDavid du Colombier 	if((s & Pspresent) != 0 && (s & Pslinemask) == Pslow){
170784860c5dSDavid du Colombier 		portlend(ctlr, port, "low");
170884860c5dSDavid du Colombier 		s &= ~Pspresent;		/* not for us this time */
170984860c5dSDavid du Colombier 	}
171084860c5dSDavid du Colombier 	iunlock(ctlr);
171184860c5dSDavid du Colombier 	qunlock(&ctlr->portlck);
171284860c5dSDavid du Colombier 	poperror();
171384860c5dSDavid du Colombier 
171484860c5dSDavid du Colombier 	/*
171584860c5dSDavid du Colombier 	 * We must return status bits as a
171684860c5dSDavid du Colombier 	 * get port status hub request would do.
171784860c5dSDavid du Colombier 	 */
171884860c5dSDavid du Colombier 	r = 0;
171984860c5dSDavid du Colombier 	if(s & Pspresent)
172084860c5dSDavid du Colombier 		r |= HPpresent|HPhigh;
172184860c5dSDavid du Colombier 	if(s & Psenable)
172284860c5dSDavid du Colombier 		r |= HPenable;
172384860c5dSDavid du Colombier 	if(s & Pssuspend)
172484860c5dSDavid du Colombier 		r |= HPsuspend;
172584860c5dSDavid du Colombier 	if(s & Psreset)
172684860c5dSDavid du Colombier 		r |= HPreset;
172784860c5dSDavid du Colombier 	if(s & Psstatuschg)
172884860c5dSDavid du Colombier 		r |= HPstatuschg;
172984860c5dSDavid du Colombier 	if(s & Pschange)
173084860c5dSDavid du Colombier 		r |= HPchange;
173184860c5dSDavid du Colombier 	return r;
173284860c5dSDavid du Colombier }
173384860c5dSDavid du Colombier 
173484860c5dSDavid du Colombier static char*
seprintio(char * s,char * e,Qio * io,char * pref)173584860c5dSDavid du Colombier seprintio(char *s, char *e, Qio *io, char *pref)
173684860c5dSDavid du Colombier {
173784860c5dSDavid du Colombier 	s = seprint(s,e,"%s io %#p qh %#p id %#x", pref, io, io->qh, io->usbid);
173884860c5dSDavid du Colombier 	s = seprint(s,e," iot %ld", io->iotime);
173984860c5dSDavid du Colombier 	s = seprint(s,e," tog %#x tok %#x err %s", io->toggle, io->tok, io->err);
174084860c5dSDavid du Colombier 	return s;
174184860c5dSDavid du Colombier }
174284860c5dSDavid du Colombier 
174384860c5dSDavid du Colombier static char*
seprintep(char * s,char * e,Ep * ep)174484860c5dSDavid du Colombier seprintep(char *s, char *e, Ep *ep)
174584860c5dSDavid du Colombier {
174684860c5dSDavid du Colombier 	Qio *io;
174784860c5dSDavid du Colombier 	Ctlio *cio;
174884860c5dSDavid du Colombier 	Ctlr *ctlr;
174984860c5dSDavid du Colombier 
175084860c5dSDavid du Colombier 	ctlr = ep->hp->aux;
175184860c5dSDavid du Colombier 	ilock(ctlr);
175284860c5dSDavid du Colombier 	if(ep->aux == nil){
175384860c5dSDavid du Colombier 		*s = 0;
175484860c5dSDavid du Colombier 		iunlock(ctlr);
175584860c5dSDavid du Colombier 		return s;
175684860c5dSDavid du Colombier 	}
175784860c5dSDavid du Colombier 	switch(ep->ttype){
175884860c5dSDavid du Colombier 	case Tctl:
175984860c5dSDavid du Colombier 		cio = ep->aux;
176084860c5dSDavid du Colombier 		s = seprintio(s, e, cio, "c");
176184860c5dSDavid du Colombier 		s = seprint(s, e, "\trepl %d ndata %d\n", ep->rhrepl, cio->ndata);
176284860c5dSDavid du Colombier 		break;
176384860c5dSDavid du Colombier 	case Tbulk:
176484860c5dSDavid du Colombier 	case Tintr:
176584860c5dSDavid du Colombier 		io = ep->aux;
176684860c5dSDavid du Colombier 		if(ep->mode != OWRITE)
176784860c5dSDavid du Colombier 			s = seprintio(s, e, &io[OREAD], "r");
176884860c5dSDavid du Colombier 		if(ep->mode != OREAD)
176984860c5dSDavid du Colombier 			s = seprintio(s, e, &io[OWRITE], "w");
177084860c5dSDavid du Colombier 		break;
177184860c5dSDavid du Colombier 	case Tiso:
177284860c5dSDavid du Colombier 		*s = 0;
177384860c5dSDavid du Colombier 		break;
177484860c5dSDavid du Colombier 	}
177584860c5dSDavid du Colombier 	iunlock(ctlr);
177684860c5dSDavid du Colombier 	return s;
177784860c5dSDavid du Colombier }
177884860c5dSDavid du Colombier 
177984860c5dSDavid du Colombier /*
178084860c5dSDavid du Colombier  * halt condition was cleared on the endpoint. update our toggles.
178184860c5dSDavid du Colombier  */
178284860c5dSDavid du Colombier static void
clrhalt(Ep * ep)178384860c5dSDavid du Colombier clrhalt(Ep *ep)
178484860c5dSDavid du Colombier {
178584860c5dSDavid du Colombier 	Qio *io;
178684860c5dSDavid du Colombier 
178784860c5dSDavid du Colombier 	ep->clrhalt = 0;
178884860c5dSDavid du Colombier 	coherence();
178984860c5dSDavid du Colombier 	switch(ep->ttype){
179084860c5dSDavid du Colombier 	case Tintr:
179184860c5dSDavid du Colombier 	case Tbulk:
179284860c5dSDavid du Colombier 		io = ep->aux;
179384860c5dSDavid du Colombier 		if(ep->mode != OREAD){
179484860c5dSDavid du Colombier 			qlock(&io[OWRITE]);
179584860c5dSDavid du Colombier 			io[OWRITE].toggle = Tddata0;
179684860c5dSDavid du Colombier 			deprint("ep clrhalt for io %#p\n", io+OWRITE);
179784860c5dSDavid du Colombier 			qunlock(&io[OWRITE]);
179884860c5dSDavid du Colombier 		}
179984860c5dSDavid du Colombier 		if(ep->mode != OWRITE){
180084860c5dSDavid du Colombier 			qlock(&io[OREAD]);
180184860c5dSDavid du Colombier 			io[OREAD].toggle = Tddata0;
180284860c5dSDavid du Colombier 			deprint("ep clrhalt for io %#p\n", io+OREAD);
180384860c5dSDavid du Colombier 			qunlock(&io[OREAD]);
180484860c5dSDavid du Colombier 		}
180584860c5dSDavid du Colombier 		break;
180684860c5dSDavid du Colombier 	}
180784860c5dSDavid du Colombier }
180884860c5dSDavid du Colombier 
180984860c5dSDavid du Colombier static void
xdump(char * pref,void * qh)181084860c5dSDavid du Colombier xdump(char* pref, void *qh)
181184860c5dSDavid du Colombier {
181284860c5dSDavid du Colombier 	int i;
181384860c5dSDavid du Colombier 	ulong *u;
181484860c5dSDavid du Colombier 
181584860c5dSDavid du Colombier 	u = qh;
181684860c5dSDavid du Colombier 	print("%s %#p:", pref, u);
181784860c5dSDavid du Colombier 	for(i = 0; i < 16; i++)
181884860c5dSDavid du Colombier 		if((i%4) == 0)
181984860c5dSDavid du Colombier 			print("\n %#8.8ulx", u[i]);
182084860c5dSDavid du Colombier 		else
182184860c5dSDavid du Colombier 			print(" %#8.8ulx", u[i]);
182284860c5dSDavid du Colombier 	print("\n");
182384860c5dSDavid du Colombier }
182484860c5dSDavid du Colombier 
182584860c5dSDavid du Colombier static long
episohscpy(Ctlr * ctlr,Ep * ep,Isoio * iso,uchar * b,long count)182684860c5dSDavid du Colombier episohscpy(Ctlr *ctlr, Ep *ep, Isoio* iso, uchar *b, long count)
182784860c5dSDavid du Colombier {
182884860c5dSDavid du Colombier 	int nr;
182984860c5dSDavid du Colombier 	long tot;
183084860c5dSDavid du Colombier 	Itd *tdu;
183184860c5dSDavid du Colombier 
183284860c5dSDavid du Colombier 	for(tot = 0; iso->tdi != iso->tdu && tot < count; tot += nr){
183384860c5dSDavid du Colombier 		tdu = iso->tdu;
183484860c5dSDavid du Colombier 		if(itdactive(tdu))
183584860c5dSDavid du Colombier 			break;
183684860c5dSDavid du Colombier 		nr = tdu->ndata;
183784860c5dSDavid du Colombier 		if(tot + nr > count)
183884860c5dSDavid du Colombier 			nr = count - tot;
183984860c5dSDavid du Colombier 		if(nr == 0)
184084860c5dSDavid du Colombier 			print("ehci: ep%d.%d: too many polls\n",
184184860c5dSDavid du Colombier 				ep->dev->nb, ep->nb);
184284860c5dSDavid du Colombier 		else{
184384860c5dSDavid du Colombier 			iunlock(ctlr);		/* We could page fault here */
184484860c5dSDavid du Colombier 			memmove(b+tot, tdu->data, nr);
184584860c5dSDavid du Colombier 			ilock(ctlr);
184684860c5dSDavid du Colombier 			if(nr < tdu->ndata)
184784860c5dSDavid du Colombier 				memmove(tdu->data, tdu->data+nr, tdu->ndata - nr);
184884860c5dSDavid du Colombier 			tdu->ndata -= nr;
184984860c5dSDavid du Colombier 			coherence();
185084860c5dSDavid du Colombier 		}
185184860c5dSDavid du Colombier 		if(tdu->ndata == 0){
185284860c5dSDavid du Colombier 			itdinit(iso, tdu);
185384860c5dSDavid du Colombier 			iso->tdu = tdu->next;
185484860c5dSDavid du Colombier 		}
185584860c5dSDavid du Colombier 	}
185684860c5dSDavid du Colombier 	return tot;
185784860c5dSDavid du Colombier }
185884860c5dSDavid du Colombier 
185984860c5dSDavid du Colombier static long
episofscpy(Ctlr * ctlr,Ep * ep,Isoio * iso,uchar * b,long count)186084860c5dSDavid du Colombier episofscpy(Ctlr *ctlr, Ep *ep, Isoio* iso, uchar *b, long count)
186184860c5dSDavid du Colombier {
186284860c5dSDavid du Colombier 	int nr;
186384860c5dSDavid du Colombier 	long tot;
186484860c5dSDavid du Colombier 	Sitd *stdu;
186584860c5dSDavid du Colombier 
186684860c5dSDavid du Colombier 	for(tot = 0; iso->stdi != iso->stdu && tot < count; tot += nr){
186784860c5dSDavid du Colombier 		stdu = iso->stdu;
186884860c5dSDavid du Colombier 		if(stdu->csw & Stdactive){
186984860c5dSDavid du Colombier 			diprint("ehci: episoread: %#p tdu active\n", iso);
187084860c5dSDavid du Colombier 			break;
187184860c5dSDavid du Colombier 		}
187284860c5dSDavid du Colombier 		nr = stdu->ndata;
187384860c5dSDavid du Colombier 		if(tot + nr > count)
187484860c5dSDavid du Colombier 			nr = count - tot;
187584860c5dSDavid du Colombier 		if(nr == 0)
187684860c5dSDavid du Colombier 			print("ehci: ep%d.%d: too many polls\n",
187784860c5dSDavid du Colombier 				ep->dev->nb, ep->nb);
187884860c5dSDavid du Colombier 		else{
187984860c5dSDavid du Colombier 			iunlock(ctlr);		/* We could page fault here */
188084860c5dSDavid du Colombier 			memmove(b+tot, stdu->data, nr);
188184860c5dSDavid du Colombier 			ilock(ctlr);
188284860c5dSDavid du Colombier 			if(nr < stdu->ndata)
188384860c5dSDavid du Colombier 				memmove(stdu->data, stdu->data+nr,
188484860c5dSDavid du Colombier 					stdu->ndata - nr);
188584860c5dSDavid du Colombier 			stdu->ndata -= nr;
188684860c5dSDavid du Colombier 			coherence();
188784860c5dSDavid du Colombier 		}
188884860c5dSDavid du Colombier 		if(stdu->ndata == 0){
188984860c5dSDavid du Colombier 			sitdinit(iso, stdu);
189084860c5dSDavid du Colombier 			iso->stdu = stdu->next;
189184860c5dSDavid du Colombier 		}
189284860c5dSDavid du Colombier 	}
189384860c5dSDavid du Colombier 	return tot;
189484860c5dSDavid du Colombier }
189584860c5dSDavid du Colombier 
189684860c5dSDavid du Colombier static long
episoread(Ep * ep,Isoio * iso,void * a,long count)189784860c5dSDavid du Colombier episoread(Ep *ep, Isoio *iso, void *a, long count)
189884860c5dSDavid du Colombier {
189984860c5dSDavid du Colombier 	Ctlr *ctlr;
190084860c5dSDavid du Colombier 	uchar *b;
190184860c5dSDavid du Colombier 	long tot;
190284860c5dSDavid du Colombier 
190384860c5dSDavid du Colombier 	iso->debug = ep->debug;
190484860c5dSDavid du Colombier 	diprint("ehci: episoread: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
190584860c5dSDavid du Colombier 
190684860c5dSDavid du Colombier 	b = a;
190784860c5dSDavid du Colombier 	ctlr = ep->hp->aux;
190884860c5dSDavid du Colombier 	qlock(iso);
190984860c5dSDavid du Colombier 	if(waserror()){
191084860c5dSDavid du Colombier 		qunlock(iso);
191184860c5dSDavid du Colombier 		nexterror();
191284860c5dSDavid du Colombier 	}
191384860c5dSDavid du Colombier 	iso->err = nil;
191484860c5dSDavid du Colombier 	iso->nerrs = 0;
191584860c5dSDavid du Colombier 	ilock(ctlr);
191684860c5dSDavid du Colombier 	if(iso->state == Qclose){
191784860c5dSDavid du Colombier 		iunlock(ctlr);
191884860c5dSDavid du Colombier 		error(iso->err ? iso->err : Eio);
191984860c5dSDavid du Colombier 	}
192084860c5dSDavid du Colombier 	iso->state = Qrun;
192184860c5dSDavid du Colombier 	coherence();
192284860c5dSDavid du Colombier 	while(isocanread(iso) == 0){
192384860c5dSDavid du Colombier 		iunlock(ctlr);
192484860c5dSDavid du Colombier 		diprint("ehci: episoread: %#p sleep\n", iso);
192584860c5dSDavid du Colombier 		if(waserror()){
192684860c5dSDavid du Colombier 			if(iso->err == nil)
192784860c5dSDavid du Colombier 				iso->err = "I/O timed out";
192884860c5dSDavid du Colombier 			ilock(ctlr);
192984860c5dSDavid du Colombier 			break;
193084860c5dSDavid du Colombier 		}
193184860c5dSDavid du Colombier 		tsleep(iso, isocanread, iso, ep->tmout);
193284860c5dSDavid du Colombier 		poperror();
193384860c5dSDavid du Colombier 		ilock(ctlr);
193484860c5dSDavid du Colombier 	}
193584860c5dSDavid du Colombier 	if(iso->state == Qclose){
193684860c5dSDavid du Colombier 		iunlock(ctlr);
193784860c5dSDavid du Colombier 		error(iso->err ? iso->err : Eio);
193884860c5dSDavid du Colombier 	}
193984860c5dSDavid du Colombier 	iso->state = Qdone;
194084860c5dSDavid du Colombier 	coherence();
194184860c5dSDavid du Colombier 	assert(iso->tdu != iso->tdi);
194284860c5dSDavid du Colombier 
194384860c5dSDavid du Colombier 	if(iso->hs != 0)
194484860c5dSDavid du Colombier 		tot = episohscpy(ctlr, ep, iso, b, count);
194584860c5dSDavid du Colombier 	else
194684860c5dSDavid du Colombier 		tot = episofscpy(ctlr, ep, iso, b, count);
194784860c5dSDavid du Colombier 	iunlock(ctlr);
194884860c5dSDavid du Colombier 	qunlock(iso);
194984860c5dSDavid du Colombier 	poperror();
195084860c5dSDavid du Colombier 	diprint("uhci: episoread: %#p %uld bytes err '%s'\n", iso, tot, iso->err);
195184860c5dSDavid du Colombier 	if(iso->err != nil)
195284860c5dSDavid du Colombier 		error(iso->err);
195384860c5dSDavid du Colombier 	return tot;
195484860c5dSDavid du Colombier }
195584860c5dSDavid du Colombier 
195684860c5dSDavid du Colombier /*
195784860c5dSDavid du Colombier  * iso->tdu is the next place to put data. When it gets full
195884860c5dSDavid du Colombier  * it is activated and tdu advanced.
195984860c5dSDavid du Colombier  */
196084860c5dSDavid du Colombier static long
putsamples(Isoio * iso,uchar * b,long count)196184860c5dSDavid du Colombier putsamples(Isoio *iso, uchar *b, long count)
196284860c5dSDavid du Colombier {
196384860c5dSDavid du Colombier 	long tot, n;
196484860c5dSDavid du Colombier 
196584860c5dSDavid du Colombier 	for(tot = 0; isocanwrite(iso) && tot < count; tot += n){
196684860c5dSDavid du Colombier 		n = count-tot;
196784860c5dSDavid du Colombier 		if(iso->hs != 0){
196884860c5dSDavid du Colombier 			if(n > iso->tdu->mdata - iso->nleft)
196984860c5dSDavid du Colombier 				n = iso->tdu->mdata - iso->nleft;
197084860c5dSDavid du Colombier 			memmove(iso->tdu->data + iso->nleft, b + tot, n);
197184860c5dSDavid du Colombier 			coherence();
197284860c5dSDavid du Colombier 			iso->nleft += n;
197384860c5dSDavid du Colombier 			if(iso->nleft == iso->tdu->mdata){
197484860c5dSDavid du Colombier 				itdinit(iso, iso->tdu);
197584860c5dSDavid du Colombier 				iso->nleft = 0;
197684860c5dSDavid du Colombier 				iso->tdu = iso->tdu->next;
197784860c5dSDavid du Colombier 			}
197884860c5dSDavid du Colombier 		}else{
197984860c5dSDavid du Colombier 			if(n > iso->stdu->mdata - iso->nleft)
198084860c5dSDavid du Colombier 				n = iso->stdu->mdata - iso->nleft;
198184860c5dSDavid du Colombier 			memmove(iso->stdu->data + iso->nleft, b + tot, n);
198284860c5dSDavid du Colombier 			coherence();
198384860c5dSDavid du Colombier 			iso->nleft += n;
198484860c5dSDavid du Colombier 			if(iso->nleft == iso->stdu->mdata){
198584860c5dSDavid du Colombier 				sitdinit(iso, iso->stdu);
198684860c5dSDavid du Colombier 				iso->nleft = 0;
198784860c5dSDavid du Colombier 				iso->stdu = iso->stdu->next;
198884860c5dSDavid du Colombier 			}
198984860c5dSDavid du Colombier 		}
199084860c5dSDavid du Colombier 	}
199184860c5dSDavid du Colombier 	return tot;
199284860c5dSDavid du Colombier }
199384860c5dSDavid du Colombier 
199484860c5dSDavid du Colombier /*
199584860c5dSDavid du Colombier  * Queue data for writing and return error status from
199684860c5dSDavid du Colombier  * last writes done, to maintain buffered data.
199784860c5dSDavid du Colombier  */
199884860c5dSDavid du Colombier static long
episowrite(Ep * ep,Isoio * iso,void * a,long count)199984860c5dSDavid du Colombier episowrite(Ep *ep, Isoio *iso, void *a, long count)
200084860c5dSDavid du Colombier {
200184860c5dSDavid du Colombier 	Ctlr *ctlr;
200284860c5dSDavid du Colombier 	uchar *b;
200384860c5dSDavid du Colombier 	int tot, nw;
200484860c5dSDavid du Colombier 	char *err;
200584860c5dSDavid du Colombier 
200684860c5dSDavid du Colombier 	iso->debug = ep->debug;
200784860c5dSDavid du Colombier 	diprint("ehci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
200884860c5dSDavid du Colombier 
200984860c5dSDavid du Colombier 	ctlr = ep->hp->aux;
201084860c5dSDavid du Colombier 	qlock(iso);
201184860c5dSDavid du Colombier 	if(waserror()){
201284860c5dSDavid du Colombier 		qunlock(iso);
201384860c5dSDavid du Colombier 		nexterror();
201484860c5dSDavid du Colombier 	}
201584860c5dSDavid du Colombier 	ilock(ctlr);
201684860c5dSDavid du Colombier 	if(iso->state == Qclose){
201784860c5dSDavid du Colombier 		iunlock(ctlr);
201884860c5dSDavid du Colombier 		error(iso->err ? iso->err : Eio);
201984860c5dSDavid du Colombier 	}
202084860c5dSDavid du Colombier 	iso->state = Qrun;
202184860c5dSDavid du Colombier 	coherence();
202284860c5dSDavid du Colombier 	b = a;
202384860c5dSDavid du Colombier 	for(tot = 0; tot < count; tot += nw){
202484860c5dSDavid du Colombier 		while(isocanwrite(iso) == 0){
202584860c5dSDavid du Colombier 			iunlock(ctlr);
202684860c5dSDavid du Colombier 			diprint("ehci: episowrite: %#p sleep\n", iso);
202784860c5dSDavid du Colombier 			if(waserror()){
202884860c5dSDavid du Colombier 				if(iso->err == nil)
202984860c5dSDavid du Colombier 					iso->err = "I/O timed out";
203084860c5dSDavid du Colombier 				ilock(ctlr);
203184860c5dSDavid du Colombier 				break;
203284860c5dSDavid du Colombier 			}
203384860c5dSDavid du Colombier 			tsleep(iso, isocanwrite, iso, ep->tmout);
203484860c5dSDavid du Colombier 			poperror();
203584860c5dSDavid du Colombier 			ilock(ctlr);
203684860c5dSDavid du Colombier 		}
203784860c5dSDavid du Colombier 		err = iso->err;
203884860c5dSDavid du Colombier 		iso->err = nil;
203984860c5dSDavid du Colombier 		if(iso->state == Qclose || err != nil){
204084860c5dSDavid du Colombier 			iunlock(ctlr);
204184860c5dSDavid du Colombier 			error(err ? err : Eio);
204284860c5dSDavid du Colombier 		}
204384860c5dSDavid du Colombier 		if(iso->state != Qrun)
204484860c5dSDavid du Colombier 			panic("episowrite: iso not running");
204584860c5dSDavid du Colombier 		iunlock(ctlr);		/* We could page fault here */
204684860c5dSDavid du Colombier 		nw = putsamples(iso, b+tot, count-tot);
204784860c5dSDavid du Colombier 		ilock(ctlr);
204884860c5dSDavid du Colombier 	}
204984860c5dSDavid du Colombier 	if(iso->state != Qclose)
205084860c5dSDavid du Colombier 		iso->state = Qdone;
205184860c5dSDavid du Colombier 	iunlock(ctlr);
205284860c5dSDavid du Colombier 	err = iso->err;		/* in case it failed early */
205384860c5dSDavid du Colombier 	iso->err = nil;
205484860c5dSDavid du Colombier 	qunlock(iso);
205584860c5dSDavid du Colombier 	poperror();
205684860c5dSDavid du Colombier 	if(err != nil)
205784860c5dSDavid du Colombier 		error(err);
205884860c5dSDavid du Colombier 	diprint("ehci: episowrite: %#p %d bytes\n", iso, tot);
205984860c5dSDavid du Colombier 	return tot;
206084860c5dSDavid du Colombier }
206184860c5dSDavid du Colombier 
206284860c5dSDavid du Colombier static int
nexttoggle(int toggle,int count,int maxpkt)206384860c5dSDavid du Colombier nexttoggle(int toggle, int count, int maxpkt)
206484860c5dSDavid du Colombier {
206584860c5dSDavid du Colombier 	int np;
206684860c5dSDavid du Colombier 
206784860c5dSDavid du Colombier 	np = count / maxpkt;
206884860c5dSDavid du Colombier 	if(np == 0)
206984860c5dSDavid du Colombier 		np = 1;
207084860c5dSDavid du Colombier 	if((np % 2) == 0)
207184860c5dSDavid du Colombier 		return toggle;
207284860c5dSDavid du Colombier 	if(toggle == Tddata1)
207384860c5dSDavid du Colombier 		return Tddata0;
207484860c5dSDavid du Colombier 	else
207584860c5dSDavid du Colombier 		return Tddata1;
207684860c5dSDavid du Colombier }
207784860c5dSDavid du Colombier 
207884860c5dSDavid du Colombier static Td*
epgettd(Qio * io,int flags,void * a,int count,int maxpkt)207984860c5dSDavid du Colombier epgettd(Qio *io, int flags, void *a, int count, int maxpkt)
208084860c5dSDavid du Colombier {
208184860c5dSDavid du Colombier 	Td *td;
208284860c5dSDavid du Colombier 	ulong pa;
208384860c5dSDavid du Colombier 	int i;
208484860c5dSDavid du Colombier 
208584860c5dSDavid du Colombier 	if(count > Tdmaxpkt)
208684860c5dSDavid du Colombier 		panic("ehci: epgettd: too many bytes");
208784860c5dSDavid du Colombier 	td = tdalloc();
208884860c5dSDavid du Colombier 	td->csw = flags | io->toggle | io->tok | count << Tdlenshift |
208984860c5dSDavid du Colombier 		Tderr2 | Tderr1;
209084860c5dSDavid du Colombier 
209184860c5dSDavid du Colombier 	/*
209284860c5dSDavid du Colombier 	 * use the space wasted by alignment as an
209384860c5dSDavid du Colombier 	 * embedded buffer if count bytes fit in there.
209484860c5dSDavid du Colombier 	 */
209584860c5dSDavid du Colombier 	assert(Align > sizeof(Td));
209684860c5dSDavid du Colombier 	if(count <= Align - sizeof(Td)){
209784860c5dSDavid du Colombier 		td->data = td->sbuff;
209884860c5dSDavid du Colombier 		td->buff = nil;
209984860c5dSDavid du Colombier 	}else
210084860c5dSDavid du Colombier 		td->data = td->buff = smalloc(Tdmaxpkt);
210184860c5dSDavid du Colombier 
210284860c5dSDavid du Colombier 	pa = PADDR(td->data);
210384860c5dSDavid du Colombier 	for(i = 0; i < nelem(td->buffer); i++){
210484860c5dSDavid du Colombier 		td->buffer[i] = pa;
210584860c5dSDavid du Colombier 		if(i > 0)
210684860c5dSDavid du Colombier 			td->buffer[i] &= ~0xFFF;
210784860c5dSDavid du Colombier 		pa += 0x1000;
210884860c5dSDavid du Colombier 	}
210984860c5dSDavid du Colombier 	td->ndata = count;
211084860c5dSDavid du Colombier 	if(a != nil && count > 0)
211184860c5dSDavid du Colombier 		memmove(td->data, a, count);
211284860c5dSDavid du Colombier 	coherence();
211384860c5dSDavid du Colombier 	io->toggle = nexttoggle(io->toggle, count, maxpkt);
211484860c5dSDavid du Colombier 	coherence();
211584860c5dSDavid du Colombier 	return td;
211684860c5dSDavid du Colombier }
211784860c5dSDavid du Colombier 
211884860c5dSDavid du Colombier /*
211984860c5dSDavid du Colombier  * Try to get them idle
212084860c5dSDavid du Colombier  */
212184860c5dSDavid du Colombier static void
aborttds(Qh * qh)212284860c5dSDavid du Colombier aborttds(Qh *qh)
212384860c5dSDavid du Colombier {
212484860c5dSDavid du Colombier 	Td *td;
212584860c5dSDavid du Colombier 
212684860c5dSDavid du Colombier 	qh->state = Qdone;
212784860c5dSDavid du Colombier 	coherence();
212884860c5dSDavid du Colombier 	if(qh->sched >= 0 && (qh->eps0 & Qhspeedmask) != Qhhigh)
212984860c5dSDavid du Colombier 		qh->eps0 |= Qhint;	/* inactivate on next pass */
213084860c5dSDavid du Colombier 	coherence();
213184860c5dSDavid du Colombier 	for(td = qh->tds; td != nil; td = td->next){
213284860c5dSDavid du Colombier 		if(td->csw & Tdactive)
213384860c5dSDavid du Colombier 			td->ndata = 0;
213484860c5dSDavid du Colombier 		td->csw |= Tdhalt;
213584860c5dSDavid du Colombier 		coherence();
213684860c5dSDavid du Colombier 	}
213784860c5dSDavid du Colombier }
213884860c5dSDavid du Colombier 
213984860c5dSDavid du Colombier /*
214084860c5dSDavid du Colombier  * Some controllers do not post the usb/error interrupt after
214184860c5dSDavid du Colombier  * the work has been done. It seems that we must poll for them.
214284860c5dSDavid du Colombier  */
214384860c5dSDavid du Colombier static int
workpending(void * a)214484860c5dSDavid du Colombier workpending(void *a)
214584860c5dSDavid du Colombier {
214684860c5dSDavid du Colombier 	Ctlr *ctlr;
214784860c5dSDavid du Colombier 
214884860c5dSDavid du Colombier 	ctlr = a;
214984860c5dSDavid du Colombier 	return ctlr->nreqs > 0;
215084860c5dSDavid du Colombier }
215184860c5dSDavid du Colombier 
215284860c5dSDavid du Colombier static void
ehcipoll(void * a)215384860c5dSDavid du Colombier ehcipoll(void* a)
215484860c5dSDavid du Colombier {
215584860c5dSDavid du Colombier 	Hci *hp;
215684860c5dSDavid du Colombier 	Ctlr *ctlr;
215784860c5dSDavid du Colombier 	Poll *poll;
215884860c5dSDavid du Colombier 	int i;
215984860c5dSDavid du Colombier 
216084860c5dSDavid du Colombier 	hp = a;
216184860c5dSDavid du Colombier 	ctlr = hp->aux;
216284860c5dSDavid du Colombier 	poll = &ctlr->poll;
216384860c5dSDavid du Colombier 	for(;;){
216484860c5dSDavid du Colombier 		if(ctlr->nreqs == 0){
216584860c5dSDavid du Colombier 			if(0)ddprint("ehcipoll %#p sleep\n", ctlr->capio);
216684860c5dSDavid du Colombier 			sleep(poll, workpending, ctlr);
216784860c5dSDavid du Colombier 			if(0)ddprint("ehcipoll %#p awaken\n", ctlr->capio);
216884860c5dSDavid du Colombier 		}
216984860c5dSDavid du Colombier 		for(i = 0; i < 16 && ctlr->nreqs > 0; i++)
217084860c5dSDavid du Colombier 			if(ehciintr(hp) == 0)
217184860c5dSDavid du Colombier 				 break;
217284860c5dSDavid du Colombier 		do{
217384860c5dSDavid du Colombier 			tsleep(&up->sleep, return0, 0, 1);
217484860c5dSDavid du Colombier 			ehciintr(hp);
217584860c5dSDavid du Colombier 		}while(ctlr->nreqs > 0);
217684860c5dSDavid du Colombier 	}
217784860c5dSDavid du Colombier }
217884860c5dSDavid du Colombier 
217984860c5dSDavid du Colombier static void
pollcheck(Hci * hp)218084860c5dSDavid du Colombier pollcheck(Hci *hp)
218184860c5dSDavid du Colombier {
218284860c5dSDavid du Colombier 	Ctlr *ctlr;
218384860c5dSDavid du Colombier 	Poll *poll;
218484860c5dSDavid du Colombier 
218584860c5dSDavid du Colombier 	ctlr = hp->aux;
218684860c5dSDavid du Colombier 	poll = &ctlr->poll;
218784860c5dSDavid du Colombier 
218884860c5dSDavid du Colombier 	if(poll->must != 0 && poll->does == 0){
218984860c5dSDavid du Colombier 		lock(poll);
219084860c5dSDavid du Colombier 		if(poll->must != 0 && poll->does == 0){
219184860c5dSDavid du Colombier 			poll->does++;
219284860c5dSDavid du Colombier 			print("ehci %#p: polling\n", ctlr->capio);
219384860c5dSDavid du Colombier 			kproc("ehcipoll", ehcipoll, hp);
219484860c5dSDavid du Colombier 		}
219584860c5dSDavid du Colombier 		unlock(poll);
219684860c5dSDavid du Colombier 	}
219784860c5dSDavid du Colombier }
219884860c5dSDavid du Colombier 
219984860c5dSDavid du Colombier static int
epiodone(void * a)220084860c5dSDavid du Colombier epiodone(void *a)
220184860c5dSDavid du Colombier {
220284860c5dSDavid du Colombier 	Qh *qh;
220384860c5dSDavid du Colombier 
220484860c5dSDavid du Colombier 	qh = a;
220584860c5dSDavid du Colombier 	return qh->state != Qrun;
220684860c5dSDavid du Colombier }
220784860c5dSDavid du Colombier 
220884860c5dSDavid du Colombier static void
epiowait(Hci * hp,Qio * io,int tmout,ulong load)220984860c5dSDavid du Colombier epiowait(Hci *hp, Qio *io, int tmout, ulong load)
221084860c5dSDavid du Colombier {
221184860c5dSDavid du Colombier 	Qh *qh;
221284860c5dSDavid du Colombier 	int timedout;
221384860c5dSDavid du Colombier 	Ctlr *ctlr;
221484860c5dSDavid du Colombier 
221584860c5dSDavid du Colombier 	ctlr = hp->aux;
221684860c5dSDavid du Colombier 	qh = io->qh;
22175a5ba3fdSDavid du Colombier 	ddqprint("ehci %#p: io %#p sleep on qh %#p state %s\n",
22185a5ba3fdSDavid du Colombier 		ctlr->capio, io, qh, qhsname[qh->state]);
221984860c5dSDavid du Colombier 	timedout = 0;
222084860c5dSDavid du Colombier 	if(waserror()){
22215a5ba3fdSDavid du Colombier 		dqprint("ehci %#p: io %#p qh %#p timed out\n",
22225a5ba3fdSDavid du Colombier 			ctlr->capio, io, qh);
222384860c5dSDavid du Colombier 		timedout++;
222484860c5dSDavid du Colombier 	}else{
222584860c5dSDavid du Colombier 		if(tmout == 0)
222684860c5dSDavid du Colombier 			sleep(io, epiodone, qh);
222784860c5dSDavid du Colombier 		else
222884860c5dSDavid du Colombier 			tsleep(io, epiodone, qh, tmout);
222984860c5dSDavid du Colombier 		poperror();
223084860c5dSDavid du Colombier 	}
223184860c5dSDavid du Colombier 
223284860c5dSDavid du Colombier 	ilock(ctlr);
223384860c5dSDavid du Colombier 	/* Are we missing interrupts? */
223484860c5dSDavid du Colombier 	if(qh->state == Qrun){
223584860c5dSDavid du Colombier 		iunlock(ctlr);
223684860c5dSDavid du Colombier 		ehciintr(hp);
223784860c5dSDavid du Colombier 		ilock(ctlr);
223884860c5dSDavid du Colombier 		if(qh->state == Qdone){
223984860c5dSDavid du Colombier 			dqprint("ehci %#p: polling required\n", ctlr->capio);
224084860c5dSDavid du Colombier 			ctlr->poll.must = 1;
224184860c5dSDavid du Colombier 			pollcheck(hp);
224284860c5dSDavid du Colombier 		}
224384860c5dSDavid du Colombier 	}
224484860c5dSDavid du Colombier 
224584860c5dSDavid du Colombier 	if(qh->state == Qrun){
22465a5ba3fdSDavid du Colombier //		dqprint("ehci %#p: io %#p qh %#p timed out (no intr?)\n",
22475a5ba3fdSDavid du Colombier 		iprint("ehci %#p: io %#p qh %#p timed out (no intr?)\n",
22485a5ba3fdSDavid du Colombier 			ctlr->capio, io, qh);
224984860c5dSDavid du Colombier 		timedout = 1;
225084860c5dSDavid du Colombier 	}else if(qh->state != Qdone && qh->state != Qclose)
225184860c5dSDavid du Colombier 		panic("ehci: epio: queue state %d", qh->state);
225284860c5dSDavid du Colombier 	if(timedout){
225384860c5dSDavid du Colombier 		aborttds(io->qh);
225484860c5dSDavid du Colombier 		io->err = "request timed out";
225584860c5dSDavid du Colombier 		iunlock(ctlr);
225684860c5dSDavid du Colombier 		if(!waserror()){
225784860c5dSDavid du Colombier 			tsleep(&up->sleep, return0, 0, Abortdelay);
225884860c5dSDavid du Colombier 			poperror();
225984860c5dSDavid du Colombier 		}
226084860c5dSDavid du Colombier 		ilock(ctlr);
226184860c5dSDavid du Colombier 	}
226284860c5dSDavid du Colombier 	if(qh->state != Qclose)
226384860c5dSDavid du Colombier 		qh->state = Qidle;
226484860c5dSDavid du Colombier 	coherence();
226584860c5dSDavid du Colombier 	qhlinktd(qh, nil);
226684860c5dSDavid du Colombier 	ctlr->load -= load;
226784860c5dSDavid du Colombier 	ctlr->nreqs--;
226884860c5dSDavid du Colombier 	iunlock(ctlr);
226984860c5dSDavid du Colombier }
227084860c5dSDavid du Colombier 
227184860c5dSDavid du Colombier /*
227284860c5dSDavid du Colombier  * Non iso I/O.
227384860c5dSDavid du Colombier  * To make it work for control transfers, the caller may
227484860c5dSDavid du Colombier  * lock the Qio for the entire control transfer.
227584860c5dSDavid du Colombier  */
227684860c5dSDavid du Colombier static long
epio(Ep * ep,Qio * io,void * a,long count,int mustlock)227784860c5dSDavid du Colombier epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
227884860c5dSDavid du Colombier {
227984860c5dSDavid du Colombier 	int saved, ntds, tmout;
228084860c5dSDavid du Colombier 	long n, tot;
228184860c5dSDavid du Colombier 	ulong load;
228284860c5dSDavid du Colombier 	char *err;
228384860c5dSDavid du Colombier 	char buf[128];
228484860c5dSDavid du Colombier 	uchar *c;
228584860c5dSDavid du Colombier 	Ctlr *ctlr;
228684860c5dSDavid du Colombier 	Qh* qh;
228784860c5dSDavid du Colombier 	Td *td, *ltd, *td0, *ntd;
228884860c5dSDavid du Colombier 
228984860c5dSDavid du Colombier 	qh = io->qh;
229084860c5dSDavid du Colombier 	ctlr = ep->hp->aux;
229184860c5dSDavid du Colombier 	io->debug = ep->debug;
229284860c5dSDavid du Colombier 	tmout = ep->tmout;
229384860c5dSDavid du Colombier 	ddeprint("epio: %s ep%d.%d io %#p count %ld load %uld\n",
229484860c5dSDavid du Colombier 		io->tok == Tdtokin ? "in" : "out",
229584860c5dSDavid du Colombier 		ep->dev->nb, ep->nb, io, count, ctlr->load);
229684860c5dSDavid du Colombier 	if((ehcidebug > 1 || ep->debug > 1) && io->tok != Tdtokin){
229784860c5dSDavid du Colombier 		seprintdata(buf, buf+sizeof(buf), a, count);
229884860c5dSDavid du Colombier 		print("echi epio: user data: %s\n", buf);
229984860c5dSDavid du Colombier 	}
230084860c5dSDavid du Colombier 	if(mustlock){
230184860c5dSDavid du Colombier 		qlock(io);
230284860c5dSDavid du Colombier 		if(waserror()){
230384860c5dSDavid du Colombier 			qunlock(io);
230484860c5dSDavid du Colombier 			nexterror();
230584860c5dSDavid du Colombier 		}
230684860c5dSDavid du Colombier 	}
230784860c5dSDavid du Colombier 	io->err = nil;
230884860c5dSDavid du Colombier 	ilock(ctlr);
230984860c5dSDavid du Colombier 	if(qh->state == Qclose){	/* Tds released by cancelio */
231084860c5dSDavid du Colombier 		iunlock(ctlr);
231184860c5dSDavid du Colombier 		error(io->err ? io->err : Eio);
231284860c5dSDavid du Colombier 	}
231384860c5dSDavid du Colombier 	if(qh->state != Qidle)
231484860c5dSDavid du Colombier 		panic("epio: qh not idle");
231584860c5dSDavid du Colombier 	qh->state = Qinstall;
231684860c5dSDavid du Colombier 	iunlock(ctlr);
231784860c5dSDavid du Colombier 
231884860c5dSDavid du Colombier 	c = a;
231984860c5dSDavid du Colombier 	td0 = ltd = nil;
232084860c5dSDavid du Colombier 	load = tot = 0;
232184860c5dSDavid du Colombier 	do{
232284860c5dSDavid du Colombier 		n = (Tdmaxpkt / ep->maxpkt) * ep->maxpkt;
232384860c5dSDavid du Colombier 		if(count-tot < n)
232484860c5dSDavid du Colombier 			n = count-tot;
2325d24fba2aSDavid du Colombier 		if(c != nil && io->tok != Tdtokin)
232684860c5dSDavid du Colombier 			td = epgettd(io, Tdactive, c+tot, n, ep->maxpkt);
232784860c5dSDavid du Colombier 		else
232884860c5dSDavid du Colombier 			td = epgettd(io, Tdactive, nil, n, ep->maxpkt);
232984860c5dSDavid du Colombier 		if(td0 == nil)
233084860c5dSDavid du Colombier 			td0 = td;
233184860c5dSDavid du Colombier 		else
233284860c5dSDavid du Colombier 			tdlinktd(ltd, td);
233384860c5dSDavid du Colombier 		ltd = td;
233484860c5dSDavid du Colombier 		tot += n;
233584860c5dSDavid du Colombier 		load += ep->load;
233684860c5dSDavid du Colombier 	}while(tot < count);
233784860c5dSDavid du Colombier 	if(td0 == nil || ltd == nil)
233884860c5dSDavid du Colombier 		panic("epio: no td");
233984860c5dSDavid du Colombier 
234084860c5dSDavid du Colombier 	ltd->csw |= Tdioc;		/* the last one interrupts */
234184860c5dSDavid du Colombier 	coherence();
234284860c5dSDavid du Colombier 
234384860c5dSDavid du Colombier 	ddeprint("ehci: load %uld ctlr load %uld\n", load, ctlr->load);
234484860c5dSDavid du Colombier 	if(ehcidebug > 1 || ep->debug > 1)
234584860c5dSDavid du Colombier 		dumptd(td0, "epio: put: ");
234684860c5dSDavid du Colombier 
234784860c5dSDavid du Colombier 	ilock(ctlr);
234884860c5dSDavid du Colombier 	if(qh->state != Qclose){
234984860c5dSDavid du Colombier 		io->iotime = TK2MS(MACHP(0)->ticks);
235084860c5dSDavid du Colombier 		qh->state = Qrun;
235184860c5dSDavid du Colombier 		coherence();
235284860c5dSDavid du Colombier 		qhlinktd(qh, td0);
235384860c5dSDavid du Colombier 		ctlr->nreqs++;
235484860c5dSDavid du Colombier 		ctlr->load += load;
235584860c5dSDavid du Colombier 	}
235684860c5dSDavid du Colombier 	iunlock(ctlr);
235784860c5dSDavid du Colombier 
235884860c5dSDavid du Colombier 	if(ctlr->poll.does)
235984860c5dSDavid du Colombier 		wakeup(&ctlr->poll);
236084860c5dSDavid du Colombier 
236184860c5dSDavid du Colombier 	epiowait(ep->hp, io, tmout, load);
236284860c5dSDavid du Colombier 	if(ehcidebug > 1 || ep->debug > 1){
236384860c5dSDavid du Colombier 		dumptd(td0, "epio: got: ");
236484860c5dSDavid du Colombier 		qhdump(qh);
236584860c5dSDavid du Colombier 	}
236684860c5dSDavid du Colombier 
236784860c5dSDavid du Colombier 	tot = 0;
236884860c5dSDavid du Colombier 	c = a;
236984860c5dSDavid du Colombier 	saved = 0;
237084860c5dSDavid du Colombier 	ntds = 0;
237184860c5dSDavid du Colombier 	for(td = td0; td != nil; td = ntd){
237284860c5dSDavid du Colombier 		ntds++;
237384860c5dSDavid du Colombier 		/*
237484860c5dSDavid du Colombier 		 * Use td tok, not io tok, because of setup packets.
23752a782499SDavid du Colombier 		 * Also, we must save the next toggle value from the
23762a782499SDavid du Colombier 		 * last completed Td (in case of a short packet, or
23772a782499SDavid du Colombier 		 * fewer than the requested number of packets in the
23782a782499SDavid du Colombier 		 * Td being transferred).
237984860c5dSDavid du Colombier 		 */
23802a782499SDavid du Colombier 		if(td->csw & (Tdhalt|Tdactive))
23812a782499SDavid du Colombier 			saved++;
23822a782499SDavid du Colombier 		else{
23832a782499SDavid du Colombier 			if(!saved){
238484860c5dSDavid du Colombier 				io->toggle = td->csw & Tddata1;
238584860c5dSDavid du Colombier 				coherence();
238684860c5dSDavid du Colombier 			}
238784860c5dSDavid du Colombier 			tot += td->ndata;
2388d24fba2aSDavid du Colombier 			if(c != nil && (td->csw & Tdtok) == Tdtokin && td->ndata > 0){
238984860c5dSDavid du Colombier 				memmove(c, td->data, td->ndata);
239084860c5dSDavid du Colombier 				c += td->ndata;
239184860c5dSDavid du Colombier 			}
239284860c5dSDavid du Colombier 		}
239384860c5dSDavid du Colombier 		ntd = td->next;
239484860c5dSDavid du Colombier 		tdfree(td);
239584860c5dSDavid du Colombier 	}
239684860c5dSDavid du Colombier 	err = io->err;
239784860c5dSDavid du Colombier 	if(mustlock){
239884860c5dSDavid du Colombier 		qunlock(io);
239984860c5dSDavid du Colombier 		poperror();
240084860c5dSDavid du Colombier 	}
240184860c5dSDavid du Colombier 	ddeprint("epio: io %#p: %d tds: return %ld err '%s'\n",
240284860c5dSDavid du Colombier 		io, ntds, tot, err);
240384860c5dSDavid du Colombier 	if(err == Estalled)
240484860c5dSDavid du Colombier 		return 0;	/* that's our convention */
240584860c5dSDavid du Colombier 	if(err != nil)
240684860c5dSDavid du Colombier 		error(err);
240784860c5dSDavid du Colombier 	if(tot < 0)
240884860c5dSDavid du Colombier 		error(Eio);
240984860c5dSDavid du Colombier 	return tot;
241084860c5dSDavid du Colombier }
241184860c5dSDavid du Colombier 
241284860c5dSDavid du Colombier static long
epread(Ep * ep,void * a,long count)241384860c5dSDavid du Colombier epread(Ep *ep, void *a, long count)
241484860c5dSDavid du Colombier {
241584860c5dSDavid du Colombier 	Ctlio *cio;
241684860c5dSDavid du Colombier 	Qio *io;
241784860c5dSDavid du Colombier 	Isoio *iso;
241884860c5dSDavid du Colombier 	char buf[160];
241984860c5dSDavid du Colombier 	ulong delta;
242084860c5dSDavid du Colombier 
242184860c5dSDavid du Colombier 	ddeprint("ehci: epread\n");
242284860c5dSDavid du Colombier 	if(ep->aux == nil)
242384860c5dSDavid du Colombier 		panic("epread: not open");
242484860c5dSDavid du Colombier 
242584860c5dSDavid du Colombier 	pollcheck(ep->hp);
242684860c5dSDavid du Colombier 
242784860c5dSDavid du Colombier 	switch(ep->ttype){
242884860c5dSDavid du Colombier 	case Tctl:
242984860c5dSDavid du Colombier 		cio = ep->aux;
243084860c5dSDavid du Colombier 		qlock(cio);
243184860c5dSDavid du Colombier 		if(waserror()){
243284860c5dSDavid du Colombier 			qunlock(cio);
243384860c5dSDavid du Colombier 			nexterror();
243484860c5dSDavid du Colombier 		}
243584860c5dSDavid du Colombier 		ddeprint("epread ctl ndata %d\n", cio->ndata);
243684860c5dSDavid du Colombier 		if(cio->ndata < 0)
243784860c5dSDavid du Colombier 			error("request expected");
243884860c5dSDavid du Colombier 		else if(cio->ndata == 0){
243984860c5dSDavid du Colombier 			cio->ndata = -1;
244084860c5dSDavid du Colombier 			count = 0;
244184860c5dSDavid du Colombier 		}else{
244284860c5dSDavid du Colombier 			if(count > cio->ndata)
244384860c5dSDavid du Colombier 				count = cio->ndata;
244484860c5dSDavid du Colombier 			if(count > 0)
244584860c5dSDavid du Colombier 				memmove(a, cio->data, count);
244684860c5dSDavid du Colombier 			/* BUG for big transfers */
244784860c5dSDavid du Colombier 			free(cio->data);
244884860c5dSDavid du Colombier 			cio->data = nil;
244984860c5dSDavid du Colombier 			cio->ndata = 0;	/* signal EOF next time */
245084860c5dSDavid du Colombier 		}
245184860c5dSDavid du Colombier 		qunlock(cio);
245284860c5dSDavid du Colombier 		poperror();
245384860c5dSDavid du Colombier 		if(ehcidebug>1 || ep->debug){
245484860c5dSDavid du Colombier 			seprintdata(buf, buf+sizeof(buf), a, count);
245584860c5dSDavid du Colombier 			print("epread: %s\n", buf);
245684860c5dSDavid du Colombier 		}
245784860c5dSDavid du Colombier 		return count;
245884860c5dSDavid du Colombier 	case Tbulk:
245984860c5dSDavid du Colombier 		io = ep->aux;
246084860c5dSDavid du Colombier 		if(ep->clrhalt)
246184860c5dSDavid du Colombier 			clrhalt(ep);
246284860c5dSDavid du Colombier 		return epio(ep, &io[OREAD], a, count, 1);
246384860c5dSDavid du Colombier 	case Tintr:
246484860c5dSDavid du Colombier 		io = ep->aux;
246584860c5dSDavid du Colombier 		delta = TK2MS(MACHP(0)->ticks) - io[OREAD].iotime + 1;
246684860c5dSDavid du Colombier 		if(delta < ep->pollival / 2)
246784860c5dSDavid du Colombier 			tsleep(&up->sleep, return0, 0, ep->pollival/2 - delta);
246884860c5dSDavid du Colombier 		if(ep->clrhalt)
246984860c5dSDavid du Colombier 			clrhalt(ep);
247084860c5dSDavid du Colombier 		return epio(ep, &io[OREAD], a, count, 1);
247184860c5dSDavid du Colombier 	case Tiso:
247284860c5dSDavid du Colombier 		iso = ep->aux;
247384860c5dSDavid du Colombier 		return episoread(ep, iso, a, count);
247484860c5dSDavid du Colombier 	}
247584860c5dSDavid du Colombier 	return -1;
247684860c5dSDavid du Colombier }
247784860c5dSDavid du Colombier 
247884860c5dSDavid du Colombier /*
247984860c5dSDavid du Colombier  * Control transfers are one setup write (data0)
248084860c5dSDavid du Colombier  * plus zero or more reads/writes (data1, data0, ...)
248184860c5dSDavid du Colombier  * plus a final write/read with data1 to ack.
248284860c5dSDavid du Colombier  * For both host to device and device to host we perform
248384860c5dSDavid du Colombier  * the entire transfer when the user writes the request,
248484860c5dSDavid du Colombier  * and keep any data read from the device for a later read.
248584860c5dSDavid du Colombier  * We call epio three times instead of placing all Tds at
248684860c5dSDavid du Colombier  * the same time because doing so leads to crc/tmout errors
248784860c5dSDavid du Colombier  * for some devices.
248884860c5dSDavid du Colombier  * Upon errors on the data phase we must still run the status
248984860c5dSDavid du Colombier  * phase or the device may cease responding in the future.
249084860c5dSDavid du Colombier  */
249184860c5dSDavid du Colombier static long
epctlio(Ep * ep,Ctlio * cio,void * a,long count)249284860c5dSDavid du Colombier epctlio(Ep *ep, Ctlio *cio, void *a, long count)
249384860c5dSDavid du Colombier {
249484860c5dSDavid du Colombier 	uchar *c;
249584860c5dSDavid du Colombier 	long len;
249684860c5dSDavid du Colombier 
249784860c5dSDavid du Colombier 	ddeprint("epctlio: cio %#p ep%d.%d count %ld\n",
249884860c5dSDavid du Colombier 		cio, ep->dev->nb, ep->nb, count);
249984860c5dSDavid du Colombier 	if(count < Rsetuplen)
250084860c5dSDavid du Colombier 		error("short usb comand");
250184860c5dSDavid du Colombier 	qlock(cio);
250284860c5dSDavid du Colombier 	free(cio->data);
250384860c5dSDavid du Colombier 	cio->data = nil;
250484860c5dSDavid du Colombier 	cio->ndata = 0;
250584860c5dSDavid du Colombier 	if(waserror()){
250684860c5dSDavid du Colombier 		free(cio->data);
250784860c5dSDavid du Colombier 		cio->data = nil;
250884860c5dSDavid du Colombier 		cio->ndata = 0;
250984860c5dSDavid du Colombier 		qunlock(cio);
251084860c5dSDavid du Colombier 		nexterror();
251184860c5dSDavid du Colombier 	}
251284860c5dSDavid du Colombier 
251384860c5dSDavid du Colombier 	/* set the address if unset and out of configuration state */
251484860c5dSDavid du Colombier 	if(ep->dev->state != Dconfig && ep->dev->state != Dreset)
251584860c5dSDavid du Colombier 		if(cio->usbid == 0){
251684860c5dSDavid du Colombier 			cio->usbid = (ep->nb&Epmax) << 7 | ep->dev->nb&Devmax;
251784860c5dSDavid du Colombier 			coherence();
251884860c5dSDavid du Colombier 			qhsetaddr(cio->qh, cio->usbid);
251984860c5dSDavid du Colombier 		}
252084860c5dSDavid du Colombier 	/* adjust maxpkt if the user has learned a different one */
252184860c5dSDavid du Colombier 	if(qhmaxpkt(cio->qh) != ep->maxpkt)
252284860c5dSDavid du Colombier 		qhsetmaxpkt(cio->qh, ep->maxpkt);
252384860c5dSDavid du Colombier 	c = a;
252484860c5dSDavid du Colombier 	cio->tok = Tdtoksetup;
252584860c5dSDavid du Colombier 	cio->toggle = Tddata0;
252684860c5dSDavid du Colombier 	coherence();
252784860c5dSDavid du Colombier 	if(epio(ep, cio, a, Rsetuplen, 0) < Rsetuplen)
252884860c5dSDavid du Colombier 		error(Eio);
252984860c5dSDavid du Colombier 	a = c + Rsetuplen;
253084860c5dSDavid du Colombier 	count -= Rsetuplen;
253184860c5dSDavid du Colombier 
253284860c5dSDavid du Colombier 	cio->toggle = Tddata1;
253384860c5dSDavid du Colombier 	if(c[Rtype] & Rd2h){
253484860c5dSDavid du Colombier 		cio->tok = Tdtokin;
253584860c5dSDavid du Colombier 		len = GET2(c+Rcount);
253684860c5dSDavid du Colombier 		if(len <= 0)
253784860c5dSDavid du Colombier 			error("bad length in d2h request");
253884860c5dSDavid du Colombier 		if(len > Maxctllen)
253984860c5dSDavid du Colombier 			error("d2h data too large to fit in ehci");
254084860c5dSDavid du Colombier 		a = cio->data = smalloc(len+1);
254184860c5dSDavid du Colombier 	}else{
254284860c5dSDavid du Colombier 		cio->tok = Tdtokout;
254384860c5dSDavid du Colombier 		len = count;
254484860c5dSDavid du Colombier 	}
254584860c5dSDavid du Colombier 	coherence();
254684860c5dSDavid du Colombier 	if(len > 0)
254784860c5dSDavid du Colombier 		if(waserror())
254884860c5dSDavid du Colombier 			len = -1;
254984860c5dSDavid du Colombier 		else{
255084860c5dSDavid du Colombier 			len = epio(ep, cio, a, len, 0);
255184860c5dSDavid du Colombier 			poperror();
255284860c5dSDavid du Colombier 		}
255384860c5dSDavid du Colombier 	if(c[Rtype] & Rd2h){
255484860c5dSDavid du Colombier 		count = Rsetuplen;
255584860c5dSDavid du Colombier 		cio->ndata = len;
255684860c5dSDavid du Colombier 		cio->tok = Tdtokout;
255784860c5dSDavid du Colombier 	}else{
255884860c5dSDavid du Colombier 		if(len < 0)
255984860c5dSDavid du Colombier 			count = -1;
256084860c5dSDavid du Colombier 		else
256184860c5dSDavid du Colombier 			count = Rsetuplen + len;
256284860c5dSDavid du Colombier 		cio->tok = Tdtokin;
256384860c5dSDavid du Colombier 	}
256484860c5dSDavid du Colombier 	cio->toggle = Tddata1;
256584860c5dSDavid du Colombier 	coherence();
256684860c5dSDavid du Colombier 	epio(ep, cio, nil, 0, 0);
256784860c5dSDavid du Colombier 	qunlock(cio);
256884860c5dSDavid du Colombier 	poperror();
256984860c5dSDavid du Colombier 	ddeprint("epctlio cio %#p return %ld\n", cio, count);
257084860c5dSDavid du Colombier 	return count;
257184860c5dSDavid du Colombier }
257284860c5dSDavid du Colombier 
257384860c5dSDavid du Colombier static long
epwrite(Ep * ep,void * a,long count)257484860c5dSDavid du Colombier epwrite(Ep *ep, void *a, long count)
257584860c5dSDavid du Colombier {
257684860c5dSDavid du Colombier 	Qio *io;
257784860c5dSDavid du Colombier 	Ctlio *cio;
257884860c5dSDavid du Colombier 	Isoio *iso;
257984860c5dSDavid du Colombier 	ulong delta;
258084860c5dSDavid du Colombier 
258184860c5dSDavid du Colombier 	pollcheck(ep->hp);
258284860c5dSDavid du Colombier 
258384860c5dSDavid du Colombier 	ddeprint("ehci: epwrite ep%d.%d\n", ep->dev->nb, ep->nb);
258484860c5dSDavid du Colombier 	if(ep->aux == nil)
258584860c5dSDavid du Colombier 		panic("ehci: epwrite: not open");
258684860c5dSDavid du Colombier 	switch(ep->ttype){
258784860c5dSDavid du Colombier 	case Tctl:
258884860c5dSDavid du Colombier 		cio = ep->aux;
258984860c5dSDavid du Colombier 		return epctlio(ep, cio, a, count);
259084860c5dSDavid du Colombier 	case Tbulk:
259184860c5dSDavid du Colombier 		io = ep->aux;
259284860c5dSDavid du Colombier 		if(ep->clrhalt)
259384860c5dSDavid du Colombier 			clrhalt(ep);
259484860c5dSDavid du Colombier 		return epio(ep, &io[OWRITE], a, count, 1);
259584860c5dSDavid du Colombier 	case Tintr:
259684860c5dSDavid du Colombier 		io = ep->aux;
259784860c5dSDavid du Colombier 		delta = TK2MS(MACHP(0)->ticks) - io[OWRITE].iotime + 1;
259884860c5dSDavid du Colombier 		if(delta < ep->pollival)
259984860c5dSDavid du Colombier 			tsleep(&up->sleep, return0, 0, ep->pollival - delta);
260084860c5dSDavid du Colombier 		if(ep->clrhalt)
260184860c5dSDavid du Colombier 			clrhalt(ep);
260284860c5dSDavid du Colombier 		return epio(ep, &io[OWRITE], a, count, 1);
260384860c5dSDavid du Colombier 	case Tiso:
260484860c5dSDavid du Colombier 		iso = ep->aux;
260584860c5dSDavid du Colombier 		return episowrite(ep, iso, a, count);
260684860c5dSDavid du Colombier 	}
260784860c5dSDavid du Colombier 	return -1;
260884860c5dSDavid du Colombier }
260984860c5dSDavid du Colombier 
261084860c5dSDavid du Colombier static void
isofsinit(Ep * ep,Isoio * iso)261184860c5dSDavid du Colombier isofsinit(Ep *ep, Isoio *iso)
261284860c5dSDavid du Colombier {
261384860c5dSDavid du Colombier 	long left;
261484860c5dSDavid du Colombier 	Sitd *td, *ltd;
261584860c5dSDavid du Colombier 	int i;
261684860c5dSDavid du Colombier 	ulong frno;
261784860c5dSDavid du Colombier 
261884860c5dSDavid du Colombier 	left = 0;
261984860c5dSDavid du Colombier 	ltd = nil;
262084860c5dSDavid du Colombier 	frno = iso->td0frno;
262184860c5dSDavid du Colombier 	for(i = 0; i < iso->nframes; i++){
262284860c5dSDavid du Colombier 		td = sitdalloc();
262384860c5dSDavid du Colombier 		td->data = iso->data + i * ep->maxpkt;
262484860c5dSDavid du Colombier 		td->epc = ep->dev->port << Stdportshift;
262584860c5dSDavid du Colombier 		td->epc |= ep->dev->hub << Stdhubshift;
262684860c5dSDavid du Colombier 		td->epc |= ep->nb << Stdepshift;
262784860c5dSDavid du Colombier 		td->epc |= ep->dev->nb << Stddevshift;
262884860c5dSDavid du Colombier 		td->mfs = 034 << Stdscmshift | 1 << Stdssmshift;
262984860c5dSDavid du Colombier 		if(ep->mode == OREAD){
263084860c5dSDavid du Colombier 			td->epc |= Stdin;
263184860c5dSDavid du Colombier 			td->mdata = ep->maxpkt;
263284860c5dSDavid du Colombier 		}else{
263384860c5dSDavid du Colombier 			td->mdata = (ep->hz+left) * ep->pollival / 1000;
263484860c5dSDavid du Colombier 			td->mdata *= ep->samplesz;
263584860c5dSDavid du Colombier 			left = (ep->hz+left) * ep->pollival % 1000;
263684860c5dSDavid du Colombier 			if(td->mdata > ep->maxpkt){
263784860c5dSDavid du Colombier 				print("ehci: ep%d.%d: size > maxpkt\n",
263884860c5dSDavid du Colombier 					ep->dev->nb, ep->nb);
263984860c5dSDavid du Colombier 				print("size = %ld max = %ld\n",
264084860c5dSDavid du Colombier 					td->mdata,ep->maxpkt);
264184860c5dSDavid du Colombier 				td->mdata = ep->maxpkt;
264284860c5dSDavid du Colombier 			}
264384860c5dSDavid du Colombier 		}
264484860c5dSDavid du Colombier 		coherence();
264584860c5dSDavid du Colombier 
264684860c5dSDavid du Colombier 		iso->sitdps[frno] = td;
264784860c5dSDavid du Colombier 		coherence();
264884860c5dSDavid du Colombier 		sitdinit(iso, td);
264984860c5dSDavid du Colombier 		if(ltd != nil)
265084860c5dSDavid du Colombier 			ltd->next = td;
265184860c5dSDavid du Colombier 		ltd = td;
265284860c5dSDavid du Colombier 		frno = TRUNC(frno+ep->pollival, Nisoframes);
265384860c5dSDavid du Colombier 	}
265484860c5dSDavid du Colombier 	ltd->next = iso->sitdps[iso->td0frno];
265584860c5dSDavid du Colombier 	coherence();
265684860c5dSDavid du Colombier }
265784860c5dSDavid du Colombier 
265884860c5dSDavid du Colombier static void
isohsinit(Ep * ep,Isoio * iso)265984860c5dSDavid du Colombier isohsinit(Ep *ep, Isoio *iso)
266084860c5dSDavid du Colombier {
266184860c5dSDavid du Colombier 	int ival, p;
266284860c5dSDavid du Colombier 	long left;
266384860c5dSDavid du Colombier 	ulong frno, i, pa;
266484860c5dSDavid du Colombier 	Itd *ltd, *td;
266584860c5dSDavid du Colombier 
266684860c5dSDavid du Colombier 	iso->hs = 1;
266784860c5dSDavid du Colombier 	ival = 1;
266884860c5dSDavid du Colombier 	if(ep->pollival > 8)
266984860c5dSDavid du Colombier 		ival = ep->pollival/8;
267084860c5dSDavid du Colombier 	left = 0;
267184860c5dSDavid du Colombier 	ltd = nil;
267284860c5dSDavid du Colombier 	frno = iso->td0frno;
267384860c5dSDavid du Colombier 	for(i = 0; i < iso->nframes; i++){
267484860c5dSDavid du Colombier 		td = itdalloc();
267584860c5dSDavid du Colombier 		td->data = iso->data + i * 8 * iso->maxsize;
267684860c5dSDavid du Colombier 		pa = PADDR(td->data) & ~0xFFF;
267784860c5dSDavid du Colombier 		for(p = 0; p < 8; p++)
267884860c5dSDavid du Colombier 			td->buffer[i] = pa + p * 0x1000;
267984860c5dSDavid du Colombier 		td->buffer[0] = PADDR(iso->data) & ~0xFFF |
268084860c5dSDavid du Colombier 			ep->nb << Itdepshift | ep->dev->nb << Itddevshift;
268184860c5dSDavid du Colombier 		if(ep->mode == OREAD)
268284860c5dSDavid du Colombier 			td->buffer[1] |= Itdin;
268384860c5dSDavid du Colombier 		else
268484860c5dSDavid du Colombier 			td->buffer[1] |= Itdout;
268584860c5dSDavid du Colombier 		td->buffer[1] |= ep->maxpkt << Itdmaxpktshift;
268684860c5dSDavid du Colombier 		td->buffer[2] |= ep->ntds << Itdntdsshift;
268784860c5dSDavid du Colombier 
268884860c5dSDavid du Colombier 		if(ep->mode == OREAD)
268984860c5dSDavid du Colombier 			td->mdata = 8 * iso->maxsize;
269084860c5dSDavid du Colombier 		else{
269184860c5dSDavid du Colombier 			td->mdata = (ep->hz + left) * ep->pollival / 1000;
269284860c5dSDavid du Colombier 			td->mdata *= ep->samplesz;
269384860c5dSDavid du Colombier 			left = (ep->hz + left) * ep->pollival % 1000;
269484860c5dSDavid du Colombier 		}
269584860c5dSDavid du Colombier 		coherence();
269684860c5dSDavid du Colombier 		iso->itdps[frno] = td;
269784860c5dSDavid du Colombier 		coherence();
269884860c5dSDavid du Colombier 		itdinit(iso, td);
269984860c5dSDavid du Colombier 		if(ltd != nil)
270084860c5dSDavid du Colombier 			ltd->next = td;
270184860c5dSDavid du Colombier 		ltd = td;
270284860c5dSDavid du Colombier 		frno = TRUNC(frno + ival, Nisoframes);
270384860c5dSDavid du Colombier 	}
270484860c5dSDavid du Colombier }
270584860c5dSDavid du Colombier 
270684860c5dSDavid du Colombier static void
isoopen(Ctlr * ctlr,Ep * ep)270784860c5dSDavid du Colombier isoopen(Ctlr *ctlr, Ep *ep)
270884860c5dSDavid du Colombier {
270984860c5dSDavid du Colombier 	int ival;		/* pollival in ms */
271084860c5dSDavid du Colombier 	int tpf;		/* tds per frame */
271184860c5dSDavid du Colombier 	int i, n, w, woff;
271284860c5dSDavid du Colombier 	ulong frno;
271384860c5dSDavid du Colombier 	Isoio *iso;
271484860c5dSDavid du Colombier 
271584860c5dSDavid du Colombier 	iso = ep->aux;
271684860c5dSDavid du Colombier 	switch(ep->mode){
271784860c5dSDavid du Colombier 	case OREAD:
271884860c5dSDavid du Colombier 		iso->tok = Tdtokin;
271984860c5dSDavid du Colombier 		break;
272084860c5dSDavid du Colombier 	case OWRITE:
272184860c5dSDavid du Colombier 		iso->tok = Tdtokout;
272284860c5dSDavid du Colombier 		break;
272384860c5dSDavid du Colombier 	default:
272484860c5dSDavid du Colombier 		error("iso i/o is half-duplex");
272584860c5dSDavid du Colombier 	}
272684860c5dSDavid du Colombier 	iso->usbid = ep->nb << 7 | ep->dev->nb & Devmax;
272784860c5dSDavid du Colombier 	iso->state = Qidle;
272884860c5dSDavid du Colombier 	coherence();
272984860c5dSDavid du Colombier 	iso->debug = ep->debug;
273084860c5dSDavid du Colombier 	ival = ep->pollival;
273184860c5dSDavid du Colombier 	tpf = 1;
273284860c5dSDavid du Colombier 	if(ep->dev->speed == Highspeed){
273384860c5dSDavid du Colombier 		tpf = 8;
273484860c5dSDavid du Colombier 		if(ival <= 8)
273584860c5dSDavid du Colombier 			ival = 1;
273684860c5dSDavid du Colombier 		else
273784860c5dSDavid du Colombier 			ival /= 8;
273884860c5dSDavid du Colombier 	}
273984860c5dSDavid du Colombier 	assert(ival != 0);
274084860c5dSDavid du Colombier 	iso->nframes = Nisoframes / ival;
274184860c5dSDavid du Colombier 	if(iso->nframes < 3)
274284860c5dSDavid du Colombier 		error("uhci isoopen bug");	/* we need at least 3 tds */
274384860c5dSDavid du Colombier 	iso->maxsize = ep->ntds * ep->maxpkt;
274484860c5dSDavid du Colombier 	if(ctlr->load + ep->load > 800)
274584860c5dSDavid du Colombier 		print("usb: ehci: bandwidth may be exceeded\n");
274684860c5dSDavid du Colombier 	ilock(ctlr);
274784860c5dSDavid du Colombier 	ctlr->load += ep->load;
274884860c5dSDavid du Colombier 	ctlr->isoload += ep->load;
274984860c5dSDavid du Colombier 	ctlr->nreqs++;
275084860c5dSDavid du Colombier 	dprint("ehci: load %uld isoload %uld\n", ctlr->load, ctlr->isoload);
275184860c5dSDavid du Colombier 	diprint("iso nframes %d pollival %uld ival %d maxpkt %uld ntds %d\n",
275284860c5dSDavid du Colombier 		iso->nframes, ep->pollival, ival, ep->maxpkt, ep->ntds);
275384860c5dSDavid du Colombier 	iunlock(ctlr);
275484860c5dSDavid du Colombier 	if(ctlr->poll.does)
275584860c5dSDavid du Colombier 		wakeup(&ctlr->poll);
275684860c5dSDavid du Colombier 
275784860c5dSDavid du Colombier 	/*
275884860c5dSDavid du Colombier 	 * From here on this cannot raise errors
275984860c5dSDavid du Colombier 	 * unless we catch them and release here all memory allocated.
276084860c5dSDavid du Colombier 	 */
276184860c5dSDavid du Colombier 	assert(ep->maxpkt > 0 && ep->ntds > 0 && ep->ntds < 4);
276284860c5dSDavid du Colombier 	assert(ep->maxpkt <= 1024);
276384860c5dSDavid du Colombier 	iso->tdps = smalloc(sizeof(uintptr) * Nisoframes);
276484860c5dSDavid du Colombier 	iso->data = smalloc(iso->nframes * tpf * ep->ntds * ep->maxpkt);
276584860c5dSDavid du Colombier 	iso->td0frno = TRUNC(ctlr->opio->frno + 10, Nisoframes);
276684860c5dSDavid du Colombier 	/* read: now; write: 1s ahead */
276784860c5dSDavid du Colombier 
276884860c5dSDavid du Colombier 	if(ep->dev->speed == Highspeed)
276984860c5dSDavid du Colombier 		isohsinit(ep, iso);
277084860c5dSDavid du Colombier 	else
277184860c5dSDavid du Colombier 		isofsinit(ep, iso);
277284860c5dSDavid du Colombier 	iso->tdu = iso->tdi = iso->itdps[iso->td0frno];
277384860c5dSDavid du Colombier 	iso->stdu = iso->stdi = iso->sitdps[iso->td0frno];
277484860c5dSDavid du Colombier 	coherence();
277584860c5dSDavid du Colombier 
277684860c5dSDavid du Colombier 	ilock(ctlr);
277784860c5dSDavid du Colombier 	frno = iso->td0frno;
277884860c5dSDavid du Colombier 	for(i = 0; i < iso->nframes; i++){
277984860c5dSDavid du Colombier 		*iso->tdps[frno] = ctlr->frames[frno];
278084860c5dSDavid du Colombier 		frno = TRUNC(frno+ival, Nisoframes);
278184860c5dSDavid du Colombier 	}
278284860c5dSDavid du Colombier 
278384860c5dSDavid du Colombier 	/*
278484860c5dSDavid du Colombier 	 * Iso uses a virtual frame window of Nisoframes, and we must
278584860c5dSDavid du Colombier 	 * fill the actual ctlr frame array by placing ctlr->nframes/Nisoframes
278684860c5dSDavid du Colombier 	 * copies of the window in the frame array.
278784860c5dSDavid du Colombier 	 */
278884860c5dSDavid du Colombier 	assert(ctlr->nframes >= Nisoframes && Nisoframes >= iso->nframes);
278984860c5dSDavid du Colombier 	assert(Nisoframes >= Nintrleafs);
279084860c5dSDavid du Colombier 	n = ctlr->nframes / Nisoframes;
279184860c5dSDavid du Colombier 	for(w = 0; w < n; w++){
279284860c5dSDavid du Colombier 		frno = iso->td0frno;
279384860c5dSDavid du Colombier 		woff = w * Nisoframes;
279484860c5dSDavid du Colombier 		for(i = 0; i < iso->nframes ; i++){
279584860c5dSDavid du Colombier 			assert(woff+frno < ctlr->nframes);
279684860c5dSDavid du Colombier 			assert(iso->tdps[frno] != nil);
279784860c5dSDavid du Colombier 			if(ep->dev->speed == Highspeed)
279884860c5dSDavid du Colombier 				ctlr->frames[woff+frno] = PADDR(iso->tdps[frno])
279984860c5dSDavid du Colombier 					|Litd;
280084860c5dSDavid du Colombier 			else
280184860c5dSDavid du Colombier 				ctlr->frames[woff+frno] = PADDR(iso->tdps[frno])
280284860c5dSDavid du Colombier 					|Lsitd;
280384860c5dSDavid du Colombier 			coherence();
280484860c5dSDavid du Colombier 			frno = TRUNC(frno+ep->pollival, Nisoframes);
280584860c5dSDavid du Colombier 		}
280684860c5dSDavid du Colombier 	}
280784860c5dSDavid du Colombier 	coherence();
280884860c5dSDavid du Colombier 	iso->next = ctlr->iso;
280984860c5dSDavid du Colombier 	ctlr->iso = iso;
281084860c5dSDavid du Colombier 	coherence();
281184860c5dSDavid du Colombier 	iso->state = Qdone;
281284860c5dSDavid du Colombier 	iunlock(ctlr);
281384860c5dSDavid du Colombier 	if(ehcidebug > 1 || iso->debug >1)
281484860c5dSDavid du Colombier 		isodump(iso, 0);
281584860c5dSDavid du Colombier }
281684860c5dSDavid du Colombier 
281784860c5dSDavid du Colombier /*
281884860c5dSDavid du Colombier  * Allocate the endpoint and set it up for I/O
281984860c5dSDavid du Colombier  * in the controller. This must follow what's said
282084860c5dSDavid du Colombier  * in Ep regarding configuration, including perhaps
282184860c5dSDavid du Colombier  * the saved toggles (saved on a previous close of
282284860c5dSDavid du Colombier  * the endpoint data file by epclose).
282384860c5dSDavid du Colombier  */
282484860c5dSDavid du Colombier static void
epopen(Ep * ep)282584860c5dSDavid du Colombier epopen(Ep *ep)
282684860c5dSDavid du Colombier {
282784860c5dSDavid du Colombier 	Ctlr *ctlr;
282884860c5dSDavid du Colombier 	Ctlio *cio;
282984860c5dSDavid du Colombier 	Qio *io;
283084860c5dSDavid du Colombier 	int usbid;
283184860c5dSDavid du Colombier 
283284860c5dSDavid du Colombier 	ctlr = ep->hp->aux;
283384860c5dSDavid du Colombier 	deprint("ehci: epopen ep%d.%d\n", ep->dev->nb, ep->nb);
283484860c5dSDavid du Colombier 	if(ep->aux != nil)
283584860c5dSDavid du Colombier 		panic("ehci: epopen called with open ep");
283684860c5dSDavid du Colombier 	if(waserror()){
283784860c5dSDavid du Colombier 		free(ep->aux);
283884860c5dSDavid du Colombier 		ep->aux = nil;
283984860c5dSDavid du Colombier 		nexterror();
284084860c5dSDavid du Colombier 	}
284184860c5dSDavid du Colombier 	switch(ep->ttype){
284284860c5dSDavid du Colombier 	case Tnone:
284384860c5dSDavid du Colombier 		error("endpoint not configured");
284484860c5dSDavid du Colombier 	case Tiso:
284584860c5dSDavid du Colombier 		ep->aux = smalloc(sizeof(Isoio));
284684860c5dSDavid du Colombier 		isoopen(ctlr, ep);
284784860c5dSDavid du Colombier 		break;
284884860c5dSDavid du Colombier 	case Tctl:
284984860c5dSDavid du Colombier 		cio = ep->aux = smalloc(sizeof(Ctlio));
285084860c5dSDavid du Colombier 		cio->debug = ep->debug;
285184860c5dSDavid du Colombier 		cio->ndata = -1;
285284860c5dSDavid du Colombier 		cio->data = nil;
285384860c5dSDavid du Colombier 		if(ep->dev->isroot != 0 && ep->nb == 0)	/* root hub */
285484860c5dSDavid du Colombier 			break;
285584860c5dSDavid du Colombier 		cio->qh = qhalloc(ctlr, ep, cio, "epc");
285684860c5dSDavid du Colombier 		break;
285784860c5dSDavid du Colombier 	case Tbulk:
285884860c5dSDavid du Colombier 		ep->pollival = 1;	/* assume this; doesn't really matter */
285984860c5dSDavid du Colombier 		/* and fall... */
286084860c5dSDavid du Colombier 	case Tintr:
286184860c5dSDavid du Colombier 		io = ep->aux = smalloc(sizeof(Qio)*2);
286284860c5dSDavid du Colombier 		io[OREAD].debug = io[OWRITE].debug = ep->debug;
286384860c5dSDavid du Colombier 		usbid = (ep->nb&Epmax) << 7 | ep->dev->nb &Devmax;
286484860c5dSDavid du Colombier 		assert(ep->pollival != 0);
286584860c5dSDavid du Colombier 		if(ep->mode != OREAD){
286684860c5dSDavid du Colombier 			if(ep->toggle[OWRITE] != 0)
286784860c5dSDavid du Colombier 				io[OWRITE].toggle = Tddata1;
286884860c5dSDavid du Colombier 			else
286984860c5dSDavid du Colombier 				io[OWRITE].toggle = Tddata0;
287084860c5dSDavid du Colombier 			io[OWRITE].tok = Tdtokout;
287184860c5dSDavid du Colombier 			io[OWRITE].usbid = usbid;
287284860c5dSDavid du Colombier 			io[OWRITE].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */
287384860c5dSDavid du Colombier 			io[OWRITE].qh = qhalloc(ctlr, ep, io+OWRITE, "epw");
287484860c5dSDavid du Colombier 		}
287584860c5dSDavid du Colombier 		if(ep->mode != OWRITE){
287684860c5dSDavid du Colombier 			if(ep->toggle[OREAD] != 0)
287784860c5dSDavid du Colombier 				io[OREAD].toggle = Tddata1;
287884860c5dSDavid du Colombier 			else
287984860c5dSDavid du Colombier 				io[OREAD].toggle = Tddata0;
288084860c5dSDavid du Colombier 			io[OREAD].tok = Tdtokin;
288184860c5dSDavid du Colombier 			io[OREAD].usbid = usbid;
288284860c5dSDavid du Colombier 			io[OREAD].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */
288384860c5dSDavid du Colombier 			io[OREAD].qh = qhalloc(ctlr, ep, io+OREAD, "epr");
288484860c5dSDavid du Colombier 		}
288584860c5dSDavid du Colombier 		break;
288684860c5dSDavid du Colombier 	}
288784860c5dSDavid du Colombier 	coherence();
288884860c5dSDavid du Colombier 	if(ehcidebug>1 || ep->debug)
288984860c5dSDavid du Colombier 		dump(ep->hp);
289084860c5dSDavid du Colombier 	deprint("ehci: epopen done\n");
289184860c5dSDavid du Colombier 	poperror();
289284860c5dSDavid du Colombier }
289384860c5dSDavid du Colombier 
289484860c5dSDavid du Colombier static void
cancelio(Ctlr * ctlr,Qio * io)289584860c5dSDavid du Colombier cancelio(Ctlr *ctlr, Qio *io)
289684860c5dSDavid du Colombier {
289784860c5dSDavid du Colombier 	Qh *qh;
289884860c5dSDavid du Colombier 
289984860c5dSDavid du Colombier 	ilock(ctlr);
290084860c5dSDavid du Colombier 	qh = io->qh;
290184860c5dSDavid du Colombier 	if(io == nil || io->qh == nil || io->qh->state == Qclose){
290284860c5dSDavid du Colombier 		iunlock(ctlr);
290384860c5dSDavid du Colombier 		return;
290484860c5dSDavid du Colombier 	}
290584860c5dSDavid du Colombier 	dqprint("ehci: cancelio for qh %#p state %s\n",
290684860c5dSDavid du Colombier 		qh, qhsname[qh->state]);
290784860c5dSDavid du Colombier 	aborttds(qh);
290884860c5dSDavid du Colombier 	qh->state = Qclose;
290984860c5dSDavid du Colombier 	iunlock(ctlr);
291084860c5dSDavid du Colombier 	if(!waserror()){
291184860c5dSDavid du Colombier 		tsleep(&up->sleep, return0, 0, Abortdelay);
291284860c5dSDavid du Colombier 		poperror();
291384860c5dSDavid du Colombier 	}
291484860c5dSDavid du Colombier 	wakeup(io);
291584860c5dSDavid du Colombier 	qlock(io);
291684860c5dSDavid du Colombier 	/* wait for epio if running */
291784860c5dSDavid du Colombier 	qunlock(io);
291884860c5dSDavid du Colombier 
291984860c5dSDavid du Colombier 	qhfree(ctlr, qh);
292084860c5dSDavid du Colombier 	io->qh = nil;
292184860c5dSDavid du Colombier }
292284860c5dSDavid du Colombier 
292384860c5dSDavid du Colombier static void
cancelisoio(Ctlr * ctlr,Isoio * iso,int pollival,ulong load)292484860c5dSDavid du Colombier cancelisoio(Ctlr *ctlr, Isoio *iso, int pollival, ulong load)
292584860c5dSDavid du Colombier {
292684860c5dSDavid du Colombier 	int frno, i, n, t, w, woff;
292784860c5dSDavid du Colombier 	ulong *lp, *tp;
292884860c5dSDavid du Colombier 	Isoio **il;
292984860c5dSDavid du Colombier 	Itd *td;
293084860c5dSDavid du Colombier 	Sitd *std;
293184860c5dSDavid du Colombier 
293284860c5dSDavid du Colombier 	ilock(ctlr);
293384860c5dSDavid du Colombier 	if(iso->state == Qclose){
293484860c5dSDavid du Colombier 		iunlock(ctlr);
293584860c5dSDavid du Colombier 		return;
293684860c5dSDavid du Colombier 	}
293784860c5dSDavid du Colombier 	ctlr->nreqs--;
293884860c5dSDavid du Colombier 	if(iso->state != Qrun && iso->state != Qdone)
293984860c5dSDavid du Colombier 		panic("bad iso state");
294084860c5dSDavid du Colombier 	iso->state = Qclose;
294184860c5dSDavid du Colombier 	coherence();
294284860c5dSDavid du Colombier 	if(ctlr->isoload < load)
294384860c5dSDavid du Colombier 		panic("ehci: low isoload");
294484860c5dSDavid du Colombier 	ctlr->isoload -= load;
294584860c5dSDavid du Colombier 	ctlr->load -= load;
294684860c5dSDavid du Colombier 	for(il = &ctlr->iso; *il != nil; il = &(*il)->next)
294784860c5dSDavid du Colombier 		if(*il == iso)
294884860c5dSDavid du Colombier 			break;
294984860c5dSDavid du Colombier 	if(*il == nil)
295084860c5dSDavid du Colombier 		panic("cancleiso: not found");
295184860c5dSDavid du Colombier 	*il = iso->next;
295284860c5dSDavid du Colombier 
295384860c5dSDavid du Colombier 	frno = iso->td0frno;
295484860c5dSDavid du Colombier 	for(i = 0; i < iso->nframes; i++){
295584860c5dSDavid du Colombier 		tp = iso->tdps[frno];
295684860c5dSDavid du Colombier 		if(iso->hs != 0){
295784860c5dSDavid du Colombier 			td = iso->itdps[frno];
295884860c5dSDavid du Colombier 			for(t = 0; t < nelem(td->csw); t++)
295984860c5dSDavid du Colombier 				td->csw[t] &= ~(Itdioc|Itdactive);
296084860c5dSDavid du Colombier 		}else{
296184860c5dSDavid du Colombier 			std = iso->sitdps[frno];
296284860c5dSDavid du Colombier 			std->csw &= ~(Stdioc|Stdactive);
296384860c5dSDavid du Colombier 		}
296484860c5dSDavid du Colombier 		coherence();
296584860c5dSDavid du Colombier 		for(lp = &ctlr->frames[frno]; !(*lp & Lterm);
296684860c5dSDavid du Colombier 		    lp = &LPTR(*lp)[0])
296784860c5dSDavid du Colombier 			if(LPTR(*lp) == tp)
296884860c5dSDavid du Colombier 				break;
296984860c5dSDavid du Colombier 		if(*lp & Lterm)
297084860c5dSDavid du Colombier 			panic("cancelisoio: td not found");
297184860c5dSDavid du Colombier 		*lp = tp[0];
297284860c5dSDavid du Colombier 		/*
297384860c5dSDavid du Colombier 		 * Iso uses a virtual frame window of Nisoframes, and we must
297484860c5dSDavid du Colombier 		 * restore pointers in copies of the window kept at ctlr->frames.
297584860c5dSDavid du Colombier 		 */
297684860c5dSDavid du Colombier 		if(lp == &ctlr->frames[frno]){
297784860c5dSDavid du Colombier 			n = ctlr->nframes / Nisoframes;
297884860c5dSDavid du Colombier 			for(w = 1; w < n; w++){
297984860c5dSDavid du Colombier 				woff = w * Nisoframes;
298084860c5dSDavid du Colombier 				ctlr->frames[woff+frno] = *lp;
298184860c5dSDavid du Colombier 			}
298284860c5dSDavid du Colombier 		}
298384860c5dSDavid du Colombier 		coherence();
298484860c5dSDavid du Colombier 		frno = TRUNC(frno+pollival, Nisoframes);
298584860c5dSDavid du Colombier 	}
298684860c5dSDavid du Colombier 	iunlock(ctlr);
298784860c5dSDavid du Colombier 
298884860c5dSDavid du Colombier 	/*
298984860c5dSDavid du Colombier 	 * wakeup anyone waiting for I/O and
299084860c5dSDavid du Colombier 	 * wait to be sure no I/O is in progress in the controller.
299184860c5dSDavid du Colombier 	 * and then wait to be sure episo* is no longer running.
299284860c5dSDavid du Colombier 	 */
299384860c5dSDavid du Colombier 	wakeup(iso);
299484860c5dSDavid du Colombier 	diprint("cancelisoio iso %#p waiting for I/O to cease\n", iso);
299584860c5dSDavid du Colombier 	tsleep(&up->sleep, return0, 0, 5);
299684860c5dSDavid du Colombier 	qlock(iso);
299784860c5dSDavid du Colombier 	qunlock(iso);
299884860c5dSDavid du Colombier 	diprint("cancelisoio iso %#p releasing iso\n", iso);
299984860c5dSDavid du Colombier 
300084860c5dSDavid du Colombier 	frno = iso->td0frno;
300184860c5dSDavid du Colombier 	for(i = 0; i < iso->nframes; i++){
300284860c5dSDavid du Colombier 		if(iso->hs != 0)
300384860c5dSDavid du Colombier 			itdfree(iso->itdps[frno]);
300484860c5dSDavid du Colombier 		else
300584860c5dSDavid du Colombier 			sitdfree(iso->sitdps[frno]);
300684860c5dSDavid du Colombier 		iso->tdps[frno] = nil;
300784860c5dSDavid du Colombier 		frno = TRUNC(frno+pollival, Nisoframes);
300884860c5dSDavid du Colombier 	}
300984860c5dSDavid du Colombier 	free(iso->tdps);
301084860c5dSDavid du Colombier 	iso->tdps = nil;
301184860c5dSDavid du Colombier 	free(iso->data);
301284860c5dSDavid du Colombier 	iso->data = nil;
301384860c5dSDavid du Colombier 	coherence();
301484860c5dSDavid du Colombier }
301584860c5dSDavid du Colombier 
301684860c5dSDavid du Colombier static void
epclose(Ep * ep)301784860c5dSDavid du Colombier epclose(Ep *ep)
301884860c5dSDavid du Colombier {
301984860c5dSDavid du Colombier 	Qio *io;
302084860c5dSDavid du Colombier 	Ctlio *cio;
302184860c5dSDavid du Colombier 	Isoio *iso;
302284860c5dSDavid du Colombier 	Ctlr *ctlr;
302384860c5dSDavid du Colombier 
302484860c5dSDavid du Colombier 	ctlr = ep->hp->aux;
302584860c5dSDavid du Colombier 	deprint("ehci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
302684860c5dSDavid du Colombier 
302784860c5dSDavid du Colombier 	if(ep->aux == nil)
302884860c5dSDavid du Colombier 		panic("ehci: epclose called with closed ep");
302984860c5dSDavid du Colombier 	switch(ep->ttype){
303084860c5dSDavid du Colombier 	case Tctl:
303184860c5dSDavid du Colombier 		cio = ep->aux;
303284860c5dSDavid du Colombier 		cancelio(ctlr, cio);
303384860c5dSDavid du Colombier 		free(cio->data);
303484860c5dSDavid du Colombier 		cio->data = nil;
303584860c5dSDavid du Colombier 		break;
303684860c5dSDavid du Colombier 	case Tintr:
303784860c5dSDavid du Colombier 	case Tbulk:
303884860c5dSDavid du Colombier 		io = ep->aux;
303984860c5dSDavid du Colombier 		ep->toggle[OREAD] = ep->toggle[OWRITE] = 0;
304084860c5dSDavid du Colombier 		if(ep->mode != OWRITE){
304184860c5dSDavid du Colombier 			cancelio(ctlr, &io[OREAD]);
304284860c5dSDavid du Colombier 			if(io[OREAD].toggle == Tddata1)
304384860c5dSDavid du Colombier 				ep->toggle[OREAD] = 1;
304484860c5dSDavid du Colombier 		}
304584860c5dSDavid du Colombier 		if(ep->mode != OREAD){
304684860c5dSDavid du Colombier 			cancelio(ctlr, &io[OWRITE]);
304784860c5dSDavid du Colombier 			if(io[OWRITE].toggle == Tddata1)
304884860c5dSDavid du Colombier 				ep->toggle[OWRITE] = 1;
304984860c5dSDavid du Colombier 		}
305084860c5dSDavid du Colombier 		coherence();
305184860c5dSDavid du Colombier 		break;
305284860c5dSDavid du Colombier 	case Tiso:
305384860c5dSDavid du Colombier 		iso = ep->aux;
305484860c5dSDavid du Colombier 		cancelisoio(ctlr, iso, ep->pollival, ep->load);
305584860c5dSDavid du Colombier 		break;
305684860c5dSDavid du Colombier 	default:
305784860c5dSDavid du Colombier 		panic("epclose: bad ttype");
305884860c5dSDavid du Colombier 	}
305984860c5dSDavid du Colombier 	free(ep->aux);
306084860c5dSDavid du Colombier 	ep->aux = nil;
306184860c5dSDavid du Colombier }
306284860c5dSDavid du Colombier 
306384860c5dSDavid du Colombier /*
306484860c5dSDavid du Colombier  * return smallest power of 2 >= n
306584860c5dSDavid du Colombier  */
306684860c5dSDavid du Colombier static int
flog2(int n)306784860c5dSDavid du Colombier flog2(int n)
306884860c5dSDavid du Colombier {
306984860c5dSDavid du Colombier 	int i;
307084860c5dSDavid du Colombier 
307184860c5dSDavid du Colombier 	for(i = 0; (1 << i) < n; i++)
307284860c5dSDavid du Colombier 		;
307384860c5dSDavid du Colombier 	return i;
307484860c5dSDavid du Colombier }
307584860c5dSDavid du Colombier 
307684860c5dSDavid du Colombier /*
307784860c5dSDavid du Colombier  * build the periodic scheduling tree:
307884860c5dSDavid du Colombier  * framesize must be a multiple of the tree size
307984860c5dSDavid du Colombier  */
308084860c5dSDavid du Colombier static void
mkqhtree(Ctlr * ctlr)308184860c5dSDavid du Colombier mkqhtree(Ctlr *ctlr)
308284860c5dSDavid du Colombier {
308384860c5dSDavid du Colombier 	int i, n, d, o, leaf0, depth;
308484860c5dSDavid du Colombier 	ulong leafs[Nintrleafs];
308584860c5dSDavid du Colombier 	Qh *qh;
308684860c5dSDavid du Colombier 	Qh **tree;
308784860c5dSDavid du Colombier 	Qtree *qt;
308884860c5dSDavid du Colombier 
308984860c5dSDavid du Colombier 	depth = flog2(Nintrleafs);
309084860c5dSDavid du Colombier 	n = (1 << (depth+1)) - 1;
309184860c5dSDavid du Colombier 	qt = mallocz(sizeof(*qt), 1);
309284860c5dSDavid du Colombier 	if(qt == nil)
309384860c5dSDavid du Colombier 		panic("ehci: mkqhtree: no memory");
309484860c5dSDavid du Colombier 	qt->nel = n;
309584860c5dSDavid du Colombier 	qt->depth = depth;
309684860c5dSDavid du Colombier 	qt->bw = mallocz(n * sizeof(qt->bw), 1);
309784860c5dSDavid du Colombier 	qt->root = tree = mallocz(n * sizeof(Qh *), 1);
309884860c5dSDavid du Colombier 	if(qt->bw == nil || tree == nil)
309984860c5dSDavid du Colombier 		panic("ehci: mkqhtree: no memory");
310084860c5dSDavid du Colombier 	for(i = 0; i < n; i++){
310184860c5dSDavid du Colombier 		tree[i] = qh = edalloc();
310284860c5dSDavid du Colombier 		if(qh == nil)
310384860c5dSDavid du Colombier 			panic("ehci: mkqhtree: no memory");
310484860c5dSDavid du Colombier 		qh->nlink = qh->alink = qh->link = Lterm;
310584860c5dSDavid du Colombier 		qh->csw = Tdhalt;
310684860c5dSDavid du Colombier 		qh->state = Qidle;
310784860c5dSDavid du Colombier 		coherence();
310884860c5dSDavid du Colombier 		if(i > 0)
310984860c5dSDavid du Colombier 			qhlinkqh(tree[i], tree[(i-1)/2]);
311084860c5dSDavid du Colombier 	}
311184860c5dSDavid du Colombier 	ctlr->ntree = i;
311284860c5dSDavid du Colombier 	dprint("ehci: tree: %d endpoints allocated\n", i);
311384860c5dSDavid du Colombier 
311484860c5dSDavid du Colombier 	/* distribute leaves evenly round the frame list */
311584860c5dSDavid du Colombier 	leaf0 = n / 2;
311684860c5dSDavid du Colombier 	for(i = 0; i < Nintrleafs; i++){
311784860c5dSDavid du Colombier 		o = 0;
311884860c5dSDavid du Colombier 		for(d = 0; d < depth; d++){
311984860c5dSDavid du Colombier 			o <<= 1;
312084860c5dSDavid du Colombier 			if(i & (1 << d))
312184860c5dSDavid du Colombier 				o |= 1;
312284860c5dSDavid du Colombier 		}
312384860c5dSDavid du Colombier 		if(leaf0 + o >= n){
312484860c5dSDavid du Colombier 			print("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n);
312584860c5dSDavid du Colombier 			break;
312684860c5dSDavid du Colombier 		}
312784860c5dSDavid du Colombier 		leafs[i] = PADDR(tree[leaf0 + o]) | Lqh;
312884860c5dSDavid du Colombier 	}
312984860c5dSDavid du Colombier 	assert((ctlr->nframes % Nintrleafs) == 0);
313084860c5dSDavid du Colombier 	for(i = 0; i < ctlr->nframes; i += Nintrleafs){
313184860c5dSDavid du Colombier 		memmove(ctlr->frames + i, leafs, sizeof leafs);
313284860c5dSDavid du Colombier 		coherence();
313384860c5dSDavid du Colombier 	}
313484860c5dSDavid du Colombier 	ctlr->tree = qt;
313584860c5dSDavid du Colombier 	coherence();
313684860c5dSDavid du Colombier }
313784860c5dSDavid du Colombier 
313884860c5dSDavid du Colombier void
ehcimeminit(Ctlr * ctlr)313984860c5dSDavid du Colombier ehcimeminit(Ctlr *ctlr)
314084860c5dSDavid du Colombier {
314184860c5dSDavid du Colombier 	int i, frsize;
314284860c5dSDavid du Colombier 	Eopio *opio;
314384860c5dSDavid du Colombier 
314484860c5dSDavid du Colombier 	opio = ctlr->opio;
314584860c5dSDavid du Colombier 	frsize = ctlr->nframes * sizeof(ulong);
314684860c5dSDavid du Colombier 	assert((frsize & 0xFFF) == 0);		/* must be 4k aligned */
314784860c5dSDavid du Colombier 	ctlr->frames = xspanalloc(frsize, frsize, 0);
314884860c5dSDavid du Colombier 	if(ctlr->frames == nil)
314984860c5dSDavid du Colombier 		panic("ehci reset: no memory");
315084860c5dSDavid du Colombier 
315184860c5dSDavid du Colombier 	for (i = 0; i < ctlr->nframes; i++)
315284860c5dSDavid du Colombier 		ctlr->frames[i] = Lterm;
315384860c5dSDavid du Colombier 	opio->frbase = PADDR(ctlr->frames);
315484860c5dSDavid du Colombier 	opio->frno = 0;
315584860c5dSDavid du Colombier 	coherence();
315684860c5dSDavid du Colombier 
315784860c5dSDavid du Colombier 	qhalloc(ctlr, nil, nil, nil);	/* init async list */
315884860c5dSDavid du Colombier 	mkqhtree(ctlr);			/* init sync list */
315984860c5dSDavid du Colombier 	edfree(edalloc());		/* try to get some ones pre-allocated */
316084860c5dSDavid du Colombier 
316184860c5dSDavid du Colombier 	dprint("ehci %#p flb %#lux frno %#lux\n",
316284860c5dSDavid du Colombier 		ctlr->capio, opio->frbase, opio->frno);
316384860c5dSDavid du Colombier }
316484860c5dSDavid du Colombier 
316584860c5dSDavid du Colombier static void
init(Hci * hp)316684860c5dSDavid du Colombier init(Hci *hp)
316784860c5dSDavid du Colombier {
316884860c5dSDavid du Colombier 	Ctlr *ctlr;
316984860c5dSDavid du Colombier 	Eopio *opio;
317084860c5dSDavid du Colombier 	int i;
31712a782499SDavid du Colombier 	static int ctlrno;
317284860c5dSDavid du Colombier 
317384860c5dSDavid du Colombier 	hp->highspeed = 1;
317484860c5dSDavid du Colombier 	ctlr = hp->aux;
317584860c5dSDavid du Colombier 	opio = ctlr->opio;
317684860c5dSDavid du Colombier 	dprint("ehci %#p init\n", ctlr->capio);
317784860c5dSDavid du Colombier 
317884860c5dSDavid du Colombier 	ilock(ctlr);
317984860c5dSDavid du Colombier 	/*
318084860c5dSDavid du Colombier 	 * Unless we activate frroll interrupt
318184860c5dSDavid du Colombier 	 * some machines won't post other interrupts.
318284860c5dSDavid du Colombier 	 */
318384860c5dSDavid du Colombier 	opio->intr = Iusb|Ierr|Iportchg|Ihcerr|Iasync;
318484860c5dSDavid du Colombier 	coherence();
318584860c5dSDavid du Colombier 	opio->cmd |= Cpse;
318684860c5dSDavid du Colombier 	coherence();
318784860c5dSDavid du Colombier 	opio->cmd |= Case;
318884860c5dSDavid du Colombier 	coherence();
318984860c5dSDavid du Colombier 	ehcirun(ctlr, 1);
31902a782499SDavid du Colombier 	/*
31912a782499SDavid du Colombier 	 * route all ports by default to only one ehci (the first).
31922a782499SDavid du Colombier 	 * it's not obvious how multiple ehcis could work and on some
31932a782499SDavid du Colombier 	 * machines, setting Callmine on all ehcis makes the machine seize up.
31942a782499SDavid du Colombier 	 */
31952a782499SDavid du Colombier 	opio->config = (ctlrno == 0? Callmine: 0);
319684860c5dSDavid du Colombier 	coherence();
319784860c5dSDavid du Colombier 
319884860c5dSDavid du Colombier 	for (i = 0; i < hp->nports; i++)
319984860c5dSDavid du Colombier 		opio->portsc[i] = Pspower;
320084860c5dSDavid du Colombier 	iunlock(ctlr);
320184860c5dSDavid du Colombier 	if(ehcidebug > 1)
320284860c5dSDavid du Colombier 		dump(hp);
32032a782499SDavid du Colombier 	ctlrno++;
320484860c5dSDavid du Colombier }
320584860c5dSDavid du Colombier 
320684860c5dSDavid du Colombier void
ehcilinkage(Hci * hp)320784860c5dSDavid du Colombier ehcilinkage(Hci *hp)
320884860c5dSDavid du Colombier {
320984860c5dSDavid du Colombier 	hp->init = init;
321084860c5dSDavid du Colombier 	hp->dump = dump;
321184860c5dSDavid du Colombier 	hp->interrupt = interrupt;
321284860c5dSDavid du Colombier 	hp->epopen = epopen;
321384860c5dSDavid du Colombier 	hp->epclose = epclose;
321484860c5dSDavid du Colombier 	hp->epread = epread;
321584860c5dSDavid du Colombier 	hp->epwrite = epwrite;
321684860c5dSDavid du Colombier 	hp->seprintep = seprintep;
321784860c5dSDavid du Colombier 	hp->portenable = portenable;
321884860c5dSDavid du Colombier 	hp->portreset = portreset;
321984860c5dSDavid du Colombier 	hp->portstatus = portstatus;
322084860c5dSDavid du Colombier //	hp->shutdown = shutdown;
322184860c5dSDavid du Colombier //	hp->debug = setdebug;
322284860c5dSDavid du Colombier 	hp->type = "ehci";
322384860c5dSDavid du Colombier }
3224