xref: /plan9/sys/src/9/pc/usbuhci.c (revision 06d03f7ff63fcb3b4b1cee02b61b8ddaa5dc47ee)
1ade43d10SDavid du Colombier /*
2906943f9SDavid du Colombier  * USB Universal Host Controller Interface (sic) driver.
3906943f9SDavid du Colombier  *
4906943f9SDavid du Colombier  * BUGS:
5906943f9SDavid du Colombier  * - Too many delays and ilocks.
6906943f9SDavid du Colombier  * - bandwidth admission control must be done per-frame.
7906943f9SDavid du Colombier  * - interrupt endpoints should go on a tree like [oe]hci.
8906943f9SDavid du Colombier  * - must warn of power overruns.
9ade43d10SDavid du Colombier  */
10906943f9SDavid du Colombier 
119a747e4fSDavid du Colombier #include	"u.h"
129a747e4fSDavid du Colombier #include	"../port/lib.h"
139a747e4fSDavid du Colombier #include	"mem.h"
149a747e4fSDavid du Colombier #include	"dat.h"
159a747e4fSDavid du Colombier #include	"fns.h"
169a747e4fSDavid du Colombier #include	"io.h"
179a747e4fSDavid du Colombier #include	"../port/error.h"
1884860c5dSDavid du Colombier #include	"../port/usb.h"
199a747e4fSDavid du Colombier 
20906943f9SDavid du Colombier typedef struct Ctlio Ctlio;
21906943f9SDavid du Colombier typedef struct Ctlr Ctlr;
22906943f9SDavid du Colombier typedef struct Isoio Isoio;
23906943f9SDavid du Colombier typedef struct Qh Qh;
24906943f9SDavid du Colombier typedef struct Qhpool Qhpool;
25906943f9SDavid du Colombier typedef struct Qio Qio;
26906943f9SDavid du Colombier typedef struct Td Td;
27906943f9SDavid du Colombier typedef struct Tdpool Tdpool;
289a747e4fSDavid du Colombier 
299a747e4fSDavid du Colombier enum
309a747e4fSDavid du Colombier {
31906943f9SDavid du Colombier 	Resetdelay	= 100,		/* delay after a controller reset (ms) */
32906943f9SDavid du Colombier 	Enabledelay	= 100,		/* waiting for a port to enable */
33906943f9SDavid du Colombier 	Abortdelay	= 5,		/* delay after cancelling Tds (ms) */
34906943f9SDavid du Colombier 	Incr		= 64,		/* for Td and Qh pools */
35906943f9SDavid du Colombier 
36906943f9SDavid du Colombier 	Tdatomic	= 8,		/* max nb. of Tds per bulk I/O op. */
37906943f9SDavid du Colombier 
38906943f9SDavid du Colombier 	/* Queue states (software) */
39906943f9SDavid du Colombier 	Qidle		= 0,
40906943f9SDavid du Colombier 	Qinstall,
41906943f9SDavid du Colombier 	Qrun,
42906943f9SDavid du Colombier 	Qdone,
43906943f9SDavid du Colombier 	Qclose,
44906943f9SDavid du Colombier 	Qfree,
45906943f9SDavid du Colombier 
46ade43d10SDavid du Colombier 	/*
47906943f9SDavid du Colombier 	 * HW constants
48ade43d10SDavid du Colombier 	 */
496a5dc222SDavid du Colombier 
50906943f9SDavid du Colombier 	Nframes		= 1024,		/* 2ⁿ for xspanalloc; max 1024 */
51906943f9SDavid du Colombier 	Align		= 16,		/* for data structures */
52ade43d10SDavid du Colombier 
53906943f9SDavid du Colombier 	/* Size of small buffer kept within Tds. (software) */
54906943f9SDavid du Colombier 	/* Keep as a multiple of Align to maintain alignment of Tds in pool */
55906943f9SDavid du Colombier 	Tdndata		= 1*Align,
56906943f9SDavid du Colombier 
57906943f9SDavid du Colombier 	/* i/o space
58906943f9SDavid du Colombier 	 * Some ports are short, some are long, some are byte.
59906943f9SDavid du Colombier 	 * We use ins[bsl] and not vmap.
60906943f9SDavid du Colombier 	 */
619a747e4fSDavid du Colombier 	Cmd		= 0,
62906943f9SDavid du Colombier 		Crun		= 0x01,
63906943f9SDavid du Colombier 		Chcreset	= 0x02,	/* host controller reset */
64906943f9SDavid du Colombier 		Cgreset		= 0x04,	/* global reset */
65906943f9SDavid du Colombier 		Cegsm		= 0x08,	/* enter global suspend */
66906943f9SDavid du Colombier 		Cfgr		= 0x10,	/* forge global resume */
67906943f9SDavid du Colombier 		Cdbg		= 0x20,	/* single step, debug */
68906943f9SDavid du Colombier 		Cmaxp		= 0x80,	/* max packet */
69906943f9SDavid du Colombier 
709a747e4fSDavid du Colombier 	Status		= 2,
71906943f9SDavid du Colombier 		Susbintr		= 0x01,	/* interrupt */
72906943f9SDavid du Colombier 		Seintr		= 0x02, /* error interrupt */
73906943f9SDavid du Colombier 		Sresume		= 0x04, /* resume detect */
74906943f9SDavid du Colombier 		Shserr		= 0x08, /* host system error */
75906943f9SDavid du Colombier 		Shcerr		= 0x10, /* host controller error */
76906943f9SDavid du Colombier 		Shalted		= 0x20, /* controller halted */
77906943f9SDavid du Colombier 		Sall		= 0x3F,
78906943f9SDavid du Colombier 
799a747e4fSDavid du Colombier 	Usbintr 		= 4,
80906943f9SDavid du Colombier 		Itmout		= 0x01, /* timeout or crc */
81906943f9SDavid du Colombier 		Iresume		= 0x02, /* resume interrupt enable */
82906943f9SDavid du Colombier 		Ioc		= 0x04, /* interrupt on complete */
83906943f9SDavid du Colombier 		Ishort		= 0x08, /* short packet interrupt */
84906943f9SDavid du Colombier 		Iall		= 0x0F,
859a747e4fSDavid du Colombier 	Frnum		= 6,
869a747e4fSDavid du Colombier 	Flbaseadd 	= 8,
87906943f9SDavid du Colombier 	SOFmod		= 0xC,		/* start of frame modifier register */
88906943f9SDavid du Colombier 
899a747e4fSDavid du Colombier 	Portsc0		= 0x10,
90906943f9SDavid du Colombier 		PSpresent	= 0x0001,	/* device present */
91906943f9SDavid du Colombier 		PSstatuschg	= 0x0002,	/* PSpresent changed */
92906943f9SDavid du Colombier 		PSenable	= 0x0004,	/* device enabled */
93906943f9SDavid du Colombier 		PSchange	= 0x0008,	/* PSenable changed */
94906943f9SDavid du Colombier 		PSresume	= 0x0040,	/* resume detected */
95906943f9SDavid du Colombier 		PSreserved1	= 0x0080,	/* always read as 1; reserved */
96906943f9SDavid du Colombier 		PSslow		= 0x0100,	/* device has low speed */
97906943f9SDavid du Colombier 		PSreset		= 0x0200,	/* port reset */
98906943f9SDavid du Colombier 		PSsuspend	= 0x1000,	/* port suspended */
999a747e4fSDavid du Colombier 
100906943f9SDavid du Colombier 	/* Transfer descriptor link */
101906943f9SDavid du Colombier 	Tdterm		= 0x1,		/* nil (terminate) */
102906943f9SDavid du Colombier 	Tdlinkqh	= 0x2,			/* link refers to a QH */
103906943f9SDavid du Colombier 	Tdvf		= 0x4,		/* run linked Tds first (depth-first)*/
1049a747e4fSDavid du Colombier 
105906943f9SDavid du Colombier 	/* Transfer status bits */
106906943f9SDavid du Colombier 	Tdbitstuff	= 0x00020000,	/* bit stuffing error */
107906943f9SDavid du Colombier 	Tdcrcto		= 0x00040000,	/* crc or timeout error */
108906943f9SDavid du Colombier 	Tdnak		= 0x00080000,	/* nak packet received */
109906943f9SDavid du Colombier 	Tdbabble	= 0x00100000,	/* babble detected */
110906943f9SDavid du Colombier 	Tddberr		= 0x00200000,	/* data buf. error */
111906943f9SDavid du Colombier 	Tdstalled	= 0x00400000,	/* serious error to ep. */
112906943f9SDavid du Colombier 	Tdactive		= 0x00800000,	/* enabled/in use by hw */
113906943f9SDavid du Colombier 	/* Transfer control bits */
114906943f9SDavid du Colombier 	Tdioc		= 0x01000000,	/* interrupt on complete */
115906943f9SDavid du Colombier 	Tdiso		= 0x02000000,	/* isochronous select */
116906943f9SDavid du Colombier 	Tdlow		= 0x04000000,	/* low speed device */
117906943f9SDavid du Colombier 	Tderr1		= 0x08000000,	/* bit 0 of error counter */
118906943f9SDavid du Colombier 	Tderr2		= 0x10000000,	/* bit 1 of error counter */
119906943f9SDavid du Colombier 	Tdspd		= 0x20000000,	/* short packet detect */
1209a747e4fSDavid du Colombier 
121906943f9SDavid du Colombier 	Tdlen		= 0x000003FF,	/* actual length field */
1229a747e4fSDavid du Colombier 
123906943f9SDavid du Colombier 	Tdfatalerr	= Tdnak|Tdbabble|Tdstalled, /* hw retries others */
124906943f9SDavid du Colombier 	Tderrors	= Tdfatalerr|Tdbitstuff|Tdcrcto|Tddberr,
1259a747e4fSDavid du Colombier 
126906943f9SDavid du Colombier 	/* Transfer descriptor token bits */
127906943f9SDavid du Colombier 	Tddata0		= 0,
128906943f9SDavid du Colombier 	Tddata1		= 0x80000,	/* data toggle (1==DATA1) */
129906943f9SDavid du Colombier 	Tdtokin		= 0x69,
130906943f9SDavid du Colombier 	Tdtokout	= 0xE1,
131906943f9SDavid du Colombier 	Tdtoksetup	= 0x2D,
1329a747e4fSDavid du Colombier 
133906943f9SDavid du Colombier 	Tdmaxpkt	= 0x800,	/* max packet size */
134906943f9SDavid du Colombier 
135906943f9SDavid du Colombier 	/* Queue head bits */
136906943f9SDavid du Colombier 	QHterm		= 1<<0,		/* nil (terminate) */
137906943f9SDavid du Colombier 	QHlinkqh		= 1<<1,		/* link refers to a QH */
138906943f9SDavid du Colombier 	QHvf		= 1<<2,		/* vertical first (depth first) */
1399a747e4fSDavid du Colombier };
1409a747e4fSDavid du Colombier 
1419a747e4fSDavid du Colombier struct Ctlr
1429a747e4fSDavid du Colombier {
143906943f9SDavid du Colombier 	Lock;			/* for ilock. qh lists and basic ctlr I/O */
144906943f9SDavid du Colombier 	QLock	portlck;	/* for port resets/enable... */
1459a747e4fSDavid du Colombier 	Pcidev*	pcidev;
1469a747e4fSDavid du Colombier 	int	active;
147906943f9SDavid du Colombier 	int	port;		/* I/O address */
148906943f9SDavid du Colombier 	Qh*	qhs;		/* list of Qhs for this controller */
149906943f9SDavid du Colombier 	Qh*	qh[Tmax];	/* Dummy Qhs to insert Qhs after */
150906943f9SDavid du Colombier 	Isoio*	iso;		/* list of active iso I/O */
151906943f9SDavid du Colombier 	ulong*	frames;		/* frame list (used by hw) */
152906943f9SDavid du Colombier 	ulong	load;		/* max load for a single frame */
153906943f9SDavid du Colombier 	ulong	isoload;		/* max iso load for a single frame */
154906943f9SDavid du Colombier 	int	nintr;		/* number of interrupts attended */
155906943f9SDavid du Colombier 	int	ntdintr;		/* number of intrs. with something to do */
156906943f9SDavid du Colombier 	int	nqhintr;		/* number of intrs. for Qhs */
157906943f9SDavid du Colombier 	int	nisointr;	/* number of intrs. for iso transfers */
158906943f9SDavid du Colombier };
1599a747e4fSDavid du Colombier 
160906943f9SDavid du Colombier struct Qio
161906943f9SDavid du Colombier {
162906943f9SDavid du Colombier 	QLock;			/* for the entire I/O process */
163906943f9SDavid du Colombier 	Rendez;			/* wait for completion */
164906943f9SDavid du Colombier 	Qh*	qh;		/* Td list (field const after init) */
165906943f9SDavid du Colombier 	int	usbid;		/* usb address for endpoint/device */
166906943f9SDavid du Colombier 	int	toggle;		/* Tddata0/Tddata1 */
167906943f9SDavid du Colombier 	int	tok;		/* Tdtoksetup, Tdtokin, Tdtokout */
168906943f9SDavid du Colombier 	ulong	iotime;		/* time of last I/O */
169906943f9SDavid du Colombier 	int	debug;		/* debug flag from the endpoint */
170906943f9SDavid du Colombier 	char*	err;		/* error string */
171906943f9SDavid du Colombier };
1729a747e4fSDavid du Colombier 
173906943f9SDavid du Colombier struct Ctlio
174906943f9SDavid du Colombier {
175906943f9SDavid du Colombier 	Qio;			/* a single Qio for each RPC */
176906943f9SDavid du Colombier 	uchar*	data;		/* read from last ctl req. */
177906943f9SDavid du Colombier 	int	ndata;		/* number of bytes read */
178906943f9SDavid du Colombier };
1799a747e4fSDavid du Colombier 
180906943f9SDavid du Colombier struct Isoio
181906943f9SDavid du Colombier {
182906943f9SDavid du Colombier 	QLock;
183906943f9SDavid du Colombier 	Rendez;			/* wait for space/completion/errors */
184906943f9SDavid du Colombier 	int	usbid;		/* address used for device/endpoint */
185906943f9SDavid du Colombier 	int	tok;		/* Tdtokin or Tdtokout */
186906943f9SDavid du Colombier 	int	state;		/* Qrun -> Qdone -> Qrun... -> Qclose */
187906943f9SDavid du Colombier 	int	nframes;	/* Nframes/ep->pollival */
188906943f9SDavid du Colombier 	uchar*	data;		/* iso data buffers if not embedded */
189906943f9SDavid du Colombier 	int	td0frno;	/* frame number for first Td */
190906943f9SDavid du Colombier 	Td*	tdu;		/* next td for user I/O in tdps */
191906943f9SDavid du Colombier 	Td*	tdi;		/* next td processed by interrupt */
192906943f9SDavid du Colombier 	char*	err;		/* error string */
193906943f9SDavid du Colombier 	int	nerrs;		/* nb of consecutive I/O errors */
194906943f9SDavid du Colombier 	long	nleft;		/* number of bytes left from last write */
195906943f9SDavid du Colombier 	int	debug;		/* debug flag from the endpoint */
196906943f9SDavid du Colombier 	Isoio*	next;		/* in list of active Isoios */
197906943f9SDavid du Colombier 	Td*	tdps[Nframes];	/* pointer to Td used for i-th frame or nil */
198906943f9SDavid du Colombier };
1999a747e4fSDavid du Colombier 
200906943f9SDavid du Colombier struct Tdpool
201906943f9SDavid du Colombier {
2029a747e4fSDavid du Colombier 	Lock;
203906943f9SDavid du Colombier 	Td*	free;
204906943f9SDavid du Colombier 	int	nalloc;
205906943f9SDavid du Colombier 	int	ninuse;
206906943f9SDavid du Colombier 	int	nfree;
2079a747e4fSDavid du Colombier };
2089a747e4fSDavid du Colombier 
209906943f9SDavid du Colombier struct Qhpool
2109a747e4fSDavid du Colombier {
211906943f9SDavid du Colombier 	Lock;
212906943f9SDavid du Colombier 	Qh*	free;
213906943f9SDavid du Colombier 	int	nalloc;
214906943f9SDavid du Colombier 	int	ninuse;
215906943f9SDavid du Colombier 	int	nfree;
2169a747e4fSDavid du Colombier };
2179a747e4fSDavid du Colombier 
2189a747e4fSDavid du Colombier /*
219906943f9SDavid du Colombier  * HW data structures
2209a747e4fSDavid du Colombier  */
2219a747e4fSDavid du Colombier 
222ade43d10SDavid du Colombier /*
223906943f9SDavid du Colombier  * Queue header (known by hw).
224906943f9SDavid du Colombier  * 16-byte aligned. first two words used by hw.
225906943f9SDavid du Colombier  * They are taken from the pool upon endpoint opening and
226906943f9SDavid du Colombier  * queued after the dummy queue header for the endpoint type
227906943f9SDavid du Colombier  * in the controller. Actual I/O happens as Tds are linked into it.
228906943f9SDavid du Colombier  * The driver does I/O in lock-step.
229906943f9SDavid du Colombier  * The user builds a list of Tds and links it into the Qh,
230906943f9SDavid du Colombier  * then the Qh goes from Qidle to Qrun and nobody touches it until
231906943f9SDavid du Colombier  * it becomes Qdone at interrupt time.
232906943f9SDavid du Colombier  * At that point the user collects the Tds and it goes Qidle.
233906943f9SDavid du Colombier  * A premature cancel may set the state to Qclose and abort I/O.
234906943f9SDavid du Colombier  * The Ctlr lock protects change of state for Qhs in use.
235ade43d10SDavid du Colombier  */
236906943f9SDavid du Colombier struct Qh
2379a747e4fSDavid du Colombier {
238906943f9SDavid du Colombier 	ulong	link;		/* link to next horiz. item (eg. Qh) */
239906943f9SDavid du Colombier 	ulong	elink;		/* link to element (eg. Td; updated by hw) */
2409a747e4fSDavid du Colombier 
241906943f9SDavid du Colombier 	ulong	state;		/* Qidle -> Qinstall -> Qrun -> Qdone | Qclose */
242906943f9SDavid du Colombier 	Qio*	io;		/* for this queue */
2439a747e4fSDavid du Colombier 
244906943f9SDavid du Colombier 	Qh*	next;		/* in active or free list */
245906943f9SDavid du Colombier 	Td*	tds;		/* Td list in this Qh (initially, elink) */
246906943f9SDavid du Colombier 	char*	tag;		/* debug and align, mostly */
247906943f9SDavid du Colombier 	ulong	align;
248906943f9SDavid du Colombier };
2499a747e4fSDavid du Colombier 
250906943f9SDavid du Colombier /*
251906943f9SDavid du Colombier  * Transfer descriptor.
252906943f9SDavid du Colombier  * 16-byte aligned. first two words used by hw. Next 4 by sw.
253906943f9SDavid du Colombier  * We keep an embedded buffer for small I/O transfers.
254906943f9SDavid du Colombier  * They are taken from the pool when buffers are needed for I/O
255906943f9SDavid du Colombier  * and linked at the Qh/Isoio for the endpoint and direction requiring it.
256906943f9SDavid du Colombier  * The block keeps actual data. They are protected from races by
257906943f9SDavid du Colombier  * the queue or the pool keeping it. The owner of the link to the Td
258906943f9SDavid du Colombier  * is free to use it and can be the only one using it.
2599a747e4fSDavid du Colombier  */
260906943f9SDavid du Colombier struct Td
261906943f9SDavid du Colombier {
262906943f9SDavid du Colombier 	ulong	link;		/* Link to next Td or Qh */
263906943f9SDavid du Colombier 	ulong	csw;		/* control and status word (updated by hw) */
264906943f9SDavid du Colombier 	ulong	token;		/* endpt, device, pid */
265906943f9SDavid du Colombier 	ulong	buffer;		/* buffer pointer */
2669a747e4fSDavid du Colombier 
267906943f9SDavid du Colombier 	Td*	next;		/* in qh or Isoio or free list */
268906943f9SDavid du Colombier 	ulong	ndata;		/* bytes available/used at data */
269906943f9SDavid du Colombier 	uchar*	data;		/* pointer to actual data */
270906943f9SDavid du Colombier 	void*	buff;		/* allocated data, for large transfers */
271906943f9SDavid du Colombier 
272906943f9SDavid du Colombier 	uchar	sbuff[Tdndata];	/* embedded buffer, for small transfers */
273906943f9SDavid du Colombier };
274906943f9SDavid du Colombier 
275906943f9SDavid du Colombier #define INB(x)		inb(ctlr->port+(x))
276906943f9SDavid du Colombier #define	INS(x)		ins(ctlr->port+(x))
277906943f9SDavid du Colombier #define INL(x)		inl(ctlr->port+(x))
278906943f9SDavid du Colombier #define OUTB(x, v)	outb(ctlr->port+(x), (v))
279906943f9SDavid du Colombier #define	OUTS(x, v)	outs(ctlr->port+(x), (v))
280906943f9SDavid du Colombier #define OUTL(x, v)	outl(ctlr->port+(x), (v))
281906943f9SDavid du Colombier #define TRUNC(x, sz)	((x) & ((sz)-1))
282906943f9SDavid du Colombier #define PTR(q)		((void*)KADDR((ulong)(q) & ~ (0xF|PCIWINDOW)))
283906943f9SDavid du Colombier #define QPTR(q)		((Qh*)PTR(q))
284906943f9SDavid du Colombier #define TPTR(q)		((Td*)PTR(q))
285906943f9SDavid du Colombier #define PORT(p)		(Portsc0 + 2*(p))
286906943f9SDavid du Colombier #define diprint		if(debug || iso->debug)print
287906943f9SDavid du Colombier #define ddiprint		if(debug>1 || iso->debug>1)print
288906943f9SDavid du Colombier #define dqprint		if(debug || (qh->io && qh->io->debug))print
289906943f9SDavid du Colombier #define ddqprint		if(debug>1 || (qh->io && qh->io->debug>1))print
290906943f9SDavid du Colombier 
291906943f9SDavid du Colombier static Ctlr* ctlrs[Nhcis];
292906943f9SDavid du Colombier 
293906943f9SDavid du Colombier static Tdpool tdpool;
294906943f9SDavid du Colombier static Qhpool qhpool;
295906943f9SDavid du Colombier static int debug;
296906943f9SDavid du Colombier 
297906943f9SDavid du Colombier static char* qhsname[] = { "idle", "install", "run", "done", "close", "FREE" };
2989a747e4fSDavid du Colombier 
2999a747e4fSDavid du Colombier static void
uhcicmd(Ctlr * ctlr,int c)300906943f9SDavid du Colombier uhcicmd(Ctlr *ctlr, int c)
3019a747e4fSDavid du Colombier {
302906943f9SDavid du Colombier 	OUTS(Cmd, c);
3039a747e4fSDavid du Colombier }
3049a747e4fSDavid du Colombier 
3059a747e4fSDavid du Colombier static void
uhcirun(Ctlr * ctlr,int on)306906943f9SDavid du Colombier uhcirun(Ctlr *ctlr, int on)
3079a747e4fSDavid du Colombier {
308906943f9SDavid du Colombier 	int i;
3099a747e4fSDavid du Colombier 
310906943f9SDavid du Colombier 	ddprint("uhci %#ux setting run to %d\n", ctlr->port, on);
3119a747e4fSDavid du Colombier 
3129a747e4fSDavid du Colombier 	if(on)
313906943f9SDavid du Colombier 		uhcicmd(ctlr, INS(Cmd)|Crun);
3149a747e4fSDavid du Colombier 	else
315906943f9SDavid du Colombier 		uhcicmd(ctlr, INS(Cmd) & ~Crun);
316906943f9SDavid du Colombier 	for(i = 0; i < 100; i++)
317906943f9SDavid du Colombier 		if(on == 0 && (INS(Status) & Shalted) != 0)
318906943f9SDavid du Colombier 			break;
319906943f9SDavid du Colombier 		else if(on != 0 && (INS(Status) & Shalted) == 0)
320906943f9SDavid du Colombier 			break;
321906943f9SDavid du Colombier 		else
322906943f9SDavid du Colombier 			delay(1);
323906943f9SDavid du Colombier 	if(i == 100)
324906943f9SDavid du Colombier 		dprint("uhci %#x run cmd timed out\n", ctlr->port);
325906943f9SDavid du Colombier 	ddprint("uhci %#ux cmd %#ux sts %#ux\n",
326906943f9SDavid du Colombier 		ctlr->port, INS(Cmd), INS(Status));
327906943f9SDavid du Colombier }
328906943f9SDavid du Colombier 
329906943f9SDavid du Colombier static int
tdlen(Td * td)330906943f9SDavid du Colombier tdlen(Td *td)
331906943f9SDavid du Colombier {
332906943f9SDavid du Colombier 	return (td->csw+1) & Tdlen;
333906943f9SDavid du Colombier }
334906943f9SDavid du Colombier 
335906943f9SDavid du Colombier static int
maxtdlen(Td * td)336906943f9SDavid du Colombier maxtdlen(Td *td)
337906943f9SDavid du Colombier {
338906943f9SDavid du Colombier 	return ((td->token>>21)+1) & (Tdmaxpkt-1);
339906943f9SDavid du Colombier }
340906943f9SDavid du Colombier 
341906943f9SDavid du Colombier static int
tdtok(Td * td)342906943f9SDavid du Colombier tdtok(Td *td)
343906943f9SDavid du Colombier {
344906943f9SDavid du Colombier 	return td->token & 0xFF;
345906943f9SDavid du Colombier }
346906943f9SDavid du Colombier 
347906943f9SDavid du Colombier static char*
seprinttd(char * s,char * se,Td * td)348906943f9SDavid du Colombier seprinttd(char *s, char *se, Td *td)
349906943f9SDavid du Colombier {
350906943f9SDavid du Colombier 	s = seprint(s, se, "%#p link %#ulx", td, td->link);
351906943f9SDavid du Colombier 	if((td->link & Tdvf) != 0)
352906943f9SDavid du Colombier 		s = seprint(s, se, "V");
353906943f9SDavid du Colombier 	if((td->link & Tdterm) != 0)
354906943f9SDavid du Colombier 		s = seprint(s, se, "T");
355906943f9SDavid du Colombier 	if((td->link & Tdlinkqh) != 0)
356906943f9SDavid du Colombier 		s = seprint(s, se, "Q");
357906943f9SDavid du Colombier 	s = seprint(s, se, " csw %#ulx ", td->csw);
358906943f9SDavid du Colombier 	if(td->csw & Tdactive)
359906943f9SDavid du Colombier 		s = seprint(s, se, "a");
360906943f9SDavid du Colombier 	if(td->csw & Tdiso)
361906943f9SDavid du Colombier 		s = seprint(s, se, "I");
362906943f9SDavid du Colombier 	if(td->csw & Tdioc)
363906943f9SDavid du Colombier 		s = seprint(s, se, "i");
364906943f9SDavid du Colombier 	if(td->csw & Tdlow)
365906943f9SDavid du Colombier 		s = seprint(s, se, "l");
366906943f9SDavid du Colombier 	if((td->csw & (Tderr1|Tderr2)) == 0)
367906943f9SDavid du Colombier 		s = seprint(s, se, "z");
368906943f9SDavid du Colombier 	if(td->csw & Tderrors)
369906943f9SDavid du Colombier 		s = seprint(s, se, " err %#ulx", td->csw & Tderrors);
370906943f9SDavid du Colombier 	if(td->csw & Tdstalled)
371906943f9SDavid du Colombier 		s = seprint(s, se, "s");
372906943f9SDavid du Colombier 	if(td->csw & Tddberr)
373906943f9SDavid du Colombier 		s = seprint(s, se, "d");
374906943f9SDavid du Colombier 	if(td->csw & Tdbabble)
375906943f9SDavid du Colombier 		s = seprint(s, se, "b");
376906943f9SDavid du Colombier 	if(td->csw & Tdnak)
377906943f9SDavid du Colombier 		s = seprint(s, se, "n");
378906943f9SDavid du Colombier 	if(td->csw & Tdcrcto)
379906943f9SDavid du Colombier 		s = seprint(s, se, "c");
380906943f9SDavid du Colombier 	if(td->csw & Tdbitstuff)
381906943f9SDavid du Colombier 		s = seprint(s, se, "B");
382906943f9SDavid du Colombier 	s = seprint(s, se, " stslen %d", tdlen(td));
383906943f9SDavid du Colombier 
384906943f9SDavid du Colombier 	s = seprint(s, se, " token %#ulx", td->token);
385906943f9SDavid du Colombier 	if(td->token == 0)		/* the BWS loopback Td, ignore rest */
386906943f9SDavid du Colombier 		return s;
387906943f9SDavid du Colombier 	s = seprint(s, se, " maxlen %d", maxtdlen(td));
388906943f9SDavid du Colombier 	if(td->token & Tddata1)
389906943f9SDavid du Colombier 		s = seprint(s, se, " d1");
390906943f9SDavid du Colombier 	else
391906943f9SDavid du Colombier 		s = seprint(s, se, " d0");
392906943f9SDavid du Colombier 	s = seprint(s, se, " id %#ulx:", (td->token>>15) & Epmax);
393906943f9SDavid du Colombier 	s = seprint(s, se, "%#ulx", (td->token>>8) & Devmax);
394906943f9SDavid du Colombier 	switch(tdtok(td)){
395906943f9SDavid du Colombier 	case Tdtokin:
396906943f9SDavid du Colombier 		s = seprint(s, se, " in");
397906943f9SDavid du Colombier 		break;
398906943f9SDavid du Colombier 	case Tdtokout:
399906943f9SDavid du Colombier 		s = seprint(s, se, " out");
400906943f9SDavid du Colombier 		break;
401906943f9SDavid du Colombier 	case Tdtoksetup:
402906943f9SDavid du Colombier 		s = seprint(s, se, " setup");
403906943f9SDavid du Colombier 		break;
404906943f9SDavid du Colombier 	default:
405906943f9SDavid du Colombier 		s = seprint(s, se, " BADPID");
406906943f9SDavid du Colombier 	}
407906943f9SDavid du Colombier 	s = seprint(s, se, "\n\t  buffer %#ulx data %#p", td->buffer, td->data);
408906943f9SDavid du Colombier 	s = seprint(s, se, " ndata %uld sbuff %#p buff %#p",
409906943f9SDavid du Colombier 		td->ndata, td->sbuff, td->buff);
410906943f9SDavid du Colombier 	if(td->ndata > 0)
411906943f9SDavid du Colombier 		s = seprintdata(s, se, td->data, td->ndata);
412906943f9SDavid du Colombier 	return s;
4139a747e4fSDavid du Colombier }
4149a747e4fSDavid du Colombier 
4159a747e4fSDavid du Colombier static void
isodump(Isoio * iso,int all)416906943f9SDavid du Colombier isodump(Isoio *iso, int all)
4179a747e4fSDavid du Colombier {
418906943f9SDavid du Colombier 	char buf[256];
419906943f9SDavid du Colombier 	Td *td;
420906943f9SDavid du Colombier 	int i;
4219a747e4fSDavid du Colombier 
422906943f9SDavid du Colombier 	print("iso %#p %s state %d nframes %d"
423906943f9SDavid du Colombier 		" td0 %#p tdu %#p tdi %#p data %#p\n",
424906943f9SDavid du Colombier 		iso, iso->tok == Tdtokin ? "in" : "out",
425906943f9SDavid du Colombier 		iso->state, iso->nframes, iso->tdps[iso->td0frno],
426906943f9SDavid du Colombier 		iso->tdu, iso->tdi, iso->data);
427906943f9SDavid du Colombier 	if(iso->err != nil)
428906943f9SDavid du Colombier 		print("\terr='%s'\n", iso->err);
429906943f9SDavid du Colombier 	if(all == 0){
430906943f9SDavid du Colombier 		seprinttd(buf, buf+sizeof(buf), iso->tdu);
431906943f9SDavid du Colombier 		print("\ttdu %s\n", buf);
432906943f9SDavid du Colombier 		seprinttd(buf, buf+sizeof(buf), iso->tdi);
433906943f9SDavid du Colombier 		print("\ttdi %s\n", buf);
4349a747e4fSDavid du Colombier 	}else{
435906943f9SDavid du Colombier 		td = iso->tdps[iso->td0frno];
436906943f9SDavid du Colombier 		for(i = 0; i < iso->nframes; i++){
437906943f9SDavid du Colombier 			seprinttd(buf, buf+sizeof(buf), td);
438906943f9SDavid du Colombier 			if(td == iso->tdi)
439906943f9SDavid du Colombier 				print("i->");
440906943f9SDavid du Colombier 			if(td == iso->tdu)
441906943f9SDavid du Colombier 				print("u->");
442906943f9SDavid du Colombier 			print("\t%s\n", buf);
4439a747e4fSDavid du Colombier 			td = td->next;
444906943f9SDavid du Colombier 		}
445906943f9SDavid du Colombier 	}
446906943f9SDavid du Colombier }
447906943f9SDavid du Colombier 
448906943f9SDavid du Colombier static int
sameptr(void * p,ulong l)449906943f9SDavid du Colombier sameptr(void *p, ulong l)
450906943f9SDavid du Colombier {
451906943f9SDavid du Colombier 	if(l & QHterm)
452906943f9SDavid du Colombier 		return p == nil;
453906943f9SDavid du Colombier 	return PTR(l) == p;
454906943f9SDavid du Colombier }
455906943f9SDavid du Colombier 
456906943f9SDavid du Colombier static void
dumptd(Td * td,char * pref)457906943f9SDavid du Colombier dumptd(Td *td, char *pref)
458906943f9SDavid du Colombier {
459906943f9SDavid du Colombier 	char buf[256];
460906943f9SDavid du Colombier 	char *s;
461906943f9SDavid du Colombier 	char *se;
462906943f9SDavid du Colombier 	int i;
463906943f9SDavid du Colombier 
464906943f9SDavid du Colombier 	i = 0;
465906943f9SDavid du Colombier 	se = buf+sizeof(buf);
466906943f9SDavid du Colombier 	for(; td != nil; td = td->next){
467906943f9SDavid du Colombier 		s = seprinttd(buf, se, td);
468906943f9SDavid du Colombier 		if(!sameptr(td->next, td->link))
469906943f9SDavid du Colombier 			seprint(s, se, " next %#p != link %#ulx %#p",
470906943f9SDavid du Colombier 				td->next, td->link, TPTR(td->link));
471906943f9SDavid du Colombier 		print("%std %s\n", pref, buf);
472906943f9SDavid du Colombier 		if(i++ > 20){
473906943f9SDavid du Colombier 			print("...more tds...\n");
4749a747e4fSDavid du Colombier 			break;
4759a747e4fSDavid du Colombier 		}
476906943f9SDavid du Colombier 	}
477906943f9SDavid du Colombier }
4789a747e4fSDavid du Colombier 
479906943f9SDavid du Colombier static void
qhdump(Qh * qh,char * pref)480906943f9SDavid du Colombier qhdump(Qh *qh, char *pref)
481906943f9SDavid du Colombier {
482906943f9SDavid du Colombier 	char buf[256];
483906943f9SDavid du Colombier 	char *s;
484906943f9SDavid du Colombier 	char *se;
485906943f9SDavid du Colombier 	ulong td;
486906943f9SDavid du Colombier 	int i;
487906943f9SDavid du Colombier 
488906943f9SDavid du Colombier 	s = buf;
489906943f9SDavid du Colombier 	se = buf+sizeof(buf);
490906943f9SDavid du Colombier 	s = seprint(s, se, "%sqh %s %#p state %s link %#ulx", pref,
491906943f9SDavid du Colombier 		qh->tag, qh, qhsname[qh->state], qh->link);
492906943f9SDavid du Colombier 	if(!sameptr(qh->tds, qh->elink))
493906943f9SDavid du Colombier 		s = seprint(s, se, " [tds %#p != elink %#ulx %#p]",
494906943f9SDavid du Colombier 			qh->tds, qh->elink, TPTR(qh->elink));
495906943f9SDavid du Colombier 	if(!sameptr(qh->next, qh->link))
496906943f9SDavid du Colombier 		s = seprint(s, se, " [next %#p != link %#ulx %#p]",
497906943f9SDavid du Colombier 			qh->next, qh->link, QPTR(qh->link));
498906943f9SDavid du Colombier 	if((qh->link & Tdterm) != 0)
499906943f9SDavid du Colombier 		s = seprint(s, se, "T");
500906943f9SDavid du Colombier 	if((qh->link & Tdlinkqh) != 0)
501906943f9SDavid du Colombier 		s = seprint(s, se, "Q");
502906943f9SDavid du Colombier 	s = seprint(s, se, " elink %#ulx", qh->elink);
503906943f9SDavid du Colombier 	if((qh->elink & Tdterm) != 0)
504906943f9SDavid du Colombier 		s = seprint(s, se, "T");
505906943f9SDavid du Colombier 	if((qh->elink & Tdlinkqh) != 0)
506906943f9SDavid du Colombier 		s = seprint(s, se, "Q");
507906943f9SDavid du Colombier 	s = seprint(s, se, " io %#p", qh->io);
508906943f9SDavid du Colombier 	if(qh->io != nil && qh->io->err != nil)
509906943f9SDavid du Colombier 		seprint(s, se, " err='%s'", qh->io->err);
510906943f9SDavid du Colombier 	print("%s\n", buf);
511906943f9SDavid du Colombier 	dumptd(qh->tds, "\t");
512906943f9SDavid du Colombier 	if((qh->elink & QHterm) == 0){
513906943f9SDavid du Colombier 		print("\thw tds:");
514906943f9SDavid du Colombier 		i = 0;
515906943f9SDavid du Colombier 		for(td = qh->elink; (td & Tdterm) == 0; td = TPTR(td)->link){
516906943f9SDavid du Colombier 			print(" %#ulx", td);
517906943f9SDavid du Colombier 			if(td == TPTR(td)->link)	/* BWS Td */
518906943f9SDavid du Colombier 				break;
519906943f9SDavid du Colombier 			if(i++ > 40){
520906943f9SDavid du Colombier 				print("...");
521906943f9SDavid du Colombier 				break;
522906943f9SDavid du Colombier 			}
523906943f9SDavid du Colombier 		}
524906943f9SDavid du Colombier 		print("\n");
525906943f9SDavid du Colombier 	}
526906943f9SDavid du Colombier }
527906943f9SDavid du Colombier 
528906943f9SDavid du Colombier static void
xdump(Ctlr * ctlr,int doilock)529906943f9SDavid du Colombier xdump(Ctlr *ctlr, int doilock)
530906943f9SDavid du Colombier {
531906943f9SDavid du Colombier 	Isoio *iso;
532906943f9SDavid du Colombier 	Qh *qh;
533906943f9SDavid du Colombier 	int i;
534906943f9SDavid du Colombier 
535906943f9SDavid du Colombier 	if(doilock){
536906943f9SDavid du Colombier 		if(ctlr == ctlrs[0]){
537906943f9SDavid du Colombier 			lock(&tdpool);
538906943f9SDavid du Colombier 			print("tds: alloc %d = inuse %d + free %d\n",
539906943f9SDavid du Colombier 				tdpool.nalloc, tdpool.ninuse, tdpool.nfree);
540906943f9SDavid du Colombier 			unlock(&tdpool);
541906943f9SDavid du Colombier 			lock(&qhpool);
542906943f9SDavid du Colombier 			print("qhs: alloc %d = inuse %d + free %d\n",
543906943f9SDavid du Colombier 				qhpool.nalloc, qhpool.ninuse, qhpool.nfree);
544906943f9SDavid du Colombier 			unlock(&qhpool);
545906943f9SDavid du Colombier 		}
546906943f9SDavid du Colombier 		ilock(ctlr);
547906943f9SDavid du Colombier 	}
548906943f9SDavid du Colombier 	print("uhci port %#x frames %#p nintr %d ntdintr %d",
549906943f9SDavid du Colombier 		ctlr->port, ctlr->frames, ctlr->nintr, ctlr->ntdintr);
550906943f9SDavid du Colombier 	print(" nqhintr %d nisointr %d\n", ctlr->nqhintr, ctlr->nisointr);
551906943f9SDavid du Colombier 	print("cmd %#ux sts %#ux fl %#ulx ps1 %#ux ps2 %#ux frames[0] %#ulx\n",
552906943f9SDavid du Colombier 		INS(Cmd), INS(Status),
553906943f9SDavid du Colombier 		INL(Flbaseadd), INS(PORT(0)), INS(PORT(1)),
554906943f9SDavid du Colombier 		ctlr->frames[0]);
555906943f9SDavid du Colombier 	for(iso = ctlr->iso; iso != nil; iso = iso->next)
556906943f9SDavid du Colombier 		isodump(iso, 1);
557906943f9SDavid du Colombier 	i = 0;
558906943f9SDavid du Colombier 	for(qh = ctlr->qhs; qh != nil; qh = qh->next){
559906943f9SDavid du Colombier 		qhdump(qh, "");
560906943f9SDavid du Colombier 		if(i++ > 20){
561906943f9SDavid du Colombier 			print("qhloop\n");
562906943f9SDavid du Colombier 			break;
563906943f9SDavid du Colombier 		}
564906943f9SDavid du Colombier 	}
565906943f9SDavid du Colombier 	print("\n");
566906943f9SDavid du Colombier 	if(doilock)
567906943f9SDavid du Colombier 		iunlock(ctlr);
568906943f9SDavid du Colombier }
569906943f9SDavid du Colombier 
570906943f9SDavid du Colombier static void
dump(Hci * hp)571906943f9SDavid du Colombier dump(Hci *hp)
572906943f9SDavid du Colombier {
573906943f9SDavid du Colombier 	xdump(hp->aux, 1);
574906943f9SDavid du Colombier }
575906943f9SDavid du Colombier 
576906943f9SDavid du Colombier static Td*
tdalloc(void)577906943f9SDavid du Colombier tdalloc(void)
578906943f9SDavid du Colombier {
579906943f9SDavid du Colombier 	int i;
580906943f9SDavid du Colombier 	Td *td;
581906943f9SDavid du Colombier 	Td *pool;
582906943f9SDavid du Colombier 
583906943f9SDavid du Colombier 	lock(&tdpool);
584906943f9SDavid du Colombier 	if(tdpool.free == nil){
585906943f9SDavid du Colombier 		ddprint("uhci: tdalloc %d Tds\n", Incr);
586906943f9SDavid du Colombier 		pool = xspanalloc(Incr*sizeof(Td), Align, 0);
587906943f9SDavid du Colombier 		if(pool == nil)
588906943f9SDavid du Colombier 			panic("tdalloc");
589906943f9SDavid du Colombier 		for(i=Incr; --i>=0;){
590906943f9SDavid du Colombier 			pool[i].next = tdpool.free;
591906943f9SDavid du Colombier 			tdpool.free = &pool[i];
592906943f9SDavid du Colombier 		}
593906943f9SDavid du Colombier 		tdpool.nalloc += Incr;
594906943f9SDavid du Colombier 		tdpool.nfree += Incr;
595906943f9SDavid du Colombier 	}
596906943f9SDavid du Colombier 	td = tdpool.free;
597906943f9SDavid du Colombier 	tdpool.free = td->next;
598906943f9SDavid du Colombier 	tdpool.ninuse++;
599906943f9SDavid du Colombier 	tdpool.nfree--;
600906943f9SDavid du Colombier 	unlock(&tdpool);
601906943f9SDavid du Colombier 
602906943f9SDavid du Colombier 	memset(td, 0, sizeof(Td));
603906943f9SDavid du Colombier 	td->link = Tdterm;
604906943f9SDavid du Colombier 	assert(((ulong)td & 0xF) == 0);
605906943f9SDavid du Colombier 	return td;
606906943f9SDavid du Colombier }
607906943f9SDavid du Colombier 
608906943f9SDavid du Colombier static void
tdfree(Td * td)609906943f9SDavid du Colombier tdfree(Td *td)
610906943f9SDavid du Colombier {
611906943f9SDavid du Colombier 	if(td == nil)
612906943f9SDavid du Colombier 		return;
613906943f9SDavid du Colombier 	free(td->buff);
614906943f9SDavid du Colombier 	td->buff = nil;
615906943f9SDavid du Colombier 	lock(&tdpool);
616906943f9SDavid du Colombier 	td->next = tdpool.free;
617906943f9SDavid du Colombier 	tdpool.free = td;
618906943f9SDavid du Colombier 	tdpool.ninuse--;
619906943f9SDavid du Colombier 	tdpool.nfree++;
620906943f9SDavid du Colombier 	unlock(&tdpool);
621906943f9SDavid du Colombier }
622906943f9SDavid du Colombier 
623906943f9SDavid du Colombier static void
qhlinkqh(Qh * qh,Qh * next)624906943f9SDavid du Colombier qhlinkqh(Qh* qh, Qh* next)
625906943f9SDavid du Colombier {
626906943f9SDavid du Colombier 	if(next == nil)
627906943f9SDavid du Colombier 		qh->link = QHterm;
628906943f9SDavid du Colombier 	else{
629906943f9SDavid du Colombier 		next->link = qh->link;
630906943f9SDavid du Colombier 		next->next = qh->next;
631906943f9SDavid du Colombier 		qh->link = PCIWADDR(next)|QHlinkqh;
632906943f9SDavid du Colombier 	}
633906943f9SDavid du Colombier 	qh->next = next;
634906943f9SDavid du Colombier }
635906943f9SDavid du Colombier 
636906943f9SDavid du Colombier static void
qhlinktd(Qh * qh,Td * td)637906943f9SDavid du Colombier qhlinktd(Qh *qh, Td *td)
638906943f9SDavid du Colombier {
639906943f9SDavid du Colombier 	qh->tds = td;
640906943f9SDavid du Colombier 	if(td == nil)
641906943f9SDavid du Colombier 		qh->elink = QHvf|QHterm;
6429a747e4fSDavid du Colombier 	else
643906943f9SDavid du Colombier 		qh->elink = PCIWADDR(td);
6449a747e4fSDavid du Colombier }
645906943f9SDavid du Colombier 
646906943f9SDavid du Colombier static void
tdlinktd(Td * td,Td * next)647906943f9SDavid du Colombier tdlinktd(Td *td, Td *next)
648906943f9SDavid du Colombier {
649906943f9SDavid du Colombier 	td->next = next;
650906943f9SDavid du Colombier 	if(next == nil)
651906943f9SDavid du Colombier 		td->link = Tdterm;
652906943f9SDavid du Colombier 	else
653906943f9SDavid du Colombier 		td->link = PCIWADDR(next)|Tdvf;
654906943f9SDavid du Colombier }
655906943f9SDavid du Colombier 
656906943f9SDavid du Colombier static Qh*
qhalloc(Ctlr * ctlr,Qh * prev,Qio * io,char * tag)657906943f9SDavid du Colombier qhalloc(Ctlr *ctlr, Qh *prev, Qio *io, char *tag)
658906943f9SDavid du Colombier {
659906943f9SDavid du Colombier 	int i;
660906943f9SDavid du Colombier 	Qh *qh;
661906943f9SDavid du Colombier 	Qh *pool;
662906943f9SDavid du Colombier 
663906943f9SDavid du Colombier 	lock(&qhpool);
664906943f9SDavid du Colombier 	if(qhpool.free == nil){
665906943f9SDavid du Colombier 		ddprint("uhci: qhalloc %d Qhs\n", Incr);
666906943f9SDavid du Colombier 		pool = xspanalloc(Incr*sizeof(Qh), Align, 0);
667906943f9SDavid du Colombier 		if(pool == nil)
668906943f9SDavid du Colombier 			panic("qhalloc");
669906943f9SDavid du Colombier 		for(i=Incr; --i>=0;){
670906943f9SDavid du Colombier 			pool[i].next = qhpool.free;
671906943f9SDavid du Colombier 			qhpool.free = &pool[i];
672906943f9SDavid du Colombier 		}
673906943f9SDavid du Colombier 		qhpool.nalloc += Incr;
674906943f9SDavid du Colombier 		qhpool.nfree += Incr;
675906943f9SDavid du Colombier 	}
676906943f9SDavid du Colombier 	qh = qhpool.free;
677906943f9SDavid du Colombier 	qhpool.free = qh->next;
678906943f9SDavid du Colombier 	qh->next = nil;
679906943f9SDavid du Colombier 	qh->link = QHterm;
680906943f9SDavid du Colombier 	qhpool.ninuse++;
681906943f9SDavid du Colombier 	qhpool.nfree--;
682906943f9SDavid du Colombier 	unlock(&qhpool);
683906943f9SDavid du Colombier 
684906943f9SDavid du Colombier 	qh->tds = nil;
685906943f9SDavid du Colombier 	qh->elink = QHterm;
686906943f9SDavid du Colombier 	qh->state = Qidle;
687906943f9SDavid du Colombier 	qh->io = io;
688906943f9SDavid du Colombier 	qh->tag = nil;
689906943f9SDavid du Colombier 	kstrdup(&qh->tag, tag);
690906943f9SDavid du Colombier 
691906943f9SDavid du Colombier 	if(prev != nil){
692906943f9SDavid du Colombier 		coherence();
693906943f9SDavid du Colombier 		ilock(ctlr);
694906943f9SDavid du Colombier 		qhlinkqh(prev, qh);
695906943f9SDavid du Colombier 		iunlock(ctlr);
696906943f9SDavid du Colombier 	}
697906943f9SDavid du Colombier 
698906943f9SDavid du Colombier 	assert(((ulong)qh & 0xF) == 0);
699906943f9SDavid du Colombier 	return qh;
700906943f9SDavid du Colombier }
701906943f9SDavid du Colombier 
702906943f9SDavid du Colombier static void
qhfree(Ctlr * ctlr,Qh * qh)703906943f9SDavid du Colombier qhfree(Ctlr *ctlr, Qh *qh)
704906943f9SDavid du Colombier {
705906943f9SDavid du Colombier 	Td *td;
706906943f9SDavid du Colombier 	Td *ltd;
707906943f9SDavid du Colombier 	Qh *q;
708906943f9SDavid du Colombier 
709906943f9SDavid du Colombier 	if(qh == nil)
710906943f9SDavid du Colombier 		return;
711906943f9SDavid du Colombier 
712906943f9SDavid du Colombier 	ilock(ctlr);
713906943f9SDavid du Colombier 	for(q = ctlr->qhs; q != nil; q = q->next)
714906943f9SDavid du Colombier 		if(q->next == qh)
715906943f9SDavid du Colombier 			break;
716906943f9SDavid du Colombier 	if(q == nil)
717906943f9SDavid du Colombier 		panic("qhfree: nil q");
718906943f9SDavid du Colombier 	q->next = qh->next;
719906943f9SDavid du Colombier 	q->link = qh->link;
720906943f9SDavid du Colombier 	iunlock(ctlr);
721906943f9SDavid du Colombier 
722906943f9SDavid du Colombier 	for(td = qh->tds; td != nil; td = ltd){
723906943f9SDavid du Colombier 		ltd = td->next;
724906943f9SDavid du Colombier 		tdfree(td);
725906943f9SDavid du Colombier 	}
726906943f9SDavid du Colombier 	lock(&qhpool);
727906943f9SDavid du Colombier 	qh->state = Qfree;	/* paranoia */
728906943f9SDavid du Colombier 	qh->next = qhpool.free;
729906943f9SDavid du Colombier 	qh->tag = nil;
730906943f9SDavid du Colombier 	qh->io = nil;
731906943f9SDavid du Colombier 	qhpool.free = qh;
732906943f9SDavid du Colombier 	qhpool.ninuse--;
733906943f9SDavid du Colombier 	qhpool.nfree++;
734906943f9SDavid du Colombier 	unlock(&qhpool);
735906943f9SDavid du Colombier 	ddprint("qhfree: qh %#p\n", qh);
736906943f9SDavid du Colombier }
737906943f9SDavid du Colombier 
738906943f9SDavid du Colombier static char*
errmsg(int err)739906943f9SDavid du Colombier errmsg(int err)
740906943f9SDavid du Colombier {
741906943f9SDavid du Colombier 	if(err == 0)
742906943f9SDavid du Colombier 		return "ok";
743906943f9SDavid du Colombier 	if(err & Tdcrcto)
744906943f9SDavid du Colombier 		return "crc/timeout error";
745906943f9SDavid du Colombier 	if(err & Tdbabble)
746906943f9SDavid du Colombier 		return "babble detected";
747906943f9SDavid du Colombier 	if(err & Tddberr)
748906943f9SDavid du Colombier 		return "db error";
749906943f9SDavid du Colombier 	if(err & Tdbitstuff)
750906943f9SDavid du Colombier 		return "bit stuffing error";
751906943f9SDavid du Colombier 	if(err & Tdstalled)
752906943f9SDavid du Colombier 		return Estalled;
753906943f9SDavid du Colombier 	return Eio;
754906943f9SDavid du Colombier }
755906943f9SDavid du Colombier 
756906943f9SDavid du Colombier static int
isocanread(void * a)757906943f9SDavid du Colombier isocanread(void *a)
758906943f9SDavid du Colombier {
759906943f9SDavid du Colombier 	Isoio *iso;
760906943f9SDavid du Colombier 
761906943f9SDavid du Colombier 	iso = a;
762906943f9SDavid du Colombier 	return iso->state == Qclose ||
763906943f9SDavid du Colombier 		(iso->state == Qrun &&
764906943f9SDavid du Colombier 		iso->tok == Tdtokin && iso->tdi != iso->tdu);
765906943f9SDavid du Colombier }
766906943f9SDavid du Colombier 
767906943f9SDavid du Colombier static int
isocanwrite(void * a)768906943f9SDavid du Colombier isocanwrite(void *a)
769906943f9SDavid du Colombier {
770906943f9SDavid du Colombier 	Isoio *iso;
771906943f9SDavid du Colombier 
772906943f9SDavid du Colombier 	iso = a;
773906943f9SDavid du Colombier 	return iso->state == Qclose ||
774906943f9SDavid du Colombier 		(iso->state == Qrun &&
775906943f9SDavid du Colombier 		iso->tok == Tdtokout && iso->tdu->next != iso->tdi);
776906943f9SDavid du Colombier }
777906943f9SDavid du Colombier 
778906943f9SDavid du Colombier static void
tdisoinit(Isoio * iso,Td * td,long count)779906943f9SDavid du Colombier tdisoinit(Isoio *iso, Td *td, long count)
780906943f9SDavid du Colombier {
781906943f9SDavid du Colombier 	td->ndata = count;
782906943f9SDavid du Colombier 	td->token = ((count-1)<<21)| ((iso->usbid & 0x7FF)<<8) | iso->tok;
783906943f9SDavid du Colombier 	td->csw = Tderr1|Tdiso|Tdactive|Tdioc;
784906943f9SDavid du Colombier }
785906943f9SDavid du Colombier 
786906943f9SDavid du Colombier /*
787906943f9SDavid du Colombier  * Process Iso i/o on interrupt. For writes update just error status.
788906943f9SDavid du Colombier  * For reads update tds to reflect data and also error status.
789906943f9SDavid du Colombier  * When tdi aproaches tdu, advance tdu; data may be lost.
790906943f9SDavid du Colombier  * (If nframes is << Nframes tdu might be far away but this avoids
791906943f9SDavid du Colombier  * races regarding frno.)
792906943f9SDavid du Colombier  * If we suffer errors for more than half the frames we stall.
793906943f9SDavid du Colombier  */
794906943f9SDavid du Colombier static void
isointerrupt(Ctlr * ctlr,Isoio * iso)795906943f9SDavid du Colombier isointerrupt(Ctlr *ctlr, Isoio* iso)
796906943f9SDavid du Colombier {
797906943f9SDavid du Colombier 	Td *tdi;
798906943f9SDavid du Colombier 	int err;
799906943f9SDavid du Colombier 	int i;
800906943f9SDavid du Colombier 	int nframes;
801906943f9SDavid du Colombier 
802906943f9SDavid du Colombier 	tdi = iso->tdi;
803906943f9SDavid du Colombier 	if((tdi->csw & Tdactive) != 0)		/* nothing new done */
804906943f9SDavid du Colombier 		return;
805906943f9SDavid du Colombier 	ctlr->nisointr++;
806906943f9SDavid du Colombier 	ddiprint("isointr: iso %#p: tdi %#p tdu %#p\n", iso, tdi, iso->tdu);
807906943f9SDavid du Colombier 	if(iso->state != Qrun && iso->state != Qdone)
808906943f9SDavid du Colombier 		panic("isointr: iso state");
809906943f9SDavid du Colombier 	if(debug > 1 || iso->debug > 1)
810906943f9SDavid du Colombier 		isodump(iso, 0);
811906943f9SDavid du Colombier 
812906943f9SDavid du Colombier 	nframes = iso->nframes / 2;		/* limit how many we look */
813906943f9SDavid du Colombier 	if(nframes > 64)
814906943f9SDavid du Colombier 		nframes = 64;
815906943f9SDavid du Colombier 
816906943f9SDavid du Colombier 	for(i = 0; i < nframes && (tdi->csw & Tdactive) == 0; i++){
817906943f9SDavid du Colombier 		tdi->csw &= ~Tdioc;
818906943f9SDavid du Colombier 		err = tdi->csw & Tderrors;
819906943f9SDavid du Colombier 		if(err == 0)
820906943f9SDavid du Colombier 			iso->nerrs = 0;
821906943f9SDavid du Colombier 		else if(iso->nerrs++ > iso->nframes/2)
822906943f9SDavid du Colombier 			tdi->csw |= Tdstalled;
823906943f9SDavid du Colombier 		if((tdi->csw & Tdstalled) != 0){
824906943f9SDavid du Colombier 			if(iso->err == nil){
825906943f9SDavid du Colombier 				iso->err = errmsg(err);
826906943f9SDavid du Colombier 				diprint("isointerrupt: tdi %#p error %#ux %s\n",
827906943f9SDavid du Colombier 					tdi, err, iso->err);
828906943f9SDavid du Colombier 				diprint("ctlr load %uld\n", ctlr->load);
829906943f9SDavid du Colombier 			}
830906943f9SDavid du Colombier 			tdi->ndata = 0;
831ade43d10SDavid du Colombier 		}else
832906943f9SDavid du Colombier 			tdi->ndata = tdlen(tdi);
833906943f9SDavid du Colombier 
834906943f9SDavid du Colombier 		if(tdi->next == iso->tdu || tdi->next->next == iso->tdu){
835906943f9SDavid du Colombier 			memset(iso->tdu->data, 0, maxtdlen(iso->tdu));
836906943f9SDavid du Colombier 			tdisoinit(iso, iso->tdu, maxtdlen(iso->tdu));
837906943f9SDavid du Colombier 			iso->tdu = iso->tdu->next;
838906943f9SDavid du Colombier 			iso->nleft = 0;
8399a747e4fSDavid du Colombier 		}
840906943f9SDavid du Colombier 		tdi = tdi->next;
841906943f9SDavid du Colombier 	}
842906943f9SDavid du Colombier 	ddiprint("isointr: %d frames processed\n", nframes);
843906943f9SDavid du Colombier 	if(i == nframes)
844906943f9SDavid du Colombier 		tdi->csw |= Tdioc;
845906943f9SDavid du Colombier 	iso->tdi = tdi;
846906943f9SDavid du Colombier 	if(isocanwrite(iso) || isocanread(iso)){
847906943f9SDavid du Colombier 		diprint("wakeup iso %#p tdi %#p tdu %#p\n", iso,
848906943f9SDavid du Colombier 			iso->tdi, iso->tdu);
849906943f9SDavid du Colombier 		wakeup(iso);
850906943f9SDavid du Colombier 	}
851906943f9SDavid du Colombier 
852906943f9SDavid du Colombier }
853906943f9SDavid du Colombier 
854906943f9SDavid du Colombier /*
855906943f9SDavid du Colombier  * Process a Qh upon interrupt. There's one per ongoing user I/O.
856906943f9SDavid du Colombier  * User process releases resources later, that is not done here.
857906943f9SDavid du Colombier  * We may find in this order one or more Tds:
858906943f9SDavid du Colombier  * - none/many non active and completed Tds
859906943f9SDavid du Colombier  * - none/one (usually(!) not active) and failed Td
860906943f9SDavid du Colombier  * - none/many active Tds.
861906943f9SDavid du Colombier  * Upon errors the entire transfer is aborted and error reported.
862906943f9SDavid du Colombier  * Otherwise, the transfer is complete only when all Tds are done or
863906943f9SDavid du Colombier  * when a read with less than maxpkt is found.
864906943f9SDavid du Colombier  * Use the software list and not qh->elink to avoid races.
865906943f9SDavid du Colombier  * We could use qh->elink to see if there's something new or not.
866906943f9SDavid du Colombier  */
867906943f9SDavid du Colombier static void
qhinterrupt(Ctlr * ctlr,Qh * qh)868906943f9SDavid du Colombier qhinterrupt(Ctlr *ctlr, Qh *qh)
869906943f9SDavid du Colombier {
870906943f9SDavid du Colombier 	Td *td;
871906943f9SDavid du Colombier 	int err;
872906943f9SDavid du Colombier 
873906943f9SDavid du Colombier 	ctlr->nqhintr++;
874906943f9SDavid du Colombier 	if(qh->state != Qrun)
875906943f9SDavid du Colombier 		panic("qhinterrupt: qh state");
876906943f9SDavid du Colombier 	if(qh->tds == nil)
877906943f9SDavid du Colombier 		panic("qhinterrupt: no tds");
878906943f9SDavid du Colombier 	if((qh->tds->csw & Tdactive) == 0)
879906943f9SDavid du Colombier 		ddqprint("qhinterrupt port %#ux qh %#p p0 %#x p1 %#x\n",
880906943f9SDavid du Colombier 			ctlr->port, qh, INS(PORT(0)), INS(PORT(1)));
881906943f9SDavid du Colombier 	for(td = qh->tds; td != nil; td = td->next){
882906943f9SDavid du Colombier 		if(td->csw & Tdactive)
883906943f9SDavid du Colombier 			return;
884906943f9SDavid du Colombier 		td->csw &= ~Tdioc;
885906943f9SDavid du Colombier 		if((td->csw & Tdstalled) != 0){
886906943f9SDavid du Colombier 			err = td->csw & Tderrors;
887906943f9SDavid du Colombier 			/* just stalled is end of xfer but not an error */
888906943f9SDavid du Colombier 			if(err != Tdstalled && qh->io->err == nil){
889906943f9SDavid du Colombier 				qh->io->err = errmsg(td->csw & Tderrors);
890906943f9SDavid du Colombier 				dqprint("qhinterrupt: td %#p error %#ux %s\n",
891906943f9SDavid du Colombier 					td, err, qh->io->err);
892906943f9SDavid du Colombier 				dqprint("ctlr load %uld\n", ctlr->load);
893906943f9SDavid du Colombier 			}
894906943f9SDavid du Colombier 			break;
895906943f9SDavid du Colombier 		}
896906943f9SDavid du Colombier 		if((td->csw & Tdnak) != 0){	/* retransmit; not serious */
897906943f9SDavid du Colombier 			td->csw &= ~Tdnak;
898906943f9SDavid du Colombier 			if(td->next == nil)
899906943f9SDavid du Colombier 				td->csw |= Tdioc;
900906943f9SDavid du Colombier 		}
901906943f9SDavid du Colombier 		td->ndata = tdlen(td);
902906943f9SDavid du Colombier 		if(td->ndata < maxtdlen(td)){	/* EOT */
903906943f9SDavid du Colombier 			td = td->next;
904906943f9SDavid du Colombier 			break;
905906943f9SDavid du Colombier 		}
906906943f9SDavid du Colombier 	}
907906943f9SDavid du Colombier 
908906943f9SDavid du Colombier 	/*
909906943f9SDavid du Colombier 	 * Done. Make void the Tds not used (errors or EOT) and wakeup epio.
910906943f9SDavid du Colombier 	 */
911906943f9SDavid du Colombier 	qh->elink = QHterm;
912906943f9SDavid du Colombier 	for(; td != nil; td = td->next)
913906943f9SDavid du Colombier 		td->ndata = 0;
914906943f9SDavid du Colombier 	qh->state = Qdone;
915906943f9SDavid du Colombier 	wakeup(qh->io);
9169a747e4fSDavid du Colombier }
9179a747e4fSDavid du Colombier 
9189a747e4fSDavid du Colombier static void
interrupt(Ureg *,void * a)9199a747e4fSDavid du Colombier interrupt(Ureg*, void *a)
9209a747e4fSDavid du Colombier {
921906943f9SDavid du Colombier 	Hci *hp;
9229a747e4fSDavid du Colombier 	Ctlr *ctlr;
923906943f9SDavid du Colombier 	int frptr;
924906943f9SDavid du Colombier 	int frno;
925906943f9SDavid du Colombier 	Qh *qh;
926906943f9SDavid du Colombier 	Isoio *iso;
927906943f9SDavid du Colombier 	int sts;
928906943f9SDavid du Colombier 	int cmd;
9299a747e4fSDavid du Colombier 
930906943f9SDavid du Colombier 	hp = a;
931906943f9SDavid du Colombier 	ctlr = hp->aux;
932906943f9SDavid du Colombier 	ilock(ctlr);
933906943f9SDavid du Colombier 	ctlr->nintr++;
934906943f9SDavid du Colombier 	sts = INS(Status);
935906943f9SDavid du Colombier 	if((sts & Sall) == 0){		/* not for us; sharing irq */
936906943f9SDavid du Colombier 		iunlock(ctlr);
9379a747e4fSDavid du Colombier 		return;
938906943f9SDavid du Colombier 	}
939906943f9SDavid du Colombier 	OUTS(Status, sts & Sall);
940906943f9SDavid du Colombier 	cmd = INS(Cmd);
941906943f9SDavid du Colombier 	if(cmd & Crun == 0){
942906943f9SDavid du Colombier 		print("uhci %#ux: not running: uhci bug?\n", ctlr->port);
943906943f9SDavid du Colombier 		/* BUG: should abort everything in this case */
944906943f9SDavid du Colombier 	}
945906943f9SDavid du Colombier 	if(debug > 1){
946906943f9SDavid du Colombier 		frptr = INL(Flbaseadd);
947906943f9SDavid du Colombier 		frno = INL(Frnum);
948906943f9SDavid du Colombier 		frno = TRUNC(frno, Nframes);
949906943f9SDavid du Colombier 		print("cmd %#ux sts %#ux frptr %#ux frno %d\n",
950906943f9SDavid du Colombier 			cmd, sts, frptr, frno);
951906943f9SDavid du Colombier 	}
952906943f9SDavid du Colombier 	ctlr->ntdintr++;
953906943f9SDavid du Colombier 	/*
954906943f9SDavid du Colombier 	 * Will we know in USB 3.0 who the interrupt was for?.
955906943f9SDavid du Colombier 	 * Do they still teach indexing in CS?
956906943f9SDavid du Colombier 	 * This is Intel's doing.
957906943f9SDavid du Colombier 	 */
958906943f9SDavid du Colombier 	for(iso = ctlr->iso; iso != nil; iso = iso->next)
959906943f9SDavid du Colombier 		if(iso->state == Qrun || iso->state == Qdone)
960906943f9SDavid du Colombier 			isointerrupt(ctlr, iso);
961906943f9SDavid du Colombier 	for(qh = ctlr->qhs; qh != nil; qh = qh->next)
962906943f9SDavid du Colombier 		if(qh->state == Qrun)
963906943f9SDavid du Colombier 			qhinterrupt(ctlr, qh);
964906943f9SDavid du Colombier 		else if(qh->state == Qclose)
965906943f9SDavid du Colombier 			qhlinktd(qh, nil);
966906943f9SDavid du Colombier 	iunlock(ctlr);
9679a747e4fSDavid du Colombier }
9689a747e4fSDavid du Colombier 
969906943f9SDavid du Colombier /*
970906943f9SDavid du Colombier  * iso->tdu is the next place to put data. When it gets full
971906943f9SDavid du Colombier  * it is activated and tdu advanced.
972906943f9SDavid du Colombier  */
9739a747e4fSDavid du Colombier static long
putsamples(Isoio * iso,uchar * b,long count)974906943f9SDavid du Colombier putsamples(Isoio *iso, uchar *b, long count)
9759a747e4fSDavid du Colombier {
976906943f9SDavid du Colombier 	long tot;
977906943f9SDavid du Colombier 	long n;
9789a747e4fSDavid du Colombier 
979906943f9SDavid du Colombier 	for(tot = 0; isocanwrite(iso) && tot < count; tot += n){
980906943f9SDavid du Colombier 		n = count-tot;
981906943f9SDavid du Colombier 		if(n > maxtdlen(iso->tdu) - iso->nleft)
982906943f9SDavid du Colombier 			n = maxtdlen(iso->tdu) - iso->nleft;
983906943f9SDavid du Colombier 		memmove(iso->tdu->data+iso->nleft, b+tot, n);
984906943f9SDavid du Colombier 		iso->nleft += n;
985906943f9SDavid du Colombier 		if(iso->nleft == maxtdlen(iso->tdu)){
986906943f9SDavid du Colombier 			tdisoinit(iso, iso->tdu, iso->nleft);
987906943f9SDavid du Colombier 			iso->nleft = 0;
988906943f9SDavid du Colombier 			iso->tdu = iso->tdu->next;
9899a747e4fSDavid du Colombier 		}
990906943f9SDavid du Colombier 	}
991906943f9SDavid du Colombier 	return tot;
992906943f9SDavid du Colombier }
993906943f9SDavid du Colombier 
994906943f9SDavid du Colombier /*
995906943f9SDavid du Colombier  * Queue data for writing and return error status from
996906943f9SDavid du Colombier  * last writes done, to maintain buffered data.
997906943f9SDavid du Colombier  */
998906943f9SDavid du Colombier static long
episowrite(Ep * ep,Isoio * iso,void * a,long count)999906943f9SDavid du Colombier episowrite(Ep *ep, Isoio *iso, void *a, long count)
1000906943f9SDavid du Colombier {
1001906943f9SDavid du Colombier 	Ctlr *ctlr;
1002906943f9SDavid du Colombier 	uchar *b;
1003906943f9SDavid du Colombier 	int tot;
1004906943f9SDavid du Colombier 	int nw;
1005906943f9SDavid du Colombier 	char *err;
1006906943f9SDavid du Colombier 
1007906943f9SDavid du Colombier 	iso->debug = ep->debug;
1008906943f9SDavid du Colombier 	diprint("uhci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
1009906943f9SDavid du Colombier 
1010906943f9SDavid du Colombier 	ctlr = ep->hp->aux;
1011906943f9SDavid du Colombier 	qlock(iso);
1012906943f9SDavid du Colombier 	if(waserror()){
1013906943f9SDavid du Colombier 		qunlock(iso);
10149a747e4fSDavid du Colombier 		nexterror();
10159a747e4fSDavid du Colombier 	}
1016906943f9SDavid du Colombier 	ilock(ctlr);
1017906943f9SDavid du Colombier 	if(iso->state == Qclose){
1018906943f9SDavid du Colombier 		iunlock(ctlr);
1019906943f9SDavid du Colombier 		error(iso->err ? iso->err : Eio);
10209a747e4fSDavid du Colombier 	}
1021906943f9SDavid du Colombier 	iso->state = Qrun;
1022906943f9SDavid du Colombier 	b = a;
1023906943f9SDavid du Colombier 	for(tot = 0; tot < count; tot += nw){
1024906943f9SDavid du Colombier 		while(isocanwrite(iso) == 0){
1025906943f9SDavid du Colombier 			iunlock(ctlr);
1026906943f9SDavid du Colombier 			diprint("uhci: episowrite: %#p sleep\n", iso);
1027906943f9SDavid du Colombier 			if(waserror()){
1028906943f9SDavid du Colombier 				if(iso->err == nil)
1029906943f9SDavid du Colombier 					iso->err = "I/O timed out";
1030906943f9SDavid du Colombier 				ilock(ctlr);
1031906943f9SDavid du Colombier 				break;
10329a747e4fSDavid du Colombier 			}
1033d37e33ffSDavid du Colombier 			tsleep(iso, isocanwrite, iso, ep->tmout);
1034906943f9SDavid du Colombier 			poperror();
1035906943f9SDavid du Colombier 			ilock(ctlr);
10369a747e4fSDavid du Colombier 		}
1037906943f9SDavid du Colombier 		err = iso->err;
1038906943f9SDavid du Colombier 		iso->err = nil;
1039906943f9SDavid du Colombier 		if(iso->state == Qclose || err != nil){
1040906943f9SDavid du Colombier 			iunlock(ctlr);
1041906943f9SDavid du Colombier 			error(err ? err : Eio);
10429a747e4fSDavid du Colombier 		}
1043906943f9SDavid du Colombier 		if(iso->state != Qrun)
1044906943f9SDavid du Colombier 			panic("episowrite: iso not running");
1045906943f9SDavid du Colombier 		iunlock(ctlr);		/* We could page fault here */
1046906943f9SDavid du Colombier 		nw = putsamples(iso, b+tot, count-tot);
1047906943f9SDavid du Colombier 		ilock(ctlr);
10489a747e4fSDavid du Colombier 	}
1049906943f9SDavid du Colombier 	if(iso->state != Qclose)
1050906943f9SDavid du Colombier 		iso->state = Qdone;
1051906943f9SDavid du Colombier 	iunlock(ctlr);
1052906943f9SDavid du Colombier 	err = iso->err;		/* in case it failed early */
1053906943f9SDavid du Colombier 	iso->err = nil;
1054906943f9SDavid du Colombier 	qunlock(iso);
1055906943f9SDavid du Colombier 	poperror();
1056906943f9SDavid du Colombier 	if(err != nil)
1057906943f9SDavid du Colombier 		error(err);
1058906943f9SDavid du Colombier 	diprint("uhci: episowrite: %#p %d bytes\n", iso, tot);
1059906943f9SDavid du Colombier 	return tot;
10603ff48bf5SDavid du Colombier }
1061906943f9SDavid du Colombier 
1062906943f9SDavid du Colombier /*
1063906943f9SDavid du Colombier  * Available data is kept at tdu and following tds, up to tdi (excluded).
1064906943f9SDavid du Colombier  */
1065906943f9SDavid du Colombier static long
episoread(Ep * ep,Isoio * iso,void * a,int count)1066906943f9SDavid du Colombier episoread(Ep *ep, Isoio *iso, void *a, int count)
1067906943f9SDavid du Colombier {
1068906943f9SDavid du Colombier 	Ctlr *ctlr;
1069906943f9SDavid du Colombier 	uchar *b;
1070906943f9SDavid du Colombier 	int nr;
1071906943f9SDavid du Colombier 	int tot;
1072906943f9SDavid du Colombier 	Td *tdu;
1073906943f9SDavid du Colombier 
1074906943f9SDavid du Colombier 	iso->debug = ep->debug;
1075906943f9SDavid du Colombier 	diprint("uhci: episoread: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
1076906943f9SDavid du Colombier 
1077906943f9SDavid du Colombier 	b = a;
1078906943f9SDavid du Colombier 	ctlr = ep->hp->aux;
1079906943f9SDavid du Colombier 	qlock(iso);
1080906943f9SDavid du Colombier 	if(waserror()){
1081906943f9SDavid du Colombier 		qunlock(iso);
1082906943f9SDavid du Colombier 		nexterror();
10839a747e4fSDavid du Colombier 	}
1084906943f9SDavid du Colombier 	iso->err = nil;
1085906943f9SDavid du Colombier 	iso->nerrs = 0;
1086906943f9SDavid du Colombier 	ilock(ctlr);
1087906943f9SDavid du Colombier 	if(iso->state == Qclose){
1088906943f9SDavid du Colombier 		iunlock(ctlr);
1089906943f9SDavid du Colombier 		error(iso->err ? iso->err : Eio);
10909a747e4fSDavid du Colombier 	}
1091906943f9SDavid du Colombier 	iso->state = Qrun;
1092906943f9SDavid du Colombier 	while(isocanread(iso) == 0){
1093906943f9SDavid du Colombier 		iunlock(ctlr);
1094906943f9SDavid du Colombier 		diprint("uhci: episoread: %#p sleep\n", iso);
1095906943f9SDavid du Colombier 		if(waserror()){
1096906943f9SDavid du Colombier 			if(iso->err == nil)
1097906943f9SDavid du Colombier 				iso->err = "I/O timed out";
1098906943f9SDavid du Colombier 			ilock(ctlr);
1099906943f9SDavid du Colombier 			break;
11009a747e4fSDavid du Colombier 		}
1101d37e33ffSDavid du Colombier 		tsleep(iso, isocanread, iso, ep->tmout);
1102906943f9SDavid du Colombier 		poperror();
1103906943f9SDavid du Colombier 		ilock(ctlr);
1104906943f9SDavid du Colombier 	}
1105906943f9SDavid du Colombier 	if(iso->state == Qclose){
1106906943f9SDavid du Colombier 		iunlock(ctlr);
1107906943f9SDavid du Colombier 		error(iso->err ? iso->err : Eio);
1108906943f9SDavid du Colombier 	}
1109906943f9SDavid du Colombier 	iso->state = Qdone;
1110906943f9SDavid du Colombier 	assert(iso->tdu != iso->tdi);
1111906943f9SDavid du Colombier 
1112906943f9SDavid du Colombier 	for(tot = 0; iso->tdi != iso->tdu && tot < count; tot += nr){
1113906943f9SDavid du Colombier 		tdu = iso->tdu;
1114906943f9SDavid du Colombier 		if(tdu->csw & Tdactive){
1115906943f9SDavid du Colombier 			diprint("uhci: episoread: %#p tdu active\n", iso);
1116906943f9SDavid du Colombier 			break;
1117906943f9SDavid du Colombier 		}
1118906943f9SDavid du Colombier 		nr = tdu->ndata;
1119906943f9SDavid du Colombier 		if(tot + nr > count)
1120906943f9SDavid du Colombier 			nr = count - tot;
1121906943f9SDavid du Colombier 		if(nr == 0)
1122906943f9SDavid du Colombier 			print("uhci: ep%d.%d: too many polls\n",
1123906943f9SDavid du Colombier 				ep->dev->nb, ep->nb);
11249a747e4fSDavid du Colombier 		else{
1125906943f9SDavid du Colombier 			iunlock(ctlr);		/* We could page fault here */
1126906943f9SDavid du Colombier 			memmove(b+tot, tdu->data, nr);
1127906943f9SDavid du Colombier 			ilock(ctlr);
1128906943f9SDavid du Colombier 			if(nr < tdu->ndata)
1129906943f9SDavid du Colombier 				memmove(tdu->data, tdu->data+nr, tdu->ndata - nr);
1130906943f9SDavid du Colombier 			tdu->ndata -= nr;
11319a747e4fSDavid du Colombier 		}
1132906943f9SDavid du Colombier 		if(tdu->ndata == 0){
1133906943f9SDavid du Colombier 			tdisoinit(iso, tdu, ep->maxpkt);
1134906943f9SDavid du Colombier 			iso->tdu = tdu->next;
11359a747e4fSDavid du Colombier 		}
1136906943f9SDavid du Colombier 	}
1137906943f9SDavid du Colombier 	iunlock(ctlr);
1138906943f9SDavid du Colombier 	qunlock(iso);
11399a747e4fSDavid du Colombier 	poperror();
1140906943f9SDavid du Colombier 	diprint("uhci: episoread: %#p %d bytes err '%s'\n", iso, tot, iso->err);
1141906943f9SDavid du Colombier 	if(iso->err != nil)
1142906943f9SDavid du Colombier 		error(iso->err);
1143906943f9SDavid du Colombier 	return tot;
11449a747e4fSDavid du Colombier }
11459a747e4fSDavid du Colombier 
11469a747e4fSDavid du Colombier static int
nexttoggle(int tog)1147906943f9SDavid du Colombier nexttoggle(int tog)
11489a747e4fSDavid du Colombier {
1149906943f9SDavid du Colombier 	if(tog == Tddata0)
1150906943f9SDavid du Colombier 		return Tddata1;
1151906943f9SDavid du Colombier 	else
1152906943f9SDavid du Colombier 		return Tddata0;
11539a747e4fSDavid du Colombier }
11549a747e4fSDavid du Colombier 
1155906943f9SDavid du Colombier static Td*
epgettd(Ep * ep,Qio * io,int flags,void * a,int count)1156906943f9SDavid du Colombier epgettd(Ep *ep, Qio *io, int flags, void *a, int count)
11579a747e4fSDavid du Colombier {
1158906943f9SDavid du Colombier 	Td *td;
1159906943f9SDavid du Colombier 	int tok;
11609a747e4fSDavid du Colombier 
1161906943f9SDavid du Colombier 	if(ep->maxpkt < count)
1162906943f9SDavid du Colombier 		error("maxpkt too short");
1163906943f9SDavid du Colombier 	td = tdalloc();
1164906943f9SDavid du Colombier 	if(count <= Tdndata)
1165906943f9SDavid du Colombier 		td->data = td->sbuff;
1166906943f9SDavid du Colombier 	else
1167906943f9SDavid du Colombier 		td->data = td->buff = smalloc(ep->maxpkt);
1168906943f9SDavid du Colombier 	td->buffer = PCIWADDR(td->data);
1169906943f9SDavid du Colombier 	td->ndata = count;
1170906943f9SDavid du Colombier 	if(a != nil && count > 0)
1171906943f9SDavid du Colombier 		memmove(td->data, a, count);
1172906943f9SDavid du Colombier 	td->csw = Tderr2|Tderr1|flags;
1173906943f9SDavid du Colombier 	if(ep->dev->speed == Lowspeed)
1174906943f9SDavid du Colombier 		td->csw |= Tdlow;
1175906943f9SDavid du Colombier 	tok = io->tok | io->toggle;
1176906943f9SDavid du Colombier 	io->toggle = nexttoggle(io->toggle);
1177906943f9SDavid du Colombier 	td->token = ((count-1)<<21) | ((io->usbid&0x7FF)<<8) | tok;
11789a747e4fSDavid du Colombier 
1179906943f9SDavid du Colombier 	return td;
11809a747e4fSDavid du Colombier }
1181906943f9SDavid du Colombier 
1182906943f9SDavid du Colombier /*
1183906943f9SDavid du Colombier  * Try to get them idle
1184906943f9SDavid du Colombier  */
1185906943f9SDavid du Colombier static void
aborttds(Qh * qh)1186906943f9SDavid du Colombier aborttds(Qh *qh)
1187906943f9SDavid du Colombier {
1188906943f9SDavid du Colombier 	Td *td;
1189906943f9SDavid du Colombier 
1190906943f9SDavid du Colombier 	qh->state = Qdone;
1191906943f9SDavid du Colombier 	qh->elink = QHterm;
1192906943f9SDavid du Colombier 	for(td = qh->tds; td != nil; td = td->next){
1193906943f9SDavid du Colombier 		if(td->csw & Tdactive)
1194906943f9SDavid du Colombier 			td->ndata = 0;
1195906943f9SDavid du Colombier 		td->csw &= ~(Tdactive|Tdioc);
11969a747e4fSDavid du Colombier 	}
11979a747e4fSDavid du Colombier }
1198906943f9SDavid du Colombier 
1199906943f9SDavid du Colombier static int
epiodone(void * a)1200906943f9SDavid du Colombier epiodone(void *a)
1201906943f9SDavid du Colombier {
1202906943f9SDavid du Colombier 	Qh *qh;
1203906943f9SDavid du Colombier 
1204906943f9SDavid du Colombier 	qh = a;
1205906943f9SDavid du Colombier 	return qh->state != Qrun;
12069a747e4fSDavid du Colombier }
12079a747e4fSDavid du Colombier 
12089a747e4fSDavid du Colombier static void
epiowait(Ctlr * ctlr,Qio * io,int tmout,ulong load)1209906943f9SDavid du Colombier epiowait(Ctlr *ctlr, Qio *io, int tmout, ulong load)
1210906943f9SDavid du Colombier {
1211906943f9SDavid du Colombier 	Qh *qh;
1212906943f9SDavid du Colombier 	int timedout;
1213906943f9SDavid du Colombier 
1214906943f9SDavid du Colombier 	qh = io->qh;
1215906943f9SDavid du Colombier 	ddqprint("uhci io %#p sleep on qh %#p state %uld\n", io, qh, qh->state);
1216906943f9SDavid du Colombier 	timedout = 0;
1217906943f9SDavid du Colombier 	if(waserror()){
1218906943f9SDavid du Colombier 		dqprint("uhci io %#p qh %#p timed out\n", io, qh);
1219906943f9SDavid du Colombier 		timedout++;
1220906943f9SDavid du Colombier 	}else{
1221906943f9SDavid du Colombier 		if(tmout == 0)
1222906943f9SDavid du Colombier 			sleep(io, epiodone, qh);
1223906943f9SDavid du Colombier 		else
1224906943f9SDavid du Colombier 			tsleep(io, epiodone, qh, tmout);
1225906943f9SDavid du Colombier 		poperror();
1226906943f9SDavid du Colombier 	}
1227906943f9SDavid du Colombier 	ilock(ctlr);
1228906943f9SDavid du Colombier 	if(qh->state == Qrun)
1229906943f9SDavid du Colombier 		timedout = 1;
1230906943f9SDavid du Colombier 	else if(qh->state != Qdone && qh->state != Qclose)
1231906943f9SDavid du Colombier 		panic("epio: queue not done and not closed");
1232906943f9SDavid du Colombier 	if(timedout){
1233906943f9SDavid du Colombier 		aborttds(io->qh);
1234906943f9SDavid du Colombier 		io->err = "request timed out";
1235906943f9SDavid du Colombier 		iunlock(ctlr);
1236906943f9SDavid du Colombier 		if(!waserror()){
1237906943f9SDavid du Colombier 			tsleep(&up->sleep, return0, 0, Abortdelay);
1238906943f9SDavid du Colombier 			poperror();
1239906943f9SDavid du Colombier 		}
1240906943f9SDavid du Colombier 		ilock(ctlr);
1241906943f9SDavid du Colombier 	}
1242906943f9SDavid du Colombier 	if(qh->state != Qclose)
1243906943f9SDavid du Colombier 		qh->state = Qidle;
1244906943f9SDavid du Colombier 	qhlinktd(qh, nil);
1245906943f9SDavid du Colombier 	ctlr->load -= load;
1246906943f9SDavid du Colombier 	iunlock(ctlr);
1247906943f9SDavid du Colombier }
1248906943f9SDavid du Colombier 
1249906943f9SDavid du Colombier /*
1250906943f9SDavid du Colombier  * Non iso I/O.
1251906943f9SDavid du Colombier  * To make it work for control transfers, the caller may
1252906943f9SDavid du Colombier  * lock the Qio for the entire control transfer.
1253906943f9SDavid du Colombier  */
1254906943f9SDavid du Colombier static long
epio(Ep * ep,Qio * io,void * a,long count,int mustlock)1255d37e33ffSDavid du Colombier epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
1256906943f9SDavid du Colombier {
1257d37e33ffSDavid du Colombier 	Td *td, *ltd, *td0, *ntd;
1258906943f9SDavid du Colombier 	Ctlr *ctlr;
1259906943f9SDavid du Colombier 	Qh* qh;
1260d37e33ffSDavid du Colombier 	long n, tot;
1261906943f9SDavid du Colombier 	char buf[128];
1262906943f9SDavid du Colombier 	uchar *c;
1263d37e33ffSDavid du Colombier 	int saved, ntds, tmout;
1264906943f9SDavid du Colombier 	ulong load;
1265906943f9SDavid du Colombier 	char *err;
1266906943f9SDavid du Colombier 
1267906943f9SDavid du Colombier 	qh = io->qh;
1268906943f9SDavid du Colombier 	ctlr = ep->hp->aux;
1269906943f9SDavid du Colombier 	io->debug = ep->debug;
1270d37e33ffSDavid du Colombier 	tmout = ep->tmout;
1271906943f9SDavid du Colombier 	ddeprint("epio: %s ep%d.%d io %#p count %ld load %uld\n",
1272906943f9SDavid du Colombier 		io->tok == Tdtokin ? "in" : "out",
1273906943f9SDavid du Colombier 		ep->dev->nb, ep->nb, io, count, ctlr->load);
1274906943f9SDavid du Colombier 	if((debug > 1 || ep->debug > 1) && io->tok != Tdtokin){
1275906943f9SDavid du Colombier 		seprintdata(buf, buf+sizeof(buf), a, count);
1276906943f9SDavid du Colombier 		print("uchi epio: user data: %s\n", buf);
1277906943f9SDavid du Colombier 	}
1278906943f9SDavid du Colombier 	if(mustlock){
1279906943f9SDavid du Colombier 		qlock(io);
1280906943f9SDavid du Colombier 		if(waserror()){
1281906943f9SDavid du Colombier 			qunlock(io);
1282906943f9SDavid du Colombier 			nexterror();
1283906943f9SDavid du Colombier 		}
1284906943f9SDavid du Colombier 	}
1285906943f9SDavid du Colombier 	io->err = nil;
1286906943f9SDavid du Colombier 	ilock(ctlr);
1287906943f9SDavid du Colombier 	if(qh->state == Qclose){	/* Tds released by cancelio */
1288906943f9SDavid du Colombier 		iunlock(ctlr);
1289906943f9SDavid du Colombier 		error(io->err ? io->err : Eio);
1290906943f9SDavid du Colombier 	}
1291906943f9SDavid du Colombier 	if(qh->state != Qidle)
1292906943f9SDavid du Colombier 		panic("epio: qh not idle");
1293906943f9SDavid du Colombier 	qh->state = Qinstall;
1294906943f9SDavid du Colombier 	iunlock(ctlr);
1295906943f9SDavid du Colombier 
1296906943f9SDavid du Colombier 	c = a;
1297906943f9SDavid du Colombier 	td0 = ltd = nil;
1298906943f9SDavid du Colombier 	load = tot = 0;
1299906943f9SDavid du Colombier 	do{
1300906943f9SDavid du Colombier 		n = ep->maxpkt;
1301906943f9SDavid du Colombier 		if(count-tot < n)
1302906943f9SDavid du Colombier 			n = count-tot;
1303d24fba2aSDavid du Colombier 		if(c != nil && io->tok != Tdtokin)
1304906943f9SDavid du Colombier 			td = epgettd(ep, io, Tdactive, c+tot, n);
1305906943f9SDavid du Colombier 		else
1306906943f9SDavid du Colombier 			td = epgettd(ep, io, Tdactive|Tdspd, nil, n);
1307906943f9SDavid du Colombier 		if(td0 == nil)
1308906943f9SDavid du Colombier 			td0 = td;
1309906943f9SDavid du Colombier 		else
1310906943f9SDavid du Colombier 			tdlinktd(ltd, td);
1311906943f9SDavid du Colombier 		ltd = td;
1312906943f9SDavid du Colombier 		tot += n;
1313906943f9SDavid du Colombier 		load += ep->load;
1314906943f9SDavid du Colombier 	}while(tot < count);
1315906943f9SDavid du Colombier 	if(td0 == nil || ltd == nil)
1316906943f9SDavid du Colombier 		panic("epio: no td");
1317906943f9SDavid du Colombier 
1318906943f9SDavid du Colombier 	ltd->csw |= Tdioc;	/* the last one interrupts */
1319906943f9SDavid du Colombier 	ddeprint("uhci: load %uld ctlr load %uld\n", load, ctlr->load);
1320906943f9SDavid du Colombier 	ilock(ctlr);
1321906943f9SDavid du Colombier 	if(qh->state != Qclose){
1322906943f9SDavid du Colombier 		io->iotime = TK2MS(MACHP(0)->ticks);
1323906943f9SDavid du Colombier 		qh->state = Qrun;
1324906943f9SDavid du Colombier 		coherence();
1325906943f9SDavid du Colombier 		qhlinktd(qh, td0);
1326906943f9SDavid du Colombier 		ctlr->load += load;
1327906943f9SDavid du Colombier 	}
1328906943f9SDavid du Colombier 	iunlock(ctlr);
1329906943f9SDavid du Colombier 
1330906943f9SDavid du Colombier 	epiowait(ctlr, io, tmout, load);
1331d37e33ffSDavid du Colombier 
1332906943f9SDavid du Colombier 	if(debug > 1 || ep->debug > 1)
1333906943f9SDavid du Colombier 		dumptd(td0, "epio: got tds: ");
1334906943f9SDavid du Colombier 
1335906943f9SDavid du Colombier 	tot = 0;
1336906943f9SDavid du Colombier 	c = a;
1337906943f9SDavid du Colombier 	saved = 0;
1338906943f9SDavid du Colombier 	ntds = 0;
1339906943f9SDavid du Colombier 	for(td = td0; td != nil; td = ntd){
1340906943f9SDavid du Colombier 		ntds++;
1341906943f9SDavid du Colombier 		/*
1342906943f9SDavid du Colombier 		 * Use td tok, not io tok, because of setup packets.
1343906943f9SDavid du Colombier 		 * Also, if the Td was stalled or active (previous Td
1344906943f9SDavid du Colombier 		 * was a short packet), we must save the toggle as it is.
1345906943f9SDavid du Colombier 		 */
1346906943f9SDavid du Colombier 		if(td->csw & (Tdstalled|Tdactive)){
1347906943f9SDavid du Colombier 			if(saved++ == 0)
1348906943f9SDavid du Colombier 				io->toggle = td->token & Tddata1;
1349906943f9SDavid du Colombier 		}else{
1350906943f9SDavid du Colombier 			tot += td->ndata;
1351d24fba2aSDavid du Colombier 			if(c != nil && tdtok(td) == Tdtokin && td->ndata > 0){
1352906943f9SDavid du Colombier 				memmove(c, td->data, td->ndata);
1353906943f9SDavid du Colombier 				c += td->ndata;
1354906943f9SDavid du Colombier 			}
1355906943f9SDavid du Colombier 		}
1356906943f9SDavid du Colombier 		ntd = td->next;
1357906943f9SDavid du Colombier 		tdfree(td);
1358906943f9SDavid du Colombier 	}
1359906943f9SDavid du Colombier 	err = io->err;
1360906943f9SDavid du Colombier 	if(mustlock){
1361906943f9SDavid du Colombier 		qunlock(io);
1362906943f9SDavid du Colombier 		poperror();
1363906943f9SDavid du Colombier 	}
1364906943f9SDavid du Colombier 	ddeprint("epio: io %#p: %d tds: return %ld err '%s'\n",
1365906943f9SDavid du Colombier 		io, ntds, tot, err);
1366906943f9SDavid du Colombier 	if(err != nil)
1367906943f9SDavid du Colombier 		error(err);
1368906943f9SDavid du Colombier 	if(tot < 0)
1369906943f9SDavid du Colombier 		error(Eio);
1370906943f9SDavid du Colombier 	return tot;
1371906943f9SDavid du Colombier }
1372906943f9SDavid du Colombier 
1373906943f9SDavid du Colombier /*
1374906943f9SDavid du Colombier  * halt condition was cleared on the endpoint. update our toggles.
1375906943f9SDavid du Colombier  */
1376906943f9SDavid du Colombier static void
clrhalt(Ep * ep)1377906943f9SDavid du Colombier clrhalt(Ep *ep)
1378906943f9SDavid du Colombier {
1379906943f9SDavid du Colombier 	Qio *io;
1380906943f9SDavid du Colombier 
1381906943f9SDavid du Colombier 	ep->clrhalt = 0;
1382906943f9SDavid du Colombier 	switch(ep->ttype){
1383906943f9SDavid du Colombier 	case Tbulk:
1384906943f9SDavid du Colombier 	case Tintr:
1385906943f9SDavid du Colombier 		io = ep->aux;
1386906943f9SDavid du Colombier 		if(ep->mode != OREAD){
1387906943f9SDavid du Colombier 			qlock(&io[OWRITE]);
1388906943f9SDavid du Colombier 			io[OWRITE].toggle = Tddata0;
1389906943f9SDavid du Colombier 			deprint("ep clrhalt for io %#p\n", io+OWRITE);
1390906943f9SDavid du Colombier 			qunlock(&io[OWRITE]);
1391906943f9SDavid du Colombier 		}
1392906943f9SDavid du Colombier 		if(ep->mode != OWRITE){
1393906943f9SDavid du Colombier 			qlock(&io[OREAD]);
1394906943f9SDavid du Colombier 			io[OREAD].toggle = Tddata0;
1395906943f9SDavid du Colombier 			deprint("ep clrhalt for io %#p\n", io+OREAD);
1396906943f9SDavid du Colombier 			qunlock(&io[OREAD]);
1397906943f9SDavid du Colombier 		}
1398906943f9SDavid du Colombier 		break;
1399906943f9SDavid du Colombier 	}
1400906943f9SDavid du Colombier }
1401906943f9SDavid du Colombier 
1402906943f9SDavid du Colombier static long
epread(Ep * ep,void * a,long count)1403906943f9SDavid du Colombier epread(Ep *ep, void *a, long count)
1404906943f9SDavid du Colombier {
1405906943f9SDavid du Colombier 	Ctlio *cio;
1406906943f9SDavid du Colombier 	Qio *io;
1407906943f9SDavid du Colombier 	Isoio *iso;
1408906943f9SDavid du Colombier 	char buf[160];
1409906943f9SDavid du Colombier 	ulong delta;
1410906943f9SDavid du Colombier 
1411906943f9SDavid du Colombier 	ddeprint("uhci: epread\n");
1412906943f9SDavid du Colombier 	if(ep->aux == nil)
1413906943f9SDavid du Colombier 		panic("epread: not open");
1414906943f9SDavid du Colombier 
1415906943f9SDavid du Colombier 	switch(ep->ttype){
1416906943f9SDavid du Colombier 	case Tctl:
1417906943f9SDavid du Colombier 		cio = ep->aux;
1418906943f9SDavid du Colombier 		qlock(cio);
1419906943f9SDavid du Colombier 		if(waserror()){
1420906943f9SDavid du Colombier 			qunlock(cio);
1421906943f9SDavid du Colombier 			nexterror();
1422906943f9SDavid du Colombier 		}
1423906943f9SDavid du Colombier 		ddeprint("epread ctl ndata %d\n", cio->ndata);
1424906943f9SDavid du Colombier 		if(cio->ndata < 0)
1425906943f9SDavid du Colombier 			error("request expected");
1426906943f9SDavid du Colombier 		else if(cio->ndata == 0){
1427906943f9SDavid du Colombier 			cio->ndata = -1;
1428906943f9SDavid du Colombier 			count = 0;
1429906943f9SDavid du Colombier 		}else{
1430906943f9SDavid du Colombier 			if(count > cio->ndata)
1431906943f9SDavid du Colombier 				count = cio->ndata;
1432906943f9SDavid du Colombier 			if(count > 0)
1433906943f9SDavid du Colombier 				memmove(a, cio->data, count);
1434906943f9SDavid du Colombier 			/* BUG for big transfers */
1435906943f9SDavid du Colombier 			free(cio->data);
1436906943f9SDavid du Colombier 			cio->data = nil;
1437906943f9SDavid du Colombier 			cio->ndata = 0;	/* signal EOF next time */
1438906943f9SDavid du Colombier 		}
1439906943f9SDavid du Colombier 		qunlock(cio);
1440906943f9SDavid du Colombier 		poperror();
1441906943f9SDavid du Colombier 		if(debug>1 || ep->debug){
1442906943f9SDavid du Colombier 			seprintdata(buf, buf+sizeof(buf), a, count);
1443906943f9SDavid du Colombier 			print("epread: %s\n", buf);
1444906943f9SDavid du Colombier 		}
1445906943f9SDavid du Colombier 		return count;
1446906943f9SDavid du Colombier 	case Tbulk:
1447906943f9SDavid du Colombier 		io = ep->aux;
1448906943f9SDavid du Colombier 		if(ep->clrhalt)
1449906943f9SDavid du Colombier 			clrhalt(ep);
1450d37e33ffSDavid du Colombier 		return epio(ep, &io[OREAD], a, count, 1);
1451906943f9SDavid du Colombier 	case Tintr:
1452906943f9SDavid du Colombier 		io = ep->aux;
1453906943f9SDavid du Colombier 		delta = TK2MS(MACHP(0)->ticks) - io[OREAD].iotime + 1;
1454906943f9SDavid du Colombier 		if(delta < ep->pollival / 2)
1455906943f9SDavid du Colombier 			tsleep(&up->sleep, return0, 0, ep->pollival/2 - delta);
1456906943f9SDavid du Colombier 		if(ep->clrhalt)
1457906943f9SDavid du Colombier 			clrhalt(ep);
1458d37e33ffSDavid du Colombier 		return epio(ep, &io[OREAD], a, count, 1);
1459906943f9SDavid du Colombier 	case Tiso:
1460906943f9SDavid du Colombier 		iso = ep->aux;
1461906943f9SDavid du Colombier 		return episoread(ep, iso, a, count);
1462906943f9SDavid du Colombier 	default:
1463906943f9SDavid du Colombier 		panic("epread: bad ep ttype %d", ep->ttype);
1464906943f9SDavid du Colombier 	}
1465906943f9SDavid du Colombier 	return -1;
1466906943f9SDavid du Colombier }
1467906943f9SDavid du Colombier 
1468906943f9SDavid du Colombier /*
1469906943f9SDavid du Colombier  * Control transfers are one setup write (data0)
1470906943f9SDavid du Colombier  * plus zero or more reads/writes (data1, data0, ...)
1471906943f9SDavid du Colombier  * plus a final write/read with data1 to ack.
1472906943f9SDavid du Colombier  * For both host to device and device to host we perform
1473906943f9SDavid du Colombier  * the entire transfer when the user writes the request,
1474906943f9SDavid du Colombier  * and keep any data read from the device for a later read.
1475906943f9SDavid du Colombier  * We call epio three times instead of placing all Tds at
1476906943f9SDavid du Colombier  * the same time because doing so leads to crc/tmout errors
1477906943f9SDavid du Colombier  * for some devices.
1478906943f9SDavid du Colombier  * Upon errors on the data phase we must still run the status
1479906943f9SDavid du Colombier  * phase or the device may cease responding in the future.
1480906943f9SDavid du Colombier  */
1481906943f9SDavid du Colombier static long
epctlio(Ep * ep,Ctlio * cio,void * a,long count)1482906943f9SDavid du Colombier epctlio(Ep *ep, Ctlio *cio, void *a, long count)
1483906943f9SDavid du Colombier {
1484906943f9SDavid du Colombier 	uchar *c;
1485906943f9SDavid du Colombier 	long len;
1486906943f9SDavid du Colombier 
1487906943f9SDavid du Colombier 	ddeprint("epctlio: cio %#p ep%d.%d count %ld\n",
1488906943f9SDavid du Colombier 		cio, ep->dev->nb, ep->nb, count);
1489906943f9SDavid du Colombier 	if(count < Rsetuplen)
1490906943f9SDavid du Colombier 		error("short usb comand");
1491906943f9SDavid du Colombier 	qlock(cio);
1492906943f9SDavid du Colombier 	free(cio->data);
1493906943f9SDavid du Colombier 	cio->data = nil;
1494906943f9SDavid du Colombier 	cio->ndata = 0;
1495906943f9SDavid du Colombier 	if(waserror()){
1496906943f9SDavid du Colombier 		qunlock(cio);
1497906943f9SDavid du Colombier 		free(cio->data);
1498906943f9SDavid du Colombier 		cio->data = nil;
1499906943f9SDavid du Colombier 		cio->ndata = 0;
1500906943f9SDavid du Colombier 		nexterror();
1501906943f9SDavid du Colombier 	}
1502906943f9SDavid du Colombier 
1503906943f9SDavid du Colombier 	/* set the address if unset and out of configuration state */
1504a23bc242SDavid du Colombier 	if(ep->dev->state != Dconfig && ep->dev->state != Dreset)
1505a23bc242SDavid du Colombier 		if(cio->usbid == 0)
1506906943f9SDavid du Colombier 			cio->usbid = ((ep->nb&Epmax)<<7)|(ep->dev->nb&Devmax);
1507906943f9SDavid du Colombier 	c = a;
1508906943f9SDavid du Colombier 	cio->tok = Tdtoksetup;
1509906943f9SDavid du Colombier 	cio->toggle = Tddata0;
1510d37e33ffSDavid du Colombier 	if(epio(ep, cio, a, Rsetuplen, 0) < Rsetuplen)
1511906943f9SDavid du Colombier 		error(Eio);
1512906943f9SDavid du Colombier 	a = c + Rsetuplen;
1513906943f9SDavid du Colombier 	count -= Rsetuplen;
1514906943f9SDavid du Colombier 
1515906943f9SDavid du Colombier 	cio->toggle = Tddata1;
1516906943f9SDavid du Colombier 	if(c[Rtype] & Rd2h){
1517906943f9SDavid du Colombier 		cio->tok = Tdtokin;
1518906943f9SDavid du Colombier 		len = GET2(c+Rcount);
1519906943f9SDavid du Colombier 		if(len <= 0)
1520906943f9SDavid du Colombier 			error("bad length in d2h request");
1521906943f9SDavid du Colombier 		if(len > Maxctllen)
1522906943f9SDavid du Colombier 			error("d2h data too large to fit in uhci");
1523906943f9SDavid du Colombier 		a = cio->data = smalloc(len+1);
1524906943f9SDavid du Colombier 	}else{
1525906943f9SDavid du Colombier 		cio->tok = Tdtokout;
1526906943f9SDavid du Colombier 		len = count;
1527906943f9SDavid du Colombier 	}
1528906943f9SDavid du Colombier 	if(len > 0)
1529906943f9SDavid du Colombier 		if(waserror())
1530906943f9SDavid du Colombier 			len = -1;
1531906943f9SDavid du Colombier 		else{
1532d37e33ffSDavid du Colombier 			len = epio(ep, cio, a, len, 0);
1533906943f9SDavid du Colombier 			poperror();
1534906943f9SDavid du Colombier 		}
1535906943f9SDavid du Colombier 	if(c[Rtype] & Rd2h){
1536906943f9SDavid du Colombier 		count = Rsetuplen;
1537906943f9SDavid du Colombier 		cio->ndata = len;
1538906943f9SDavid du Colombier 		cio->tok = Tdtokout;
1539906943f9SDavid du Colombier 	}else{
1540906943f9SDavid du Colombier 		if(len < 0)
1541906943f9SDavid du Colombier 			count = -1;
1542906943f9SDavid du Colombier 		else
1543906943f9SDavid du Colombier 			count = Rsetuplen + len;
1544906943f9SDavid du Colombier 		cio->tok = Tdtokin;
1545906943f9SDavid du Colombier 	}
1546906943f9SDavid du Colombier 	cio->toggle = Tddata1;
1547d37e33ffSDavid du Colombier 	epio(ep, cio, nil, 0, 0);
1548906943f9SDavid du Colombier 	qunlock(cio);
1549906943f9SDavid du Colombier 	poperror();
1550906943f9SDavid du Colombier 	ddeprint("epctlio cio %#p return %ld\n", cio, count);
1551906943f9SDavid du Colombier 	return count;
1552906943f9SDavid du Colombier }
1553906943f9SDavid du Colombier 
1554906943f9SDavid du Colombier static long
epwrite(Ep * ep,void * a,long count)1555906943f9SDavid du Colombier epwrite(Ep *ep, void *a, long count)
1556906943f9SDavid du Colombier {
1557906943f9SDavid du Colombier 	Ctlio *cio;
1558906943f9SDavid du Colombier 	Isoio *iso;
1559906943f9SDavid du Colombier 	Qio *io;
1560906943f9SDavid du Colombier 	ulong delta;
1561906943f9SDavid du Colombier 	char *b;
1562906943f9SDavid du Colombier 	int tot;
1563906943f9SDavid du Colombier 	int nw;
1564906943f9SDavid du Colombier 
1565906943f9SDavid du Colombier 	ddeprint("uhci: epwrite ep%d.%d\n", ep->dev->nb, ep->nb);
1566906943f9SDavid du Colombier 	if(ep->aux == nil)
1567906943f9SDavid du Colombier 		panic("uhci: epwrite: not open");
1568906943f9SDavid du Colombier 	switch(ep->ttype){
1569906943f9SDavid du Colombier 	case Tctl:
1570906943f9SDavid du Colombier 		cio = ep->aux;
1571906943f9SDavid du Colombier 		return epctlio(ep, cio, a, count);
1572906943f9SDavid du Colombier 	case Tbulk:
1573906943f9SDavid du Colombier 		io = ep->aux;
1574906943f9SDavid du Colombier 		if(ep->clrhalt)
1575906943f9SDavid du Colombier 			clrhalt(ep);
1576906943f9SDavid du Colombier 		/*
1577906943f9SDavid du Colombier 		 * Put at most Tdatomic Tds (512 bytes) at a time.
1578906943f9SDavid du Colombier 		 * Otherwise some devices produce babble errors.
1579906943f9SDavid du Colombier 		 */
1580906943f9SDavid du Colombier 		b = a;
1581906943f9SDavid du Colombier 		for(tot = 0; tot < count ; tot += nw){
1582906943f9SDavid du Colombier 			nw = count - tot;
1583906943f9SDavid du Colombier 			if(nw > Tdatomic * ep->maxpkt)
1584906943f9SDavid du Colombier 				nw = Tdatomic * ep->maxpkt;
1585d37e33ffSDavid du Colombier 			nw = epio(ep, &io[OWRITE], b+tot, nw, 1);
1586906943f9SDavid du Colombier 		}
1587906943f9SDavid du Colombier 		return tot;
1588906943f9SDavid du Colombier 	case Tintr:
1589906943f9SDavid du Colombier 		io = ep->aux;
1590906943f9SDavid du Colombier 		delta = TK2MS(MACHP(0)->ticks) - io[OWRITE].iotime + 1;
1591906943f9SDavid du Colombier 		if(delta < ep->pollival)
1592906943f9SDavid du Colombier 			tsleep(&up->sleep, return0, 0, ep->pollival - delta);
1593906943f9SDavid du Colombier 		if(ep->clrhalt)
1594906943f9SDavid du Colombier 			clrhalt(ep);
1595d37e33ffSDavid du Colombier 		return epio(ep, &io[OWRITE], a, count, 1);
1596906943f9SDavid du Colombier 	case Tiso:
1597906943f9SDavid du Colombier 		iso = ep->aux;
1598906943f9SDavid du Colombier 		return episowrite(ep, iso, a, count);
1599906943f9SDavid du Colombier 	default:
1600906943f9SDavid du Colombier 		panic("uhci: epwrite: bad ep ttype %d", ep->ttype);
1601906943f9SDavid du Colombier 	}
1602906943f9SDavid du Colombier 	return -1;
1603906943f9SDavid du Colombier }
1604906943f9SDavid du Colombier 
1605906943f9SDavid du Colombier static void
isoopen(Ep * ep)1606906943f9SDavid du Colombier isoopen(Ep *ep)
16079a747e4fSDavid du Colombier {
16089a747e4fSDavid du Colombier 	Ctlr *ctlr;
1609906943f9SDavid du Colombier 	Isoio *iso;
1610906943f9SDavid du Colombier 	int frno;
1611906943f9SDavid du Colombier 	int i;
1612906943f9SDavid du Colombier 	Td* td;
1613906943f9SDavid du Colombier 	Td* ltd;
1614906943f9SDavid du Colombier 	int size;
1615906943f9SDavid du Colombier 	int left;
16169a747e4fSDavid du Colombier 
1617906943f9SDavid du Colombier 	if(ep->mode == ORDWR)
1618906943f9SDavid du Colombier 		error("iso i/o is half-duplex");
1619906943f9SDavid du Colombier 	ctlr = ep->hp->aux;
1620906943f9SDavid du Colombier 	iso = ep->aux;
1621906943f9SDavid du Colombier 	iso->debug = ep->debug;
1622906943f9SDavid du Colombier 	iso->next = nil;			/* paranoia */
1623906943f9SDavid du Colombier 	if(ep->mode == OREAD)
1624906943f9SDavid du Colombier 		iso->tok = Tdtokin;
1625906943f9SDavid du Colombier 	else
1626906943f9SDavid du Colombier 		iso->tok = Tdtokout;
1627906943f9SDavid du Colombier 	iso->usbid = ((ep->nb & Epmax)<<7)|(ep->dev->nb & Devmax);
1628906943f9SDavid du Colombier 	iso->state = Qidle;
1629906943f9SDavid du Colombier 	iso->nframes = Nframes/ep->pollival;
1630906943f9SDavid du Colombier 	if(iso->nframes < 3)
1631906943f9SDavid du Colombier 		error("uhci isoopen bug");	/* we need at least 3 tds */
1632906943f9SDavid du Colombier 
16339a747e4fSDavid du Colombier 	ilock(ctlr);
1634d37e33ffSDavid du Colombier 	if(ctlr->load + ep->load > 800)
1635d37e33ffSDavid du Colombier 		print("usb: uhci: bandwidth may be exceeded\n");
1636906943f9SDavid du Colombier 	ctlr->load += ep->load;
1637906943f9SDavid du Colombier 	ctlr->isoload += ep->load;
1638906943f9SDavid du Colombier 	dprint("uhci: load %uld isoload %uld\n", ctlr->load, ctlr->isoload);
1639906943f9SDavid du Colombier 	iunlock(ctlr);
1640906943f9SDavid du Colombier 
1641906943f9SDavid du Colombier 	/*
1642906943f9SDavid du Colombier 	 * From here on this cannot raise errors
1643906943f9SDavid du Colombier 	 * unless we catch them and release here all memory allocated.
1644906943f9SDavid du Colombier 	 */
1645906943f9SDavid du Colombier 	if(ep->maxpkt > Tdndata)
1646906943f9SDavid du Colombier 		iso->data = smalloc(iso->nframes*ep->maxpkt);
1647906943f9SDavid du Colombier 	ilock(ctlr);
1648906943f9SDavid du Colombier 	frno = INS(Frnum) + 10;			/* start 10ms ahead */
1649906943f9SDavid du Colombier 	frno = TRUNC(frno, Nframes);
1650906943f9SDavid du Colombier 	iunlock(ctlr);
1651906943f9SDavid du Colombier 	iso->td0frno = frno;
1652906943f9SDavid du Colombier 	ltd = nil;
1653906943f9SDavid du Colombier 	left = 0;
1654906943f9SDavid du Colombier 	for(i = 0; i < iso->nframes; i++){
1655906943f9SDavid du Colombier 		td = iso->tdps[frno] = tdalloc();
1656906943f9SDavid du Colombier 		if(ep->mode == OREAD)
1657906943f9SDavid du Colombier 			size = ep->maxpkt;
1658906943f9SDavid du Colombier 		else{
1659906943f9SDavid du Colombier 			size = (ep->hz+left) * ep->pollival / 1000;
1660906943f9SDavid du Colombier 			size *= ep->samplesz;
1661906943f9SDavid du Colombier 			left = (ep->hz+left) * ep->pollival % 1000;
1662906943f9SDavid du Colombier 			if(size > ep->maxpkt){
1663906943f9SDavid du Colombier 				print("uhci: ep%d.%d: size > maxpkt\n",
1664906943f9SDavid du Colombier 					ep->dev->nb, ep->nb);
1665906943f9SDavid du Colombier 				print("size = %d max = %ld\n", size, ep->maxpkt);
1666906943f9SDavid du Colombier 				size = ep->maxpkt;
1667906943f9SDavid du Colombier 			}
1668906943f9SDavid du Colombier 		}
1669906943f9SDavid du Colombier 		if(size > Tdndata)
1670906943f9SDavid du Colombier 			td->data = iso->data + i * ep->maxpkt;
1671906943f9SDavid du Colombier 		else
1672906943f9SDavid du Colombier 			td->data = td->sbuff;
1673906943f9SDavid du Colombier 		td->buffer = PCIWADDR(td->data);
1674906943f9SDavid du Colombier 		tdisoinit(iso, td, size);
1675906943f9SDavid du Colombier 		if(ltd != nil)
1676906943f9SDavid du Colombier 			ltd->next = td;
1677906943f9SDavid du Colombier 		ltd = td;
1678906943f9SDavid du Colombier 		frno = TRUNC(frno+ep->pollival, Nframes);
1679906943f9SDavid du Colombier 	}
1680906943f9SDavid du Colombier 	ltd->next = iso->tdps[iso->td0frno];
1681906943f9SDavid du Colombier 	iso->tdi = iso->tdps[iso->td0frno];
1682906943f9SDavid du Colombier 	iso->tdu = iso->tdi;	/* read: right now; write: 1s ahead */
1683906943f9SDavid du Colombier 	ilock(ctlr);
1684906943f9SDavid du Colombier 	frno = iso->td0frno;
1685906943f9SDavid du Colombier 	for(i = 0; i < iso->nframes; i++){
1686906943f9SDavid du Colombier 		iso->tdps[frno]->link = ctlr->frames[frno];
1687906943f9SDavid du Colombier 		frno = TRUNC(frno+ep->pollival, Nframes);
1688906943f9SDavid du Colombier 	}
1689906943f9SDavid du Colombier 	coherence();
1690906943f9SDavid du Colombier 	frno = iso->td0frno;
1691906943f9SDavid du Colombier 	for(i = 0; i < iso->nframes; i++){
1692906943f9SDavid du Colombier 		ctlr->frames[frno] = PCIWADDR(iso->tdps[frno]);
1693906943f9SDavid du Colombier 		frno = TRUNC(frno+ep->pollival, Nframes);
1694906943f9SDavid du Colombier 	}
1695906943f9SDavid du Colombier 	iso->next = ctlr->iso;
1696906943f9SDavid du Colombier 	ctlr->iso = iso;
1697906943f9SDavid du Colombier 	iso->state = Qdone;
1698906943f9SDavid du Colombier 	iunlock(ctlr);
1699906943f9SDavid du Colombier 	if(debug > 1 || iso->debug >1)
1700906943f9SDavid du Colombier 		isodump(iso, 0);
1701906943f9SDavid du Colombier }
1702906943f9SDavid du Colombier 
1703906943f9SDavid du Colombier /*
1704906943f9SDavid du Colombier  * Allocate the endpoint and set it up for I/O
1705906943f9SDavid du Colombier  * in the controller. This must follow what's said
1706906943f9SDavid du Colombier  * in Ep regarding configuration, including perhaps
1707906943f9SDavid du Colombier  * the saved toggles (saved on a previous close of
1708906943f9SDavid du Colombier  * the endpoint data file by epclose).
1709906943f9SDavid du Colombier  */
1710906943f9SDavid du Colombier static void
epopen(Ep * ep)1711906943f9SDavid du Colombier epopen(Ep *ep)
1712906943f9SDavid du Colombier {
1713906943f9SDavid du Colombier 	Ctlr *ctlr;
1714906943f9SDavid du Colombier 	Qh *cqh;
1715906943f9SDavid du Colombier 	Qio *io;
1716906943f9SDavid du Colombier 	Ctlio *cio;
1717906943f9SDavid du Colombier 	int usbid;
1718906943f9SDavid du Colombier 
1719906943f9SDavid du Colombier 	ctlr = ep->hp->aux;
1720906943f9SDavid du Colombier 	deprint("uhci: epopen ep%d.%d\n", ep->dev->nb, ep->nb);
1721906943f9SDavid du Colombier 	if(ep->aux != nil)
1722906943f9SDavid du Colombier 		panic("uhci: epopen called with open ep");
1723906943f9SDavid du Colombier 	if(waserror()){
1724906943f9SDavid du Colombier 		free(ep->aux);
1725906943f9SDavid du Colombier 		ep->aux = nil;
1726906943f9SDavid du Colombier 		nexterror();
1727906943f9SDavid du Colombier 	}
1728906943f9SDavid du Colombier 	if(ep->maxpkt > Tdmaxpkt){
1729906943f9SDavid du Colombier 		print("uhci: maxkpkt too large: using %d\n", Tdmaxpkt);
1730906943f9SDavid du Colombier 		ep->maxpkt = Tdmaxpkt;
1731906943f9SDavid du Colombier 	}
1732906943f9SDavid du Colombier 	cqh = ctlr->qh[ep->ttype];
1733906943f9SDavid du Colombier 	switch(ep->ttype){
1734906943f9SDavid du Colombier 	case Tnone:
1735906943f9SDavid du Colombier 		error("endpoint not configured");
1736906943f9SDavid du Colombier 	case Tiso:
1737906943f9SDavid du Colombier 		ep->aux = smalloc(sizeof(Isoio));
1738906943f9SDavid du Colombier 		isoopen(ep);
1739906943f9SDavid du Colombier 		break;
1740906943f9SDavid du Colombier 	case Tctl:
1741906943f9SDavid du Colombier 		cio = ep->aux = smalloc(sizeof(Ctlio));
1742906943f9SDavid du Colombier 		cio->debug = ep->debug;
1743906943f9SDavid du Colombier 		cio->ndata = -1;
1744906943f9SDavid du Colombier 		cio->data = nil;
1745906943f9SDavid du Colombier 		if(ep->dev->isroot != 0 && ep->nb == 0)	/* root hub */
1746906943f9SDavid du Colombier 			break;
1747906943f9SDavid du Colombier 		cio->qh = qhalloc(ctlr, cqh, cio, "epc");
1748906943f9SDavid du Colombier 		break;
1749906943f9SDavid du Colombier 	case Tbulk:
1750906943f9SDavid du Colombier 	case Tintr:
1751906943f9SDavid du Colombier 		io = ep->aux = smalloc(sizeof(Qio)*2);
1752906943f9SDavid du Colombier 		io[OREAD].debug = io[OWRITE].debug = ep->debug;
1753906943f9SDavid du Colombier 		usbid = ((ep->nb&Epmax)<<7)|(ep->dev->nb &Devmax);
1754906943f9SDavid du Colombier 		if(ep->mode != OREAD){
1755906943f9SDavid du Colombier 			if(ep->toggle[OWRITE] != 0)
1756906943f9SDavid du Colombier 				io[OWRITE].toggle = Tddata1;
1757906943f9SDavid du Colombier 			else
1758906943f9SDavid du Colombier 				io[OWRITE].toggle = Tddata0;
1759906943f9SDavid du Colombier 			io[OWRITE].tok = Tdtokout;
1760906943f9SDavid du Colombier 			io[OWRITE].qh = qhalloc(ctlr, cqh, io+OWRITE, "epw");
1761906943f9SDavid du Colombier 			io[OWRITE].usbid = usbid;
1762906943f9SDavid du Colombier 		}
1763906943f9SDavid du Colombier 		if(ep->mode != OWRITE){
1764906943f9SDavid du Colombier 			if(ep->toggle[OREAD] != 0)
1765906943f9SDavid du Colombier 				io[OREAD].toggle = Tddata1;
1766906943f9SDavid du Colombier 			else
1767906943f9SDavid du Colombier 				io[OREAD].toggle = Tddata0;
1768906943f9SDavid du Colombier 			io[OREAD].tok = Tdtokin;
1769906943f9SDavid du Colombier 			io[OREAD].qh = qhalloc(ctlr, cqh, io+OREAD, "epr");
1770906943f9SDavid du Colombier 			io[OREAD].usbid = usbid;
1771906943f9SDavid du Colombier 		}
1772906943f9SDavid du Colombier 		break;
1773906943f9SDavid du Colombier 	}
1774906943f9SDavid du Colombier 	if(debug>1 || ep->debug)
1775906943f9SDavid du Colombier 		dump(ep->hp);
1776906943f9SDavid du Colombier 	deprint("uhci: epopen done\n");
1777906943f9SDavid du Colombier 	poperror();
1778906943f9SDavid du Colombier }
1779906943f9SDavid du Colombier 
1780906943f9SDavid du Colombier static void
cancelio(Ctlr * ctlr,Qio * io)1781906943f9SDavid du Colombier cancelio(Ctlr *ctlr, Qio *io)
1782906943f9SDavid du Colombier {
1783906943f9SDavid du Colombier 	Qh *qh;
1784906943f9SDavid du Colombier 
1785906943f9SDavid du Colombier 	ilock(ctlr);
1786906943f9SDavid du Colombier 	qh = io->qh;
1787906943f9SDavid du Colombier 	if(io == nil || io->qh == nil || io->qh->state == Qclose){
1788906943f9SDavid du Colombier 		iunlock(ctlr);
1789906943f9SDavid du Colombier 		return;
1790906943f9SDavid du Colombier 	}
1791906943f9SDavid du Colombier 	dqprint("uhci: cancelio for qh %#p state %s\n",
1792906943f9SDavid du Colombier 		qh, qhsname[qh->state]);
1793906943f9SDavid du Colombier 	aborttds(qh);
1794906943f9SDavid du Colombier 	qh->state = Qclose;
1795906943f9SDavid du Colombier 	iunlock(ctlr);
1796906943f9SDavid du Colombier 	if(!waserror()){
1797906943f9SDavid du Colombier 		tsleep(&up->sleep, return0, 0, Abortdelay);
1798906943f9SDavid du Colombier 		poperror();
1799906943f9SDavid du Colombier 	}
1800906943f9SDavid du Colombier 
1801906943f9SDavid du Colombier 	wakeup(io);
1802906943f9SDavid du Colombier 	qlock(io);
1803906943f9SDavid du Colombier 	/* wait for epio if running */
1804906943f9SDavid du Colombier 	qunlock(io);
1805906943f9SDavid du Colombier 
1806906943f9SDavid du Colombier 	qhfree(ctlr, qh);
1807906943f9SDavid du Colombier 	io->qh = nil;
1808906943f9SDavid du Colombier }
1809906943f9SDavid du Colombier 
1810906943f9SDavid du Colombier static void
cancelisoio(Ctlr * ctlr,Isoio * iso,int pollival,ulong load)1811906943f9SDavid du Colombier cancelisoio(Ctlr *ctlr, Isoio *iso, int pollival, ulong load)
1812906943f9SDavid du Colombier {
1813906943f9SDavid du Colombier 	Isoio **il;
1814906943f9SDavid du Colombier 	ulong *lp;
1815906943f9SDavid du Colombier 	int i;
1816906943f9SDavid du Colombier 	int frno;
1817906943f9SDavid du Colombier 	Td *td;
1818906943f9SDavid du Colombier 
1819906943f9SDavid du Colombier 	ilock(ctlr);
1820906943f9SDavid du Colombier 	if(iso->state == Qclose){
1821906943f9SDavid du Colombier 		iunlock(ctlr);
1822906943f9SDavid du Colombier 		return;
1823906943f9SDavid du Colombier 	}
1824906943f9SDavid du Colombier 	if(iso->state != Qrun && iso->state != Qdone)
1825906943f9SDavid du Colombier 		panic("bad iso state");
1826906943f9SDavid du Colombier 	iso->state = Qclose;
1827906943f9SDavid du Colombier 	if(ctlr->isoload < load)
1828906943f9SDavid du Colombier 		panic("uhci: low isoload");
1829906943f9SDavid du Colombier 	ctlr->isoload -= load;
1830906943f9SDavid du Colombier 	ctlr->load -= load;
1831906943f9SDavid du Colombier 	for(il = &ctlr->iso; *il != nil; il = &(*il)->next)
1832906943f9SDavid du Colombier 		if(*il == iso)
1833906943f9SDavid du Colombier 			break;
1834906943f9SDavid du Colombier 	if(*il == nil)
1835906943f9SDavid du Colombier 		panic("isocancel: not found");
1836906943f9SDavid du Colombier 	*il = iso->next;
1837906943f9SDavid du Colombier 	frno = iso->td0frno;
1838906943f9SDavid du Colombier 	for(i = 0; i < iso->nframes; i++){
1839906943f9SDavid du Colombier 		td = iso->tdps[frno];
1840906943f9SDavid du Colombier 		td->csw &= ~(Tdioc|Tdactive);
1841906943f9SDavid du Colombier 		for(lp=&ctlr->frames[frno]; !(*lp & Tdterm);
1842906943f9SDavid du Colombier 					lp = &TPTR(*lp)->link)
1843906943f9SDavid du Colombier 			if(TPTR(*lp) == td)
1844906943f9SDavid du Colombier 				break;
1845906943f9SDavid du Colombier 		if(*lp & Tdterm)
1846906943f9SDavid du Colombier 			panic("cancelisoio: td not found");
1847906943f9SDavid du Colombier 		*lp = td->link;
1848906943f9SDavid du Colombier 		frno = TRUNC(frno+pollival, Nframes);
1849906943f9SDavid du Colombier 	}
1850906943f9SDavid du Colombier 	iunlock(ctlr);
1851906943f9SDavid du Colombier 
1852906943f9SDavid du Colombier 	/*
1853906943f9SDavid du Colombier 	 * wakeup anyone waiting for I/O and
1854906943f9SDavid du Colombier 	 * wait to be sure no I/O is in progress in the controller.
1855906943f9SDavid du Colombier 	 * and then wait to be sure episo-io is no longer running.
1856906943f9SDavid du Colombier 	 */
1857906943f9SDavid du Colombier 	wakeup(iso);
1858906943f9SDavid du Colombier 	diprint("cancelisoio iso %#p waiting for I/O to cease\n", iso);
1859906943f9SDavid du Colombier 	tsleep(&up->sleep, return0, 0, 5);
1860906943f9SDavid du Colombier 	qlock(iso);
1861906943f9SDavid du Colombier 	qunlock(iso);
1862906943f9SDavid du Colombier 	diprint("cancelisoio iso %#p releasing iso\n", iso);
1863906943f9SDavid du Colombier 
1864906943f9SDavid du Colombier 	frno = iso->td0frno;
1865906943f9SDavid du Colombier 	for(i = 0; i < iso->nframes; i++){
1866906943f9SDavid du Colombier 		tdfree(iso->tdps[frno]);
1867906943f9SDavid du Colombier 		iso->tdps[frno] = nil;
1868906943f9SDavid du Colombier 		frno = TRUNC(frno+pollival, Nframes);
1869906943f9SDavid du Colombier 	}
1870906943f9SDavid du Colombier 	free(iso->data);
1871906943f9SDavid du Colombier 	iso->data = nil;
1872906943f9SDavid du Colombier }
1873906943f9SDavid du Colombier 
1874906943f9SDavid du Colombier static void
epclose(Ep * ep)1875906943f9SDavid du Colombier epclose(Ep *ep)
1876906943f9SDavid du Colombier {
1877906943f9SDavid du Colombier 	Ctlr *ctlr;
1878906943f9SDavid du Colombier 	Ctlio *cio;
1879906943f9SDavid du Colombier 	Isoio *iso;
1880906943f9SDavid du Colombier 	Qio *io;
1881906943f9SDavid du Colombier 
1882906943f9SDavid du Colombier 	ctlr = ep->hp->aux;
1883906943f9SDavid du Colombier 	deprint("uhci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
1884906943f9SDavid du Colombier 
1885906943f9SDavid du Colombier 	if(ep->aux == nil)
1886906943f9SDavid du Colombier 		panic("uhci: epclose called with closed ep");
1887906943f9SDavid du Colombier 	switch(ep->ttype){
1888906943f9SDavid du Colombier 	case Tctl:
1889906943f9SDavid du Colombier 		cio = ep->aux;
1890906943f9SDavid du Colombier 		cancelio(ctlr, cio);
1891906943f9SDavid du Colombier 		free(cio->data);
1892906943f9SDavid du Colombier 		cio->data = nil;
1893906943f9SDavid du Colombier 		break;
1894906943f9SDavid du Colombier 	case Tbulk:
1895906943f9SDavid du Colombier 	case Tintr:
1896906943f9SDavid du Colombier 		io = ep->aux;
1897906943f9SDavid du Colombier 		ep->toggle[OREAD] = ep->toggle[OWRITE] = 0;
1898906943f9SDavid du Colombier 		if(ep->mode != OWRITE){
1899906943f9SDavid du Colombier 			cancelio(ctlr, &io[OREAD]);
1900906943f9SDavid du Colombier 			if(io[OREAD].toggle == Tddata1)
1901906943f9SDavid du Colombier 				ep->toggle[OREAD] = 1;
1902906943f9SDavid du Colombier 		}
1903906943f9SDavid du Colombier 		if(ep->mode != OREAD){
1904906943f9SDavid du Colombier 			cancelio(ctlr, &io[OWRITE]);
1905906943f9SDavid du Colombier 			if(io[OWRITE].toggle == Tddata1)
1906906943f9SDavid du Colombier 				ep->toggle[OWRITE] = 1;
1907906943f9SDavid du Colombier 		}
1908906943f9SDavid du Colombier 		break;
1909906943f9SDavid du Colombier 	case Tiso:
1910906943f9SDavid du Colombier 		iso = ep->aux;
1911906943f9SDavid du Colombier 		cancelisoio(ctlr, iso, ep->pollival, ep->load);
1912906943f9SDavid du Colombier 		break;
1913906943f9SDavid du Colombier 	default:
1914906943f9SDavid du Colombier 		panic("epclose: bad ttype %d", ep->ttype);
1915906943f9SDavid du Colombier 	}
1916906943f9SDavid du Colombier 
1917906943f9SDavid du Colombier 	free(ep->aux);
1918906943f9SDavid du Colombier 	ep->aux = nil;
1919906943f9SDavid du Colombier 
1920906943f9SDavid du Colombier }
1921906943f9SDavid du Colombier 
1922906943f9SDavid du Colombier static char*
seprintep(char * s,char * e,Ep * ep)1923906943f9SDavid du Colombier seprintep(char *s, char *e, Ep *ep)
1924906943f9SDavid du Colombier {
1925906943f9SDavid du Colombier 	Ctlio *cio;
1926906943f9SDavid du Colombier 	Qio *io;
1927906943f9SDavid du Colombier 	Isoio *iso;
1928906943f9SDavid du Colombier 	Ctlr *ctlr;
1929906943f9SDavid du Colombier 
1930906943f9SDavid du Colombier 	ctlr = ep->hp->aux;
1931906943f9SDavid du Colombier 	ilock(ctlr);
1932906943f9SDavid du Colombier 	if(ep->aux == nil){
1933906943f9SDavid du Colombier 		*s = 0;
1934906943f9SDavid du Colombier 		iunlock(ctlr);
1935906943f9SDavid du Colombier 		return s;
1936906943f9SDavid du Colombier 	}
1937906943f9SDavid du Colombier 	switch(ep->ttype){
1938906943f9SDavid du Colombier 	case Tctl:
1939906943f9SDavid du Colombier 		cio = ep->aux;
1940906943f9SDavid du Colombier 		s = seprint(s,e,"cio %#p qh %#p"
1941906943f9SDavid du Colombier 			" id %#x tog %#x tok %#x err %s\n",
1942906943f9SDavid du Colombier 			cio, cio->qh, cio->usbid, cio->toggle,
1943906943f9SDavid du Colombier 			cio->tok, cio->err);
1944906943f9SDavid du Colombier 		break;
1945906943f9SDavid du Colombier 	case Tbulk:
1946906943f9SDavid du Colombier 	case Tintr:
1947906943f9SDavid du Colombier 		io = ep->aux;
1948906943f9SDavid du Colombier 		if(ep->mode != OWRITE)
1949906943f9SDavid du Colombier 			s = seprint(s,e,"r: qh %#p id %#x tog %#x tok %#x err %s\n",
1950906943f9SDavid du Colombier 				io[OREAD].qh, io[OREAD].usbid, io[OREAD].toggle,
1951906943f9SDavid du Colombier 				io[OREAD].tok, io[OREAD].err);
1952906943f9SDavid du Colombier 		if(ep->mode != OREAD)
1953906943f9SDavid du Colombier 			s = seprint(s,e,"w: qh %#p id %#x tog %#x tok %#x err %s\n",
1954906943f9SDavid du Colombier 				io[OWRITE].qh, io[OWRITE].usbid, io[OWRITE].toggle,
1955906943f9SDavid du Colombier 				io[OWRITE].tok, io[OWRITE].err);
1956906943f9SDavid du Colombier 		break;
1957906943f9SDavid du Colombier 	case Tiso:
1958906943f9SDavid du Colombier 		iso = ep->aux;
1959906943f9SDavid du Colombier 		s = seprint(s,e,"iso %#p id %#x tok %#x tdu %#p tdi %#p err %s\n",
1960906943f9SDavid du Colombier 			iso, iso->usbid, iso->tok, iso->tdu, iso->tdi, iso->err);
1961906943f9SDavid du Colombier 		break;
1962906943f9SDavid du Colombier 	}
1963906943f9SDavid du Colombier 	iunlock(ctlr);
1964906943f9SDavid du Colombier 	return s;
1965906943f9SDavid du Colombier }
1966906943f9SDavid du Colombier 
1967906943f9SDavid du Colombier static int
portenable(Hci * hp,int port,int on)1968906943f9SDavid du Colombier portenable(Hci *hp, int port, int on)
1969906943f9SDavid du Colombier {
1970906943f9SDavid du Colombier 	int s;
1971906943f9SDavid du Colombier 	int ioport;
1972906943f9SDavid du Colombier 	Ctlr *ctlr;
1973906943f9SDavid du Colombier 
1974906943f9SDavid du Colombier 	ctlr = hp->aux;
1975906943f9SDavid du Colombier 	dprint("uhci: %#x port %d enable=%d\n", ctlr->port, port, on);
1976906943f9SDavid du Colombier 	ioport = PORT(port-1);
1977906943f9SDavid du Colombier 	qlock(&ctlr->portlck);
1978906943f9SDavid du Colombier 	if(waserror()){
1979906943f9SDavid du Colombier 		qunlock(&ctlr->portlck);
1980906943f9SDavid du Colombier 		nexterror();
1981906943f9SDavid du Colombier 	}
1982906943f9SDavid du Colombier 	ilock(ctlr);
1983906943f9SDavid du Colombier 	s = INS(ioport);
1984906943f9SDavid du Colombier 	if(on)
1985906943f9SDavid du Colombier 		OUTS(ioport, s | PSenable);
1986906943f9SDavid du Colombier 	else
1987906943f9SDavid du Colombier 		OUTS(ioport, s & ~PSenable);
1988906943f9SDavid du Colombier 	microdelay(64);
1989906943f9SDavid du Colombier 	iunlock(ctlr);
1990906943f9SDavid du Colombier 	tsleep(&up->sleep, return0, 0, Enabledelay);
1991906943f9SDavid du Colombier 	dprint("uhci %#ux port %d enable=%d: sts %#x\n",
1992906943f9SDavid du Colombier 		ctlr->port, port, on, INS(ioport));
1993906943f9SDavid du Colombier 	qunlock(&ctlr->portlck);
1994906943f9SDavid du Colombier 	poperror();
1995906943f9SDavid du Colombier 	return 0;
1996906943f9SDavid du Colombier }
1997906943f9SDavid du Colombier 
1998906943f9SDavid du Colombier static int
portreset(Hci * hp,int port,int on)1999906943f9SDavid du Colombier portreset(Hci *hp, int port, int on)
2000906943f9SDavid du Colombier {
2001906943f9SDavid du Colombier 	int i, p;
2002906943f9SDavid du Colombier 	Ctlr *ctlr;
2003906943f9SDavid du Colombier 
2004906943f9SDavid du Colombier 	if(on == 0)
2005906943f9SDavid du Colombier 		return 0;
2006906943f9SDavid du Colombier 	ctlr = hp->aux;
2007906943f9SDavid du Colombier 	dprint("uhci: %#ux port %d reset\n", ctlr->port, port);
2008906943f9SDavid du Colombier 	p = PORT(port-1);
2009906943f9SDavid du Colombier 	ilock(ctlr);
2010906943f9SDavid du Colombier 	OUTS(p, PSreset);
2011906943f9SDavid du Colombier 	delay(50);
2012906943f9SDavid du Colombier 	OUTS(p, INS(p) & ~PSreset);
2013906943f9SDavid du Colombier 	OUTS(p, INS(p) | PSenable);
2014906943f9SDavid du Colombier 	microdelay(64);
2015906943f9SDavid du Colombier 	for(i=0; i<1000 && (INS(p) & PSenable) == 0; i++)
2016906943f9SDavid du Colombier 		;
2017906943f9SDavid du Colombier 	OUTS(p, (INS(p) & ~PSreset)|PSenable);
2018906943f9SDavid du Colombier 	iunlock(ctlr);
2019906943f9SDavid du Colombier 	dprint("uhci %#ux after port %d reset: sts %#x\n",
2020906943f9SDavid du Colombier 		ctlr->port, port, INS(p));
2021906943f9SDavid du Colombier 	return 0;
2022906943f9SDavid du Colombier }
2023906943f9SDavid du Colombier 
2024906943f9SDavid du Colombier static int
portstatus(Hci * hp,int port)2025906943f9SDavid du Colombier portstatus(Hci *hp, int port)
2026906943f9SDavid du Colombier {
2027906943f9SDavid du Colombier 	int s;
2028906943f9SDavid du Colombier 	int r;
2029906943f9SDavid du Colombier 	int ioport;
2030906943f9SDavid du Colombier 	Ctlr *ctlr;
2031906943f9SDavid du Colombier 
2032906943f9SDavid du Colombier 	ctlr = hp->aux;
2033906943f9SDavid du Colombier 	ioport = PORT(port-1);
2034906943f9SDavid du Colombier 	qlock(&ctlr->portlck);
2035906943f9SDavid du Colombier 	if(waserror()){
2036906943f9SDavid du Colombier 		iunlock(ctlr);
2037906943f9SDavid du Colombier 		qunlock(&ctlr->portlck);
2038906943f9SDavid du Colombier 		nexterror();
2039906943f9SDavid du Colombier 	}
2040906943f9SDavid du Colombier 	ilock(ctlr);
2041906943f9SDavid du Colombier 	s = INS(ioport);
2042906943f9SDavid du Colombier 	if(s & (PSstatuschg | PSchange)){
2043906943f9SDavid du Colombier 		OUTS(ioport, s);
2044906943f9SDavid du Colombier 		ddprint("uhci %#ux port %d status %#x\n", ctlr->port, port, s);
2045906943f9SDavid du Colombier 	}
2046906943f9SDavid du Colombier 	iunlock(ctlr);
2047906943f9SDavid du Colombier 	qunlock(&ctlr->portlck);
2048906943f9SDavid du Colombier 	poperror();
2049906943f9SDavid du Colombier 
2050906943f9SDavid du Colombier 	/*
2051906943f9SDavid du Colombier 	 * We must return status bits as a
2052906943f9SDavid du Colombier 	 * get port status hub request would do.
2053906943f9SDavid du Colombier 	 */
2054906943f9SDavid du Colombier 	r = 0;
2055906943f9SDavid du Colombier 	if(s & PSpresent)
2056906943f9SDavid du Colombier 		r |= HPpresent;
2057906943f9SDavid du Colombier 	if(s & PSenable)
2058906943f9SDavid du Colombier 		r |= HPenable;
2059906943f9SDavid du Colombier 	if(s & PSsuspend)
2060906943f9SDavid du Colombier 		r |= HPsuspend;
2061906943f9SDavid du Colombier 	if(s & PSreset)
2062906943f9SDavid du Colombier 		r |= HPreset;
2063906943f9SDavid du Colombier 	if(s & PSslow)
2064906943f9SDavid du Colombier 		r |= HPslow;
2065906943f9SDavid du Colombier 	if(s & PSstatuschg)
2066906943f9SDavid du Colombier 		r |= HPstatuschg;
2067906943f9SDavid du Colombier 	if(s & PSchange)
2068906943f9SDavid du Colombier 		r |= HPchange;
2069906943f9SDavid du Colombier 	return r;
20709a747e4fSDavid du Colombier }
20719a747e4fSDavid du Colombier 
20729a747e4fSDavid du Colombier static void
scanpci(void)20739a747e4fSDavid du Colombier scanpci(void)
20749a747e4fSDavid du Colombier {
2075906943f9SDavid du Colombier 	static int already = 0;
20769a747e4fSDavid du Colombier 	int io;
2077906943f9SDavid du Colombier 	int i;
20789a747e4fSDavid du Colombier 	Ctlr *ctlr;
20799a747e4fSDavid du Colombier 	Pcidev *p;
20809a747e4fSDavid du Colombier 
20819a747e4fSDavid du Colombier 	if(already)
20829a747e4fSDavid du Colombier 		return;
20839a747e4fSDavid du Colombier 	already = 1;
20849a747e4fSDavid du Colombier 	p = nil;
20859a747e4fSDavid du Colombier 	while(p = pcimatch(p, 0, 0)){
20869a747e4fSDavid du Colombier 		/*
2087ade43d10SDavid du Colombier 		 * Find UHCI controllers (Programming Interface = 0).
20889a747e4fSDavid du Colombier 		 */
2089ade43d10SDavid du Colombier 		if(p->ccrb != Pcibcserial || p->ccru != Pciscusb)
20909a747e4fSDavid du Colombier 			continue;
209115174232SDavid du Colombier 		switch(p->ccrp){
2092ade43d10SDavid du Colombier 		case 0:
20939a747e4fSDavid du Colombier 			io = p->mem[4].bar & ~0x0F;
209415174232SDavid du Colombier 			break;
209515174232SDavid du Colombier 		default:
209615174232SDavid du Colombier 			continue;
209715174232SDavid du Colombier 		}
20989a747e4fSDavid du Colombier 		if(io == 0){
2099906943f9SDavid du Colombier 			print("usbuhci: %#x %#x: failed to map registers\n",
210041dd6b47SDavid du Colombier 				p->vid, p->did);
21019a747e4fSDavid du Colombier 			continue;
21029a747e4fSDavid du Colombier 		}
21039a747e4fSDavid du Colombier 		if(ioalloc(io, p->mem[4].size, 0, "usbuhci") < 0){
210441dd6b47SDavid du Colombier 			print("usbuhci: port %#ux in use\n", io);
21059a747e4fSDavid du Colombier 			continue;
21069a747e4fSDavid du Colombier 		}
21079a747e4fSDavid du Colombier 		if(p->intl == 0xFF || p->intl == 0){
210841dd6b47SDavid du Colombier 			print("usbuhci: no irq assigned for port %#ux\n", io);
21099a747e4fSDavid du Colombier 			continue;
21109a747e4fSDavid du Colombier 		}
21119a747e4fSDavid du Colombier 
2112906943f9SDavid du Colombier 		dprint("uhci: %#x %#x: port %#ux size %#x irq %d\n",
21139a747e4fSDavid du Colombier 			p->vid, p->did, io, p->mem[4].size, p->intl);
21149a747e4fSDavid du Colombier 
2115*06d03f7fSDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
2116*06d03f7fSDavid du Colombier 		if (ctlr == nil)
2117*06d03f7fSDavid du Colombier 			panic("uhci: out of memory");
21189a747e4fSDavid du Colombier 		ctlr->pcidev = p;
2119906943f9SDavid du Colombier 		ctlr->port = io;
2120906943f9SDavid du Colombier 		for(i = 0; i < Nhcis; i++)
2121906943f9SDavid du Colombier 			if(ctlrs[i] == nil){
2122906943f9SDavid du Colombier 				ctlrs[i] = ctlr;
2123906943f9SDavid du Colombier 				break;
21249a747e4fSDavid du Colombier 			}
2125906943f9SDavid du Colombier 		if(i == Nhcis)
2126906943f9SDavid du Colombier 			print("uhci: bug: no more controllers\n");
2127906943f9SDavid du Colombier 	}
2128906943f9SDavid du Colombier }
2129906943f9SDavid du Colombier 
2130906943f9SDavid du Colombier static void
uhcimeminit(Ctlr * ctlr)2131906943f9SDavid du Colombier uhcimeminit(Ctlr *ctlr)
2132906943f9SDavid du Colombier {
2133906943f9SDavid du Colombier 	Td* td;
2134906943f9SDavid du Colombier 	Qh *qh;
2135906943f9SDavid du Colombier 	int frsize;
2136906943f9SDavid du Colombier 	int i;
2137906943f9SDavid du Colombier 
2138906943f9SDavid du Colombier 	ctlr->qhs = ctlr->qh[Tctl] = qhalloc(ctlr, nil, nil, "CTL");
2139906943f9SDavid du Colombier 	ctlr->qh[Tintr] = qhalloc(ctlr, ctlr->qh[Tctl], nil, "INT");
2140906943f9SDavid du Colombier 	ctlr->qh[Tbulk] = qhalloc(ctlr, ctlr->qh[Tintr], nil, "BLK");
2141906943f9SDavid du Colombier 
2142906943f9SDavid du Colombier 	/* idle Td from dummy Qh at the end. looped back to itself */
2143906943f9SDavid du Colombier 	/* This is a workaround for PIIX4 errata 29773804.pdf */
2144906943f9SDavid du Colombier 	qh = qhalloc(ctlr, ctlr->qh[Tbulk], nil, "BWS");
2145906943f9SDavid du Colombier 	td = tdalloc();
2146906943f9SDavid du Colombier 	td->link = PCIWADDR(td);
2147906943f9SDavid du Colombier 	qhlinktd(qh, td);
2148906943f9SDavid du Colombier 
2149906943f9SDavid du Colombier 	/* loop (hw only) from the last qh back to control xfers.
2150906943f9SDavid du Colombier 	 * this may be done only for some of them. Disable until ehci comes.
2151906943f9SDavid du Colombier 	 */
2152906943f9SDavid du Colombier 	if(0)
2153906943f9SDavid du Colombier 	qh->link = PCIWADDR(ctlr->qhs);
2154906943f9SDavid du Colombier 
2155906943f9SDavid du Colombier 	frsize = Nframes*sizeof(ulong);
2156906943f9SDavid du Colombier 	ctlr->frames = xspanalloc(frsize, frsize, 0);
2157906943f9SDavid du Colombier 	if(ctlr->frames == nil)
2158c9b6d007SDavid du Colombier 		panic("uhci reset: no memory");
2159906943f9SDavid du Colombier 
2160906943f9SDavid du Colombier 	ctlr->iso = nil;
2161906943f9SDavid du Colombier 	for(i = 0; i < Nframes; i++)
2162906943f9SDavid du Colombier 		ctlr->frames[i] = PCIWADDR(ctlr->qhs)|QHlinkqh;
2163906943f9SDavid du Colombier 	OUTL(Flbaseadd, PCIWADDR(ctlr->frames));
2164906943f9SDavid du Colombier 	OUTS(Frnum, 0);
2165906943f9SDavid du Colombier 	dprint("uhci %#ux flb %#ulx frno %#ux\n", ctlr->port,
2166906943f9SDavid du Colombier 		INL(Flbaseadd), INS(Frnum));
2167906943f9SDavid du Colombier }
2168906943f9SDavid du Colombier 
2169906943f9SDavid du Colombier static void
init(Hci * hp)2170906943f9SDavid du Colombier init(Hci *hp)
2171906943f9SDavid du Colombier {
2172906943f9SDavid du Colombier 	Ctlr *ctlr;
2173906943f9SDavid du Colombier 	int sts;
2174906943f9SDavid du Colombier 	int i;
2175906943f9SDavid du Colombier 
2176906943f9SDavid du Colombier 	ctlr = hp->aux;
2177906943f9SDavid du Colombier 	dprint("uhci %#ux init\n", ctlr->port);
2178906943f9SDavid du Colombier 	coherence();
2179906943f9SDavid du Colombier 	ilock(ctlr);
2180906943f9SDavid du Colombier 	OUTS(Usbintr, Itmout|Iresume|Ioc|Ishort);
2181906943f9SDavid du Colombier 	uhcirun(ctlr, 1);
2182906943f9SDavid du Colombier 	dprint("uhci: init: cmd %#ux sts %#ux sof %#ux",
2183906943f9SDavid du Colombier 		INS(Cmd), INS(Status), INS(SOFmod));
2184906943f9SDavid du Colombier 	dprint(" flb %#ulx frno %#ux psc0 %#ux psc1 %#ux",
2185906943f9SDavid du Colombier 		INL(Flbaseadd), INS(Frnum), INS(PORT(0)), INS(PORT(1)));
2186906943f9SDavid du Colombier 	/* guess other ports */
2187906943f9SDavid du Colombier 	for(i = 2; i < 6; i++){
2188906943f9SDavid du Colombier 		sts = INS(PORT(i));
2189906943f9SDavid du Colombier 		if(sts != 0xFFFF && (sts & PSreserved1) == 1){
2190906943f9SDavid du Colombier 			dprint(" psc%d %#ux", i, sts);
2191906943f9SDavid du Colombier 			hp->nports++;
2192906943f9SDavid du Colombier 		}else
2193906943f9SDavid du Colombier 			break;
2194906943f9SDavid du Colombier 	}
2195906943f9SDavid du Colombier 	for(i = 0; i < hp->nports; i++)
2196906943f9SDavid du Colombier 		OUTS(PORT(i), 0);
2197906943f9SDavid du Colombier 	iunlock(ctlr);
2198906943f9SDavid du Colombier }
2199906943f9SDavid du Colombier 
2200906943f9SDavid du Colombier static void
uhcireset(Ctlr * ctlr)2201906943f9SDavid du Colombier uhcireset(Ctlr *ctlr)
2202906943f9SDavid du Colombier {
2203906943f9SDavid du Colombier 	int i;
2204906943f9SDavid du Colombier 	int sof;
2205906943f9SDavid du Colombier 
2206906943f9SDavid du Colombier 	ilock(ctlr);
2207906943f9SDavid du Colombier 	dprint("uhci %#ux reset\n", ctlr->port);
2208906943f9SDavid du Colombier 
2209906943f9SDavid du Colombier 	/*
2210906943f9SDavid du Colombier 	 * Turn off legacy mode. Some controllers won't
2211906943f9SDavid du Colombier 	 * interrupt us as expected otherwise.
2212906943f9SDavid du Colombier 	 */
2213906943f9SDavid du Colombier 	uhcirun(ctlr, 0);
2214906943f9SDavid du Colombier 	pcicfgw16(ctlr->pcidev, 0xc0, 0x2000);
2215906943f9SDavid du Colombier 
2216906943f9SDavid du Colombier 	OUTS(Usbintr, 0);
2217906943f9SDavid du Colombier 	sof = INB(SOFmod);
2218906943f9SDavid du Colombier 	uhcicmd(ctlr, Cgreset);			/* global reset */
2219906943f9SDavid du Colombier 	delay(Resetdelay);
2220906943f9SDavid du Colombier 	uhcicmd(ctlr, 0);			/* all halt */
2221906943f9SDavid du Colombier 	uhcicmd(ctlr, Chcreset);			/* controller reset */
2222906943f9SDavid du Colombier 	for(i = 0; i < 100; i++){
2223906943f9SDavid du Colombier 		if((INS(Cmd) & Chcreset) == 0)
2224906943f9SDavid du Colombier 			break;
2225906943f9SDavid du Colombier 		delay(1);
2226906943f9SDavid du Colombier 	}
2227906943f9SDavid du Colombier 	if(i == 100)
2228906943f9SDavid du Colombier 		print("uhci %#x controller reset timed out\n", ctlr->port);
2229906943f9SDavid du Colombier 	OUTB(SOFmod, sof);
2230906943f9SDavid du Colombier 	iunlock(ctlr);
2231906943f9SDavid du Colombier }
2232906943f9SDavid du Colombier 
2233906943f9SDavid du Colombier static void
setdebug(Hci *,int d)2234906943f9SDavid du Colombier setdebug(Hci*, int d)
2235906943f9SDavid du Colombier {
2236906943f9SDavid du Colombier 	debug = d;
22379a747e4fSDavid du Colombier }
22389a747e4fSDavid du Colombier 
2239c8cbc0e9SDavid du Colombier static void
shutdown(Hci * hp)2240c8cbc0e9SDavid du Colombier shutdown(Hci *hp)
2241c8cbc0e9SDavid du Colombier {
2242c8cbc0e9SDavid du Colombier 	Ctlr *ctlr;
2243c8cbc0e9SDavid du Colombier 
2244c8cbc0e9SDavid du Colombier 	ctlr = hp->aux;
2245c8cbc0e9SDavid du Colombier 
2246c8cbc0e9SDavid du Colombier 	ilock(ctlr);
2247c8cbc0e9SDavid du Colombier 	uhcirun(ctlr, 0);
2248c8cbc0e9SDavid du Colombier 	delay(100);
2249c8cbc0e9SDavid du Colombier 	iunlock(ctlr);
2250c8cbc0e9SDavid du Colombier }
2251c8cbc0e9SDavid du Colombier 
22529a747e4fSDavid du Colombier static int
reset(Hci * hp)2253906943f9SDavid du Colombier reset(Hci *hp)
22549a747e4fSDavid du Colombier {
2255906943f9SDavid du Colombier 	static Lock resetlck;
22569a747e4fSDavid du Colombier 	int i;
22579a747e4fSDavid du Colombier 	Ctlr *ctlr;
22589a747e4fSDavid du Colombier 	Pcidev *p;
22599a747e4fSDavid du Colombier 
2260906943f9SDavid du Colombier 	if(getconf("*nousbuhci"))
2261906943f9SDavid du Colombier 		return -1;
2262906943f9SDavid du Colombier 
2263906943f9SDavid du Colombier 	ilock(&resetlck);
22649a747e4fSDavid du Colombier 	scanpci();
22659a747e4fSDavid du Colombier 
22669a747e4fSDavid du Colombier 	/*
2267906943f9SDavid du Colombier 	 * Any adapter matches if no hp->port is supplied,
22689a747e4fSDavid du Colombier 	 * otherwise the ports must match.
22699a747e4fSDavid du Colombier 	 */
2270906943f9SDavid du Colombier 	ctlr = nil;
2271906943f9SDavid du Colombier 	for(i = 0; i < Nhcis && ctlrs[i] != nil; i++){
2272906943f9SDavid du Colombier 		ctlr = ctlrs[i];
2273906943f9SDavid du Colombier 		if(ctlr->active == 0)
2274906943f9SDavid du Colombier 		if(hp->port == 0 || hp->port == ctlr->port){
22759a747e4fSDavid du Colombier 			ctlr->active = 1;
22769a747e4fSDavid du Colombier 			break;
22779a747e4fSDavid du Colombier 		}
22789a747e4fSDavid du Colombier 	}
2279906943f9SDavid du Colombier 	iunlock(&resetlck);
2280906943f9SDavid du Colombier 	if(ctlrs[i] == nil || i == Nhcis)
22819a747e4fSDavid du Colombier 		return -1;
22829a747e4fSDavid du Colombier 
22839a747e4fSDavid du Colombier 	p = ctlr->pcidev;
2284906943f9SDavid du Colombier 	hp->aux = ctlr;
2285906943f9SDavid du Colombier 	hp->port = ctlr->port;
2286906943f9SDavid du Colombier 	hp->irq = p->intl;
2287906943f9SDavid du Colombier 	hp->tbdf = p->tbdf;
2288906943f9SDavid du Colombier 	hp->nports = 2;			/* default */
22899a747e4fSDavid du Colombier 
2290906943f9SDavid du Colombier 	uhcireset(ctlr);
2291906943f9SDavid du Colombier 	uhcimeminit(ctlr);
22929a747e4fSDavid du Colombier 
22939a747e4fSDavid du Colombier 	/*
2294906943f9SDavid du Colombier 	 * Linkage to the generic HCI driver.
22959a747e4fSDavid du Colombier 	 */
2296906943f9SDavid du Colombier 	hp->init = init;
2297906943f9SDavid du Colombier 	hp->dump = dump;
2298906943f9SDavid du Colombier 	hp->interrupt = interrupt;
2299906943f9SDavid du Colombier 	hp->epopen = epopen;
2300906943f9SDavid du Colombier 	hp->epclose = epclose;
2301906943f9SDavid du Colombier 	hp->epread = epread;
2302906943f9SDavid du Colombier 	hp->epwrite = epwrite;
2303906943f9SDavid du Colombier 	hp->seprintep = seprintep;
2304906943f9SDavid du Colombier 	hp->portenable = portenable;
2305906943f9SDavid du Colombier 	hp->portreset = portreset;
2306906943f9SDavid du Colombier 	hp->portstatus = portstatus;
2307c8cbc0e9SDavid du Colombier 	hp->shutdown = shutdown;
2308906943f9SDavid du Colombier 	hp->debug = setdebug;
2309906943f9SDavid du Colombier 	hp->type = "uhci";
23109a747e4fSDavid du Colombier 	return 0;
23119a747e4fSDavid du Colombier }
23129a747e4fSDavid du Colombier 
23139a747e4fSDavid du Colombier void
usbuhcilink(void)23149a747e4fSDavid du Colombier usbuhcilink(void)
23159a747e4fSDavid du Colombier {
2316906943f9SDavid du Colombier 	addhcitype("uhci", reset);
23179a747e4fSDavid du Colombier }
2318