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