xref: /plan9/sys/src/9/pc/usbuhci.c (revision e06f534bbaa4097bc6f4764ef1dd2dc3338fbd40)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"io.h"
7 #include	"../port/error.h"
8 
9 #include	"usb.h"
10 
11 #define XPRINT if(debug)print
12 #define XXPRINT if(0)print
13 
14 static int Chatty = 0;
15 static int debug = 0;
16 
17 static	char	Estalled[] = "usb endpoint stalled";
18 
19 /*
20  * UHCI interface registers and bits
21  */
22 enum
23 {
24 	/* i/o space */
25 	Cmd = 0,
26 	Status = 2,
27 	Usbintr = 4,
28 	Frnum = 6,
29 	Flbaseadd = 8,
30 	SOFMod = 0xC,
31 	Portsc0 = 0x10,
32 	Portsc1 = 0x12,
33 
34 	/* port status */
35 	Suspend =	1<<12,
36 	PortReset =	1<<9,
37 	SlowDevice =	1<<8,
38 	ResumeDetect =	1<<6,
39 	PortChange =	1<<3,	/* write 1 to clear */
40 	PortEnable =	1<<2,
41 	StatusChange =	1<<1,	/* write 1 to clear */
42 	DevicePresent =	1<<0,
43 
44 	NFRAME = 	1024,
45 	FRAMESIZE=	NFRAME*sizeof(ulong),	/* fixed by hardware; aligned to same */
46 
47 	Vf =		1<<2,	/* TD only */
48 	IsQH =		1<<1,
49 	Terminate =	1<<0,
50 
51 	/* TD.status */
52 	SPD =		1<<29,
53 	ErrLimit0 =	0<<27,
54 	ErrLimit1 =	1<<27,
55 	ErrLimit2 =	2<<27,
56 	ErrLimit3 =	3<<27,
57 	LowSpeed =	1<<26,
58 	IsoSelect =	1<<25,
59 	IOC =		1<<24,
60 	Active =	1<<23,
61 	Stalled =	1<<22,
62 	DataBufferErr =	1<<21,
63 	Babbling =	1<<20,
64 	NAKed =		1<<19,
65 	CRCorTimeout =	1<<18,
66 	BitstuffErr =	1<<17,
67 	AnyError = (Stalled | DataBufferErr | Babbling | NAKed | CRCorTimeout | BitstuffErr),
68 
69 	/* TD.dev */
70 	IsDATA1 =	1<<19,
71 
72 	/* TD.flags (software) */
73 	CancelTD=	1<<0,
74 	IsoClean=	1<<2,
75 };
76 
77 static struct
78 {
79 	int	bit;
80 	char	*name;
81 }
82 portstatus[] =
83 {
84 	{ Suspend,		"suspend", },
85 	{ PortReset,		"reset", },
86 	{ SlowDevice,		"lowspeed", },
87 	{ ResumeDetect,		"resume", },
88 	{ PortChange,		"portchange", },
89 	{ PortEnable,		"enable", },
90 	{ StatusChange,		"statuschange", },
91 	{ DevicePresent,	"present", },
92 };
93 
94 typedef struct Ctlr Ctlr;
95 typedef struct Endptx Endptx;
96 typedef struct QH QH;
97 typedef struct TD TD;
98 
99 /*
100  * software structures
101  */
102 struct Ctlr
103 {
104 	Lock;	/* protects state shared with interrupt (eg, free list) */
105 	Ctlr*	next;
106 	Pcidev*	pcidev;
107 	int	active;
108 
109 	int	io;
110 	ulong*	frames;	/* frame list */
111 	ulong*	frameld;	/* real time load on each of the frame list entries */
112 	QLock	resetl;	/* lock controller during USB reset */
113 
114 	TD*	tdpool;
115 	TD*	freetd;
116 	QH*	qhpool;
117 	QH*	freeqh;
118 
119 	QH*	ctlq;	/* queue for control i/o */
120 	QH*	bwsop;	/* empty bandwidth sop (to PIIX4 errata specifications) */
121 	QH*	bulkq;	/* queue for bulk i/o (points back to bandwidth sop) */
122 	QH*	recvq;	/* receive queues for bulk i/o */
123 
124 	Udev*	ports[2];
125 
126 	struct {
127 		Lock;
128 		Endpt*	f;
129 	} activends;
130 
131 	long	usbints;	/* debugging */
132 	long	framenumber;
133 	long	frameptr;
134 	long	usbbogus;
135 };
136 
137 #define	IN(x)		ins(ctlr->io+(x))
138 #define	OUT(x, v)	outs(ctlr->io+(x), (v))
139 
140 static Ctlr* ctlrhead;
141 static Ctlr* ctlrtail;
142 
143 struct Endptx
144 {
145 	QH*	epq;		/* queue of TDs for this endpoint */
146 
147 	/* ISO related: */
148 	void*	tdalloc;
149 	void*	bpalloc;
150 	uchar*	bp0;		/* first block in array */
151 	TD*	td0;		/* first td in array */
152 	TD*	etd;		/* pointer into circular list of TDs for isochronous ept */
153 	TD*	xtd;		/* next td to be cleaned */
154 };
155 
156 /*
157  * UHCI hardware structures, aligned on 16-byte boundary
158  */
159 struct TD
160 {
161 	ulong	link;
162 	ulong	status;	/* controller r/w */
163 	ulong	dev;
164 	ulong	buffer;
165 
166 	/* software */
167 	ulong	flags;
168 	union{
169 		Block*	bp;	/* non-iso */
170 		ulong	offset;	/* iso */
171 	};
172 	Endpt*	ep;
173 	TD*	next;
174 };
175 #define	TFOL(p)	((TD*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW)))
176 
177 struct QH
178 {
179 	ulong	head;
180 	ulong	entries;	/* address of next TD or QH to process (updated by controller) */
181 
182 	/* software */
183 	QH*	hlink;
184 	TD*	first;
185 	QH*	next;		/* free list */
186 	TD*	last;
187 	ulong	_d1;		/* fillers */
188 	ulong	_d2;
189 };
190 #define	QFOL(p)	((QH*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW)))
191 
192 static TD *
193 alloctd(Ctlr *ctlr)
194 {
195 	TD *t;
196 
197 	ilock(ctlr);
198 	t = ctlr->freetd;
199 	if(t == nil)
200 		panic("alloctd");	/* TO DO */
201 	ctlr->freetd = t->next;
202 	t->next = nil;
203 	iunlock(ctlr);
204 	t->ep = nil;
205 	t->bp = nil;
206 	t->status = 0;
207 	t->link = Terminate;
208 	t->buffer = 0;
209 	t->flags = 0;
210 	return t;
211 }
212 
213 static void
214 freetd(Ctlr *ctlr, TD *t)
215 {
216 	t->ep = nil;
217 	if(t->bp)
218 		freeb(t->bp);
219 	t->bp = nil;
220 	ilock(ctlr);
221 	t->buffer = 0xdeadbeef;
222 	t->next = ctlr->freetd;
223 	ctlr->freetd = t;
224 	iunlock(ctlr);
225 }
226 
227 static void
228 dumpdata(Block *b, int n)
229 {
230 	int i;
231 
232 	XPRINT("\tb %8.8lux[%d]: ", (ulong)b->rp, n);
233 	if(n > 16)
234 		n = 16;
235 	for(i=0; i<n; i++)
236 		XPRINT(" %2.2ux", b->rp[i]);
237 	XPRINT("\n");
238 }
239 
240 static void
241 dumptd(TD *t, int follow)
242 {
243 	int i, n;
244 	char buf[20], *s;
245 	TD *t0;
246 
247 	t0 = t;
248 	while(t){
249 		i = t->dev & 0xFF;
250 		if(i == TokOUT || i == TokSETUP)
251 			n = ((t->dev>>21) + 1) & 0x7FF;
252 		else if((t->status & Active) == 0)
253 			n = (t->status + 1) & 0x7FF;
254 		else
255 			n = 0;
256 		s = buf;
257 		if(t->status & Active)
258 			*s++ = 'A';
259 		if(t->status & Stalled)
260 			*s++ = 'S';
261 		if(t->status & DataBufferErr)
262 			*s++ = 'D';
263 		if(t->status & Babbling)
264 			*s++ = 'B';
265 		if(t->status & NAKed)
266 			*s++ = 'N';
267 		if(t->status & CRCorTimeout)
268 			*s++ = 'T';
269 		if(t->status & BitstuffErr)
270 			*s++ = 'b';
271 		if(t->status & LowSpeed)
272 			*s++ = 'L';
273 		*s = 0;
274 		XPRINT("td %8.8lux: ", t);
275 		XPRINT("l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n",
276 			t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags);
277 		XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n",
278 			buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1);
279 		if(debug && t->bp && (t->flags & CancelTD) == 0)
280 			dumpdata(t->bp, n);
281 		if(!follow || t->link & Terminate || t->link & IsQH)
282 			break;
283 		t = TFOL(t->link);
284 		if(t == t0)
285 			break;	/* looped */
286 	}
287 }
288 
289 static TD *
290 alloctde(Ctlr *ctlr, Endpt *e, int pid, int n)
291 {
292 	TD *t;
293 	int tog, id;
294 
295 	t = alloctd(ctlr);
296 	id = (e->x<<7)|(e->dev->x&0x7F);
297 	tog = 0;
298 	if((pid == TokOUT && e->wdata01) || (pid == TokIN && e->rdata01))
299 		tog = IsDATA1;
300 	t->ep = e;
301 	t->status = ErrLimit3 | Active | IOC;	/* or put IOC only on last? */
302 	if(e->dev->ls)
303 		t->status |= LowSpeed;
304 	t->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid | tog;
305 	return t;
306 }
307 
308 static QH *
309 allocqh(Ctlr *ctlr)
310 {
311 	QH *qh;
312 
313 	ilock(ctlr);
314 	qh = ctlr->freeqh;
315 	if(qh == nil)
316 		panic("allocqh");	/* TO DO */
317 	ctlr->freeqh = qh->next;
318 	qh->next = nil;
319 	iunlock(ctlr);
320 	qh->head = Terminate;
321 	qh->entries = Terminate;
322 	qh->hlink = nil;
323 	qh->first = nil;
324 	qh->last = nil;
325 	return qh;
326 }
327 
328 static void
329 freeqh(Ctlr *ctlr, QH *qh)
330 {
331 	ilock(ctlr);
332 	qh->next = ctlr->freeqh;
333 	ctlr->freeqh = qh;
334 	iunlock(ctlr);
335 }
336 
337 static void
338 dumpqh(QH *q)
339 {
340 	int i;
341 	QH *q0;
342 
343 	q0 = q;
344 	for(i = 0; q != nil && i < 10; i++){
345 		XPRINT("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries);
346 		if((q->entries & (IsQH|Terminate)) == 0)
347 			dumptd(TFOL(q->entries), 1);
348 		if(q->head & Terminate)
349 			break;
350 		if((q->head & IsQH) == 0){
351 			XPRINT("head:");
352 			dumptd(TFOL(q->head), 1);
353 			break;
354 		}
355 		q = QFOL(q->head);
356 		if(q == q0)
357 			break;	/* looped */
358 	}
359 }
360 
361 static void
362 queuetd(Ctlr *ctlr, QH *q, TD *t, int vf, char *why)
363 {
364 	TD *lt;
365 
366 	for(lt = t; lt->next != nil; lt = lt->next)
367 		lt->link = PCIWADDR(lt->next) | vf;
368 	lt->link = Terminate;
369 	ilock(ctlr);
370 	XPRINT("queuetd %s: t=%p lt=%p q=%p first=%p last=%p entries=%.8lux\n",
371 		why, t, lt, q, q->first, q->last, q->entries);
372 	if(q->first != nil){
373 		q->last->link = PCIWADDR(t) | vf;
374 		q->last->next = t;
375 	}else{
376 		q->first = t;
377 		q->entries = PCIWADDR(t);
378 	}
379 	q->last = lt;
380 	XPRINT("	t=%p q=%p first=%p last=%p entries=%.8lux\n",
381 		t, q, q->first, q->last, q->entries);
382 	dumpqh(q);
383 	iunlock(ctlr);
384 }
385 
386 static void
387 cleantd(Ctlr *ctlr, TD *t, int discard)
388 {
389 	Block *b;
390 	int n, err;
391 
392 	XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
393 	if(t->ep != nil && t->ep->debug)
394 		dumptd(t, 0);
395 	if(t->status & Active)
396 		panic("cleantd Active");
397 	err = t->status & (AnyError&~NAKed);
398 	/* TO DO: on t->status&AnyError, q->entries will not have advanced */
399 	if (err) {
400 		XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
401 //		print("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
402 	}
403 	switch(t->dev&0xFF){
404 	case TokIN:
405 		if(discard || (t->flags & CancelTD) || t->ep == nil || t->ep->x!=0&&err){
406 			if(t->ep != nil){
407 				if(err != 0)
408 					t->ep->err = err==Stalled? Estalled: Eio;
409 				wakeup(&t->ep->rr);	/* in case anyone cares */
410 			}
411 			break;
412 		}
413 		b = t->bp;
414 		n = (t->status + 1) & 0x7FF;
415 		if(n > b->lim - b->wp)
416 			n = 0;
417 		b->wp += n;
418 		if(Chatty)
419 			dumpdata(b, n);
420 		t->bp = nil;
421 		t->ep->nbytes += n;
422 		t->ep->nblocks++;
423 		qpass(t->ep->rq, b);	/* TO DO: flow control */
424 		wakeup(&t->ep->rr);	/* TO DO */
425 		break;
426 	case TokSETUP:
427 		XPRINT("cleanTD: TokSETUP %lux\n", &t->ep);
428 		/* don't really need to wakeup: subsequent IN or OUT gives status */
429 		if(t->ep != nil) {
430 			wakeup(&t->ep->wr);	/* TO DO */
431 			XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr);
432 		}
433 		break;
434 	case TokOUT:
435 		/* TO DO: mark it done somewhere */
436 		XPRINT("cleanTD: TokOut %lux\n", &t->ep);
437 		if(t->ep != nil){
438 			if(t->bp){
439 				n = BLEN(t->bp);
440 				t->ep->nbytes += n;
441 				t->ep->nblocks++;
442 			}
443 			if(t->ep->x!=0 && err != 0)
444 				t->ep->err = err==Stalled? Estalled: Eio;
445 			if(--t->ep->ntd < 0)
446 				panic("cleantd ntd");
447 			wakeup(&t->ep->wr);	/* TO DO */
448 			XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr);
449 		}
450 		break;
451 	}
452 	freetd(ctlr, t);
453 }
454 
455 static void
456 cleanq(Ctlr *ctlr, QH *q, int discard, int vf)
457 {
458 	TD *t, *tp;
459 
460 	ilock(ctlr);
461 	tp = nil;
462 	for(t = q->first; t != nil;){
463 		XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer, t->flags, t->next);
464 		if(t->status & Active){
465 			if(t->status & NAKed){
466 				t->status = (t->status & ~NAKed) | IOC;	/* ensure interrupt next frame */
467 				tp = t;
468 				t = t->next;
469 				continue;
470 			}
471 			if(t->flags & CancelTD){
472 				XPRINT("cancelTD: %8.8lux\n", (ulong)t);
473 				t->status = (t->status & ~Active) | IOC;	/* ensure interrupt next frame */
474 				tp = t;
475 				t = t->next;
476 				continue;
477 			}
478 			tp = t;
479 			t = t->next;
480 			continue;
481 		}
482 		t->status &= ~IOC;
483 		if (tp == nil) {
484 			q->first = t->next;
485 			if(q->first != nil)
486 				q->entries = PCIWADDR(q->first);
487 			else
488 				q->entries = Terminate;
489 		} else {
490 			tp->next = t->next;
491 			if (t->next != nil)
492 				tp->link = PCIWADDR(t->next) | vf;
493 			else
494 				tp->link = Terminate;
495 		}
496 		if (q->last == t)
497 			q->last = tp;
498 		iunlock(ctlr);
499 		cleantd(ctlr, t, discard);
500 		ilock(ctlr);
501 		if (tp)
502 			t = tp->next;
503 		else
504 			t = q->first;
505 		XPRINT("t = %8.8lux\n", t);
506 		dumpqh(q);
507 	}
508 	if(q->first && q->entries != PCIWADDR(q->first)){
509 		ctlr->usbbogus++;
510 		q->entries = PCIWADDR(q->first);
511 	}
512 	iunlock(ctlr);
513 }
514 
515 static void
516 canceltds(Ctlr *ctlr, QH *q, Endpt *e)
517 {
518 	TD *t;
519 
520 	if(q != nil){
521 		ilock(ctlr);
522 		for(t = q->first; t != nil; t = t->next)
523 			if(t->ep == e)
524 				t->flags |= CancelTD;
525 		iunlock(ctlr);
526 		XPRINT("cancel:\n");
527 		dumpqh(q);
528 	}
529 }
530 
531 static void
532 eptcancel(Ctlr *ctlr, Endpt *e)
533 {
534 	Endptx *x;
535 
536 	if(e == nil)
537 		return;
538 	x = e->private;
539 	canceltds(ctlr, x->epq, e);
540 	canceltds(ctlr, ctlr->ctlq, e);
541 	canceltds(ctlr, ctlr->bulkq, e);
542 }
543 
544 static void
545 eptactivate(Ctlr *ctlr, Endpt *e)
546 {
547 	ilock(&ctlr->activends);
548 	if(e->active == 0){
549 		XPRINT("activate 0x%p\n", e);
550 		e->active = 1;
551 		e->activef = ctlr->activends.f;
552 		ctlr->activends.f = e;
553 	}
554 	iunlock(&ctlr->activends);
555 }
556 
557 static void
558 eptdeactivate(Ctlr *ctlr, Endpt *e)
559 {
560 	Endpt **l;
561 
562 	/* could be O(1) but not worth it yet */
563 	ilock(&ctlr->activends);
564 	if(e->active){
565 		e->active = 0;
566 		XPRINT("deactivate 0x%p\n", e);
567 		for(l = &ctlr->activends.f; *l != e; l = &(*l)->activef)
568 			if(*l == nil){
569 				iunlock(&ctlr->activends);
570 				panic("usb eptdeactivate");
571 			}
572 		*l = e->activef;
573 	}
574 	iunlock(&ctlr->activends);
575 }
576 
577 static void
578 queueqh(Ctlr *ctlr, QH *qh)
579 {
580 	QH *q;
581 
582 	// See if it's already queued
583 	for (q = ctlr->recvq->next; q; q = q->hlink)
584 		if (q == qh)
585 			return;
586 	if ((qh->hlink = ctlr->recvq->next) == nil)
587 		qh->head = Terminate;
588 	else
589 		qh->head = PCIWADDR(ctlr->recvq->next) | IsQH;
590 	ctlr->recvq->next = qh;
591 	ctlr->recvq->entries = PCIWADDR(qh) | IsQH;
592 }
593 
594 static QH*
595 qxmit(Ctlr *ctlr, Endpt *e, Block *b, int pid)
596 {
597 	TD *t;
598 	int n, vf;
599 	QH *qh;
600 	Endptx *x;
601 
602 	x = e->private;
603 	if(b != nil){
604 		n = BLEN(b);
605 		t = alloctde(ctlr, e, pid, n);
606 		t->bp = b;
607 		t->buffer = PCIWADDR(b->rp);
608 	}else
609 		t = alloctde(ctlr, e, pid, 0);
610 	ilock(ctlr);
611 	e->ntd++;
612 	iunlock(ctlr);
613 	if(e->debug) pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0);
614 	vf = 0;
615 	if(e->x == 0){
616 		qh = ctlr->ctlq;
617 		vf = 0;
618 	}else if((qh = x->epq) == nil || e->mode != OWRITE){
619 		qh = ctlr->bulkq;
620 		vf = Vf;
621 	}
622 	queuetd(ctlr, qh, t, vf, "qxmit");
623 	return qh;
624 }
625 
626 static QH*
627 qrcv(Ctlr *ctlr, Endpt *e)
628 {
629 	TD *t;
630 	Block *b;
631 	QH *qh;
632 	int vf;
633 	Endptx *x;
634 
635 	x = e->private;
636 	t = alloctde(ctlr, e, TokIN, e->maxpkt);
637 	b = allocb(e->maxpkt);
638 	t->bp = b;
639 	t->buffer = PCIWADDR(b->wp);
640 	vf = 0;
641 	if(e->x == 0){
642 		qh = ctlr->ctlq;
643 	}else if((qh = x->epq) == nil || e->mode != OREAD){
644 		qh = ctlr->bulkq;
645 		vf = Vf;
646 	}
647 	queuetd(ctlr, qh, t, vf, "qrcv");
648 	return qh;
649 }
650 
651 static int
652 usbsched(Ctlr *ctlr, int pollms, ulong load)
653 {
654 	int i, d, q;
655 	ulong best, worst;
656 
657 	best = 1000000;
658 	q = -1;
659 	for (d = 0; d < pollms; d++){
660 		worst = 0;
661 		for (i = d; i < NFRAME; i++){
662 			if (ctlr->frameld[i] + load > worst)
663 				worst = ctlr->frameld[i] + load;
664 		}
665 		if (worst < best){
666 			best = worst;
667 			q = d;
668 		}
669 	}
670 	return q;
671 }
672 
673 static int
674 schedendpt(Ctlr *ctlr, Endpt *e)
675 {
676 	TD *td;
677 	Endptx *x;
678 	uchar *bp;
679 	int i, id, ix, size, frnum;
680 
681 	if(!e->iso || e->sched >= 0)
682 		return 0;
683 
684 	if (e->active){
685 		return -1;
686 	}
687 	e->off = 0;
688 	e->sched = usbsched(ctlr, e->pollms, e->maxpkt);
689 	if(e->sched < 0)
690 		return -1;
691 
692 	x = e->private;
693 	if (x->tdalloc || x->bpalloc)
694 		panic("usb: tdalloc/bpalloc");
695 	x->tdalloc = mallocz(0x10 + NFRAME*sizeof(TD), 1);
696 	x->bpalloc = mallocz(0x10 + e->maxpkt*NFRAME/e->pollms, 1);
697 	x->td0 = (TD*)(((ulong)x->tdalloc + 0xf) & ~0xf);
698 	x->bp0 = (uchar *)(((ulong)x->bpalloc + 0xf) & ~0xf);
699 	frnum = (IN(Frnum) + 1) & 0x3ff;
700 	frnum = (frnum & ~(e->pollms - 1)) + e->sched;
701 	x->xtd = &x->td0[(frnum+8)&0x3ff];	/* Next td to finish */
702 	x->etd = nil;
703 	e->remain = 0;
704 	e->nbytes = 0;
705 	td = x->td0;
706 	for(i = e->sched; i < NFRAME; i += e->pollms){
707 		bp = x->bp0 + e->maxpkt*i/e->pollms;
708 		td->buffer = PCIWADDR(bp);
709 		td->ep = e;
710 		td->next = &td[1];
711 		ctlr->frameld[i] += e->maxpkt;
712 		td++;
713 	}
714 	td[-1].next = x->td0;
715 	for(i = e->sched; i < NFRAME; i += e->pollms){
716 		ix = (frnum+i) & 0x3ff;
717 		td = &x->td0[ix];
718 
719 		id = (e->x<<7)|(e->dev->x&0x7F);
720 		if (e->mode == OREAD)
721 			/* enable receive on this entry */
722 			td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN;
723 		else{
724 			size = (e->hz + e->remain)*e->pollms/1000;
725 			e->remain = (e->hz + e->remain)*e->pollms%1000;
726 			size *= e->samplesz;
727 			td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | TokOUT;
728 		}
729 		td->status = ErrLimit1 | Active | IsoSelect | IOC;
730 		td->link = ctlr->frames[ix];
731 		td->flags |= IsoClean;
732 		ctlr->frames[ix] = PCIWADDR(td);
733 	}
734 	return 0;
735 }
736 
737 static void
738 unschedendpt(Ctlr *ctlr, Endpt *e)
739 {
740 	int q;
741 	TD *td;
742 	Endptx *x;
743 	ulong *addr;
744 
745 	if(!e->iso || e->sched < 0)
746 		return;
747 
748 	x = e->private;
749 	if (x->tdalloc == nil)
750 		panic("tdalloc");
751 	for (q = e->sched; q < NFRAME; q += e->pollms){
752 		td = x->td0++;
753 		addr = &ctlr->frames[q];
754 		while(*addr != PADDR(td)) {
755 			if(*addr & IsQH)
756 				panic("usb: TD expected");
757 			addr = &TFOL(*addr)->link;
758 		}
759 		*addr = td->link;
760 		ctlr->frameld[q] -= e->maxpkt;
761 	}
762 	free(x->tdalloc);
763 	free(x->bpalloc);
764 	x->tdalloc = nil;
765 	x->bpalloc = nil;
766 	x->etd = nil;
767 	x->td0 = nil;
768 	e->sched = -1;
769 }
770 
771 static void
772 epalloc(Usbhost *uh, Endpt *e)
773 {
774 	Endptx *x;
775 
776 	x = malloc(sizeof(Endptx));
777 	e->private = x;
778 	x->epq = allocqh(uh->ctlr);
779 	if(x->epq == nil)
780 		panic("devendptx");
781 }
782 
783 static void
784 epfree(Usbhost *uh, Endpt *e)
785 {
786 	Ctlr *ctlr;
787 	Endptx *x;
788 
789 	ctlr = uh->ctlr;
790 	x = e->private;
791 	if(x->epq != nil)
792 		freeqh(ctlr, x->epq);
793 }
794 
795 static void
796 epopen(Usbhost *uh, Endpt *e)
797 {
798 	Ctlr *ctlr;
799 
800 	ctlr = uh->ctlr;
801 	if(e->iso && e->active)
802 		error("already open");
803 	if(schedendpt(ctlr, e) < 0){
804 		if(e->active)
805 			error("cannot schedule USB endpoint, active");
806 		else
807 			error("cannot schedule USB endpoint");
808 	}
809 	eptactivate(ctlr, e);
810 }
811 
812 static void
813 epclose(Usbhost *uh, Endpt *e)
814 {
815 	Ctlr *ctlr;
816 
817 	ctlr = uh->ctlr;
818 	eptdeactivate(ctlr, e);
819 	unschedendpt(ctlr, e);
820 }
821 
822 static void
823 epmode(Usbhost *uh, Endpt *e)
824 {
825 	Ctlr *ctlr;
826 	Endptx *x;
827 
828 	ctlr = uh->ctlr;
829 	x = e->private;
830 	if(e->iso) {
831 		if(x->epq != nil) {
832 			freeqh(ctlr, x->epq);
833 			x->epq = nil;
834 		}
835 	}
836 	else {
837 		/* Each bulk device gets a queue head hanging off the
838 		 * bulk queue head
839 		 */
840 		if(x->epq == nil) {
841 			x->epq = allocqh(ctlr);
842 			if(x->epq == nil)
843 				panic("epbulk: allocqh");
844 		}
845 		queueqh(ctlr, x->epq);
846 	}
847 }
848 
849 static	int	ioport[] = {-1, Portsc0, Portsc1};
850 
851 static void
852 portreset(Usbhost *uh, int port)
853 {
854 	int i, p;
855 	Ctlr *ctlr;
856 
857 	ctlr = uh->ctlr;
858 	if(port != 1 && port != 2)
859 		error(Ebadarg);
860 
861 	/* should check that device not being configured on other port? */
862 	p = ioport[port];
863 	qlock(&ctlr->resetl);
864 	if(waserror()){
865 		qunlock(&ctlr->resetl);
866 		nexterror();
867 	}
868 	XPRINT("r: %x\n", IN(p));
869 	ilock(ctlr);
870 	OUT(p, PortReset);
871 	delay(12);	/* BUG */
872 	XPRINT("r2: %x\n", IN(p));
873 	OUT(p, IN(p) & ~PortReset);
874 	XPRINT("r3: %x\n", IN(p));
875 	OUT(p, IN(p) | PortEnable);
876 	microdelay(64);
877 	for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++)
878 		;
879 	XPRINT("r': %x %d\n", IN(p), i);
880 	OUT(p, (IN(p) & ~PortReset)|PortEnable);
881 	iunlock(ctlr);
882 	poperror();
883 	qunlock(&ctlr->resetl);
884 }
885 
886 static void
887 portenable(Usbhost *uh, int port, int on)
888 {
889 	int w, p;
890 	Ctlr *ctlr;
891 
892 	ctlr = uh->ctlr;
893 	if(port != 1 && port != 2)
894 		error(Ebadarg);
895 
896 	/* should check that device not being configured on other port? */
897 	p = ioport[port];
898 	qlock(&ctlr->resetl);
899 	if(waserror()){
900 		qunlock(&ctlr->resetl);
901 		nexterror();
902 	}
903 	ilock(ctlr);
904 	w = IN(p);
905 	if(on)
906 		w |= PortEnable;
907 	else
908 		w &= ~PortEnable;
909 	OUT(p, w);
910 	microdelay(64);
911 	iunlock(ctlr);
912 	XPRINT("e: %x\n", IN(p));
913 	poperror();
914 	qunlock(&ctlr->resetl);
915 }
916 
917 static void
918 portinfo(Usbhost *uh, char *s, char *se)
919 {
920 	int x, i, j;
921 	Ctlr *ctlr;
922 
923 	ctlr = uh->ctlr;
924 	for(i = 1; i <= 2; i++) {
925 		ilock(ctlr);
926 		x = IN(ioport[i]);
927 		if((x & (PortChange|StatusChange)) != 0)
928 			OUT(ioport[i], x);
929 		iunlock(ctlr);
930 		s = seprint(s, se, "%d %ux", i, x);
931 		for(j = 0; j < nelem(portstatus); j++) {
932 			if((x & portstatus[j].bit) != 0)
933 				s = seprint(s, se, " %s", portstatus[j].name);
934 		}
935 		s = seprint(s, se, "\n");
936 	}
937 }
938 
939 static void
940 cleaniso(Endpt *e, int frnum)
941 {
942 	TD *td;
943 	int id, n, i;
944 	Endptx *x;
945 	uchar *bp;
946 
947 	x = e->private;
948 	td = x->xtd;
949 	if (td->status & Active)
950 		return;
951 	id = (e->x<<7)|(e->dev->x&0x7F);
952 	do {
953 		if (td->status & AnyError)
954 			XPRINT("usbisoerror 0x%lux\n", td->status);
955 		n = (td->status + 1) & 0x3ff;
956 		e->nbytes += n;
957 		if ((td->flags & IsoClean) == 0)
958 			e->nblocks++;
959 		if (e->mode == OREAD){
960 			e->buffered += n;
961 			e->poffset += (td->status + 1) & 0x3ff;
962 			td->offset = e->poffset;
963 			td->dev = ((e->maxpkt -1)<<21) | ((id&0x7FF)<<8) | TokIN;
964 			e->toffset = td->offset;
965 		}else{
966 			if ((td->flags & IsoClean) == 0){
967 				e->buffered -= n;
968 				if (e->buffered < 0){
969 //					print("e->buffered %d?\n", e->buffered);
970 					e->buffered = 0;
971 				}
972 			}
973 			e->toffset = td->offset;
974 			n = (e->hz + e->remain)*e->pollms/1000;
975 			e->remain = (e->hz + e->remain)*e->pollms%1000;
976 			n *= e->samplesz;
977 			td->dev = ((n -1)<<21) | ((id&0x7FF)<<8) | TokOUT;
978 			td->offset = e->poffset;
979 			e->poffset += n;
980 		}
981 		td = td->next;
982 		if (x->xtd == td){
983 			XPRINT("@");
984 			break;
985 		}
986 	} while ((td->status & Active) == 0);
987 	e->time = todget(nil);
988 	x->xtd = td;
989 	for (n = 2; n < 4; n++){
990 		i = ((frnum + n)&0x3ff);
991 		td = x->td0 + i;
992 		bp = x->bp0 + e->maxpkt*i/e->pollms;
993 		if (td->status & Active)
994 			continue;
995 
996 		if (e->mode == OWRITE){
997 			if (td == x->etd) {
998 				XPRINT("*");
999 				memset(bp+e->off, 0, e->maxpkt-e->off);
1000 				if (e->off == 0)
1001 					td->flags |= IsoClean;
1002 				else
1003 					e->buffered += (((td->dev>>21) +1) & 0x3ff) - e->off;
1004 				x->etd = nil;
1005 			}else if ((td->flags & IsoClean) == 0){
1006 				XPRINT("-");
1007 				memset(bp, 0, e->maxpkt);
1008 				td->flags |= IsoClean;
1009 			}
1010 		} else {
1011 			/* Unread bytes are now lost */
1012 			e->buffered -= (td->status + 1) & 0x3ff;
1013 		}
1014 		td->status = ErrLimit1 | Active | IsoSelect | IOC;
1015 	}
1016 	wakeup(&e->wr);
1017 }
1018 
1019 static void
1020 interrupt(Ureg*, void *a)
1021 {
1022 	QH *q;
1023 	Ctlr *ctlr;
1024 	Endpt *e;
1025 	Endptx *x;
1026 	int s, frnum;
1027 	Usbhost *uh;
1028 
1029 	uh = a;
1030 	ctlr = uh->ctlr;
1031 	s = IN(Status);
1032 	ctlr->frameptr = inl(ctlr->io+Flbaseadd);
1033 	ctlr->framenumber = IN(Frnum) & 0x3ff;
1034 	OUT(Status, s);
1035 	if ((s & 0x1f) == 0)
1036 		return;
1037 	ctlr->usbints++;
1038 	frnum = IN(Frnum) & 0x3ff;
1039 	if (s & 0x1a) {
1040 		XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ctlr->io+SOFMod));
1041 		XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1));
1042 	}
1043 
1044 	ilock(&ctlr->activends);
1045 	for(e = ctlr->activends.f; e != nil; e = e->activef) {
1046 		x = e->private;
1047 		if(!e->iso && x->epq != nil) {
1048 			XXPRINT("cleanq(ctlr, x->epq, 0, 0)\n");
1049 			cleanq(ctlr, x->epq, 0, 0);
1050 		}
1051 		if(e->iso) {
1052 			XXPRINT("cleaniso(e)\n");
1053 			cleaniso(e, frnum);
1054 		}
1055 	}
1056 	iunlock(&ctlr->activends);
1057 	XXPRINT("cleanq(ctlr, ctlr->ctlq, 0, 0)\n");
1058 	cleanq(ctlr, ctlr->ctlq, 0, 0);
1059 	XXPRINT("cleanq(ctlr, ctlr->bulkq, 0, Vf)\n");
1060 	cleanq(ctlr, ctlr->bulkq, 0, Vf);
1061 	XXPRINT("clean recvq\n");
1062 	for (q = ctlr->recvq->next; q; q = q->hlink) {
1063 		XXPRINT("cleanq(ctlr, q, 0, Vf)\n");
1064 		cleanq(ctlr, q, 0, Vf);
1065 	}
1066 }
1067 
1068 static int
1069 eptinput(void *arg)
1070 {
1071 	Endpt *e;
1072 
1073 	e = arg;
1074 	return e->eof || e->err || qcanread(e->rq);
1075 }
1076 
1077 static int
1078 isoready(void *a)
1079 {
1080 	Endptx *x;
1081 	TD *etd;
1082 
1083 	x = a;
1084 	return (etd = x->etd) == nil || (etd != x->xtd && (etd->status & Active) == 0);
1085 }
1086 
1087 static long
1088 isoio(Ctlr *ctlr, Endpt *e, void *a, long n, ulong offset, int w)
1089 {
1090 	TD *td;
1091 	Endptx *x;
1092 	int i, frnum;
1093 	uchar *p, *q, *bp;
1094 	volatile int isolock;
1095 
1096 	x = e->private;
1097 	qlock(&e->rlock);
1098 	isolock = 0;
1099 	if(waserror()){
1100 		if (isolock){
1101 			isolock = 0;
1102 			iunlock(&ctlr->activends);
1103 		}
1104 		qunlock(&e->rlock);
1105 		eptcancel(ctlr, e);
1106 		nexterror();
1107 	}
1108 	p = a;
1109 	if (offset != 0 && offset != e->foffset){
1110 		iprint("offset %lud, foffset %lud\n", offset, e->foffset);
1111 		/* Seek to a specific position */
1112 		frnum = (IN(Frnum) + 8) & 0x3ff;
1113 		td = x->td0 +frnum;
1114 		if (offset < td->offset)
1115 			error("ancient history");
1116 		while (offset > e->toffset){
1117 			tsleep(&e->wr, return0, 0, 500);
1118 		}
1119 		while (offset >= td->offset + ((w?(td->dev >> 21):td->status) + 1) & 0x7ff){
1120 			td = td->next;
1121 			if (td == x->xtd)
1122 				iprint("trouble\n");
1123 		}
1124 		ilock(&ctlr->activends);
1125 		isolock = 1;
1126 		e->off = td->offset - offset;
1127 		if (e->off >= e->maxpkt){
1128 			iprint("I can't program: %d\n", e->off);
1129 			e->off = 0;
1130 		}
1131 		x->etd = td;
1132 		e->foffset = offset;
1133 	}
1134 	do {
1135 		if (isolock == 0){
1136 			ilock(&ctlr->activends);
1137 			isolock = 1;
1138 		}
1139 		td = x->etd;
1140 		if (td == nil || e->off == 0){
1141 			if (td == nil){
1142 				XPRINT("0");
1143 				if (w){
1144 					frnum = (IN(Frnum) + 1) & 0x3ff;
1145 					td = x->td0 + frnum;
1146 					while(td->status & Active)
1147 						td = td->next;
1148 				}else{
1149 					frnum = (IN(Frnum) - 4) & 0x3ff;
1150 					td = x->td0 + frnum;
1151 					while(td->next != x->xtd)
1152 						td = td->next;
1153 				}
1154 				x->etd = td;
1155 				e->off = 0;
1156 			}else{
1157 				/* New td, make sure it's ready */
1158 				while (isoready(x) == 0){
1159 					isolock = 0;
1160 					iunlock(&ctlr->activends);
1161 					sleep(&e->wr, isoready, x);
1162 					ilock(&ctlr->activends);
1163 					isolock = 1;
1164 				}
1165 				if (x->etd == nil){
1166 					XPRINT("!");
1167 					continue;
1168 				}
1169 			}
1170 			if (w)
1171 				e->psize = ((td->dev >> 21) + 1) & 0x7ff;
1172 			else
1173 				e->psize = (x->etd->status + 1) & 0x7ff;
1174 			if(e->psize > e->maxpkt)
1175 				panic("packet size > maximum");
1176 		}
1177 		if((i = n) >= e->psize)
1178 			i = e->psize;
1179 		if (w)
1180 			e->buffered += i;
1181 		else{
1182 			e->buffered -= i;
1183 			if (e->buffered < 0)
1184 				e->buffered = 0;
1185 		}
1186 		isolock = 0;
1187 		iunlock(&ctlr->activends);
1188 		td->flags &= ~IsoClean;
1189 		bp = x->bp0 + (td - x->td0) * e->maxpkt / e->pollms;
1190 		q = bp + e->off;
1191 		if (w){
1192 			memmove(q, p, i);
1193 		}else{
1194 			memmove(p, q, i);
1195 		}
1196 		p += i;
1197 		n -= i;
1198 		e->off += i;
1199 		e->psize -= i;
1200 		if (e->psize){
1201 			if (n != 0)
1202 				panic("usb iso: can't happen");
1203 			break;
1204 		}
1205 		if(w)
1206 			td->offset = offset + (p-(uchar*)a) - (((td->dev >> 21) + 1) & 0x7ff);
1207 		td->status = ErrLimit3 | Active | IsoSelect | IOC;
1208 		x->etd = td->next;
1209 		e->off = 0;
1210 	} while(n > 0);
1211 	n = p-(uchar*)a;
1212 	e->foffset += n;
1213 	poperror();
1214 	if (isolock)
1215 		iunlock(&ctlr->activends);
1216 	qunlock(&e->rlock);
1217 	return n;
1218 }
1219 
1220 static long
1221 read(Usbhost *uh, Endpt *e, void *a, long n, vlong offset)
1222 {
1223 	long l, i;
1224 	Block *b;
1225 	Ctlr *ctlr;
1226 	uchar *p;
1227 
1228 	ctlr = uh->ctlr;
1229 	if(e->iso)
1230 		return isoio(ctlr, e, a, n, (ulong)offset, 0);
1231 
1232 	XPRINT("qlock(%p)\n", &e->rlock);
1233 	qlock(&e->rlock);
1234 	XPRINT("got qlock(%p)\n", &e->rlock);
1235 	if(waserror()){
1236 		qunlock(&e->rlock);
1237 		eptcancel(ctlr, e);
1238 		nexterror();
1239 	}
1240 	p = a;
1241 	do {
1242 		if(e->eof) {
1243 			XPRINT("e->eof\n");
1244 			break;
1245 		}
1246 		if(e->err)
1247 			error(e->err);
1248 		qrcv(ctlr, e);
1249 		if(!e->iso)
1250 			e->rdata01 ^= 1;
1251 		sleep(&e->rr, eptinput, e);
1252 		if(e->err)
1253 			error(e->err);
1254 		b = qget(e->rq);	/* TO DO */
1255 		if(b == nil) {
1256 			XPRINT("b == nil\n");
1257 			break;
1258 		}
1259 		if(waserror()){
1260 			freeb(b);
1261 			nexterror();
1262 		}
1263 		l = BLEN(b);
1264 		if((i = l) > n)
1265 			i = n;
1266 		if(i > 0){
1267 			memmove(p, b->rp, i);
1268 			p += i;
1269 		}
1270 		poperror();
1271 		freeb(b);
1272 		n -= i;
1273 		if (l != e->maxpkt)
1274 			break;
1275 	} while (n > 0);
1276 	poperror();
1277 	qunlock(&e->rlock);
1278 	return p-(uchar*)a;
1279 }
1280 
1281 static int
1282 qisempty(void *arg)
1283 {
1284 	return ((QH*)arg)->entries & Terminate;
1285 }
1286 
1287 static long
1288 write(Usbhost *uh, Endpt *e, void *a, long n, vlong offset, int tok)
1289 {
1290 	int i, j;
1291 	QH *qh;
1292 	Block *b;
1293 	Ctlr *ctlr;
1294 	uchar *p;
1295 
1296 	ctlr = uh->ctlr;
1297 	if(e->iso)
1298 		return isoio(ctlr, e, a, n, (ulong)offset, 1);
1299 
1300 	p = a;
1301 	qlock(&e->wlock);
1302 	if(waserror()){
1303 		qunlock(&e->wlock);
1304 		eptcancel(ctlr, e);
1305 		nexterror();
1306 	}
1307 	do {
1308 		if(e->err)
1309 			error(e->err);
1310 		if((i = n) >= e->maxpkt)
1311 			i = e->maxpkt;
1312 		b = allocb(i);
1313 		if(waserror()){
1314 			freeb(b);
1315 			nexterror();
1316 		}
1317 		XPRINT("out [%d]", i);
1318 		for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]);
1319 		XPRINT("\n");
1320 		memmove(b->wp, p, i);
1321 		b->wp += i;
1322 		p += i;
1323 		n -= i;
1324 		poperror();
1325 		qh = qxmit(ctlr, e, b, tok);
1326 		tok = TokOUT;
1327 		e->wdata01 ^= 1;
1328 		if(e->ntd >= e->nbuf) {
1329 XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n",
1330  "writeusb sleep", qh, qh->first, qh->last, qh->entries);
1331 			XPRINT("write: sleep %lux\n", &e->wr);
1332 			sleep(&e->wr, qisempty, qh);
1333 			XPRINT("write: awake\n");
1334 		}
1335 	} while(n > 0);
1336 	poperror();
1337 	qunlock(&e->wlock);
1338 	return p-(uchar*)a;
1339 }
1340 
1341 static void
1342 init(Usbhost* uh)
1343 {
1344 	Ctlr *ctlr;
1345 
1346 	ctlr = uh->ctlr;
1347 	ilock(ctlr);
1348 	outl(ctlr->io+Flbaseadd, PCIWADDR(ctlr->frames));
1349 	OUT(Frnum, 0);
1350 	OUT(Usbintr, 0xF);	/* enable all interrupts */
1351 	XPRINT("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(ctlr->io+SOFMod));
1352 	XPRINT("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1));
1353 	if((IN(Cmd)&1)==0)
1354 		OUT(Cmd, 1);	/* run */
1355 //	pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0));
1356 	iunlock(ctlr);
1357 }
1358 
1359 static void
1360 scanpci(void)
1361 {
1362 	int io;
1363 	Ctlr *ctlr;
1364 	Pcidev *p;
1365 	static int already = 0;
1366 
1367 	if(already)
1368 		return;
1369 	already = 1;
1370 	p = nil;
1371 	while(p = pcimatch(p, 0, 0)) {
1372 		/*
1373 		 * Find UHCI controllers.  Class = 12 (serial controller),
1374 		 * Sub-class = 3 (USB) and Programming Interface = 0.
1375 		 */
1376 		if(p->ccrb != 0x0C || p->ccru != 0x03)
1377 			continue;
1378 		switch(p->ccrp){
1379 		case 0x00:
1380 			io = p->mem[4].bar & ~0x0F;
1381 			break;
1382 		case 0x10:
1383 		case 0x20:
1384 			print("usb%chci: %x/%x %sport 0x%lux size 0x%x irq %d (ignored)\n",
1385 				(p->ccrp == 0x10? 'o': 'e'), p->vid, p->did,
1386 				(p->ccrp == 0x10? "": "USB 2 "),
1387 				p->mem[0].bar & ~0x0F, p->mem[0].size, p->intl);
1388 			/* fallthrough */
1389 		default:
1390 			continue;
1391 		}
1392 		if(io == 0) {
1393 			print("usbuhci: failed to map registers\n");
1394 			continue;
1395 		}
1396 		if(ioalloc(io, p->mem[4].size, 0, "usbuhci") < 0){
1397 			print("usbuhci: port %d in use\n", io);
1398 			continue;
1399 		}
1400 		if(p->intl == 0xFF || p->intl == 0) {
1401 			print("usbuhci: no irq assigned for port %d\n", io);
1402 			continue;
1403 		}
1404 
1405 		XPRINT("usbuhci: %x/%x port 0x%ux size 0x%x irq %d\n",
1406 			p->vid, p->did, io, p->mem[4].size, p->intl);
1407 
1408 		ctlr = malloc(sizeof(Ctlr));
1409 		ctlr->pcidev = p;
1410 		ctlr->io = io;
1411 		if(ctlrhead != nil)
1412 			ctlrtail->next = ctlr;
1413 		else
1414 			ctlrhead = ctlr;
1415 		ctlrtail = ctlr;
1416 	}
1417 }
1418 
1419 static int
1420 reset(Usbhost *uh)
1421 {
1422 	int i;
1423 	TD *t;
1424 	ulong io;
1425 	Ctlr *ctlr;
1426 	Pcidev *p;
1427 
1428 	scanpci();
1429 
1430 	/*
1431 	 * Any adapter matches if no uh->port is supplied,
1432 	 * otherwise the ports must match.
1433 	 */
1434 	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
1435 		if(ctlr->active)
1436 			continue;
1437 		if(uh->port == 0 || uh->port == ctlr->io){
1438 			ctlr->active = 1;
1439 			break;
1440 		}
1441 	}
1442 	if(ctlr == nil)
1443 		return -1;
1444 
1445 	io = ctlr->io;
1446 	p = ctlr->pcidev;
1447 
1448 	uh->ctlr = ctlr;
1449 	uh->port = io;
1450 	uh->irq = p->intl;
1451 	uh->tbdf = p->tbdf;
1452 
1453 	XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n",
1454 		IN(Cmd), IN(Status), IN(Usbintr), inb(io+Frnum));
1455 	XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n",
1456 		IN(Flbaseadd), inb(io+SOFMod), IN(Portsc0), IN(Portsc1));
1457 
1458 	OUT(Cmd, 0);					/* stop */
1459 	while((IN(Status) & (1<<5)) == 0)	/* wait for halt */
1460 		;
1461 	OUT(Status, 0xFF);				/* clear pending interrupts */
1462 	pcicfgw16(p, 0xc0, 0x2000);		/* legacy support register: turn off lunacy mode */
1463 
1464 	if(0){
1465 		i = inb(io+SOFMod);
1466 		OUT(Cmd, 4);	/* global reset */
1467 		delay(15);
1468 		OUT(Cmd, 0);	/* end reset */
1469 		delay(4);
1470 		outb(io+SOFMod, i);
1471 	}
1472 
1473 	ctlr->tdpool = xspanalloc(128*sizeof(TD), 16, 0);
1474 	for(i=128; --i>=0;){
1475 		ctlr->tdpool[i].next = ctlr->freetd;
1476 		ctlr->freetd = &ctlr->tdpool[i];
1477 	}
1478 	ctlr->qhpool = xspanalloc(64*sizeof(QH), 16, 0);
1479 	for(i=64; --i>=0;){
1480 		ctlr->qhpool[i].next = ctlr->freeqh;
1481 		ctlr->freeqh = &ctlr->qhpool[i];
1482 	}
1483 
1484 	/*
1485 	 * the last entries of the periodic (interrupt & isochronous) scheduling TD entries
1486 	 * points to the control queue and the bandwidth sop for bulk traffic.
1487 	 * this is looped following the instructions in PIIX4 errata 29773804.pdf:
1488 	 * a QH links to a looped but inactive TD as its sole entry,
1489 	 * with its head entry leading on to the bulk traffic, the last QH of which
1490 	 * links back to the empty QH.
1491 	 */
1492 	ctlr->ctlq = allocqh(ctlr);
1493 	ctlr->bwsop = allocqh(ctlr);
1494 	ctlr->bulkq = allocqh(ctlr);
1495 	ctlr->recvq = allocqh(ctlr);
1496 	t = alloctd(ctlr);	/* inactive TD, looped */
1497 	t->link = PCIWADDR(t);
1498 	ctlr->bwsop->entries = PCIWADDR(t);
1499 
1500 	ctlr->ctlq->head = PCIWADDR(ctlr->bulkq) | IsQH;
1501 	ctlr->bulkq->head = PCIWADDR(ctlr->recvq) | IsQH;
1502 	ctlr->recvq->head = PCIWADDR(ctlr->bwsop) | IsQH;
1503 	if (1)	/* don't use loop back */
1504  		ctlr->bwsop->head = Terminate;
1505 	else	/* set up loop back */
1506 		ctlr->bwsop->head = PCIWADDR(ctlr->bwsop) | IsQH;
1507 
1508 	ctlr->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0);
1509 	ctlr->frameld = xallocz(FRAMESIZE, 1);
1510 	for (i = 0; i < NFRAME; i++)
1511 		ctlr->frames[i] = PCIWADDR(ctlr->ctlq) | IsQH;
1512 
1513 	/*
1514 	 * Linkage to the generic USB driver.
1515 	 */
1516 	uh->init = init;
1517 	uh->interrupt = interrupt;
1518 
1519 	uh->portinfo = portinfo;
1520 	uh->portreset = portreset;
1521 	uh->portenable = portenable;
1522 
1523 	uh->epalloc = epalloc;
1524 	uh->epfree = epfree;
1525 	uh->epopen = epopen;
1526 	uh->epclose = epclose;
1527 	uh->epmode = epmode;
1528 
1529 	uh->read = read;
1530 	uh->write = write;
1531 
1532 	return 0;
1533 }
1534 
1535 void
1536 usbuhcilink(void)
1537 {
1538 	addusbtype("uhci", reset);
1539 }
1540