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