1*a81c3ea0SDavid du Colombier /*
2*a81c3ea0SDavid du Colombier * USB Open Host Controller Interface (Ohci) driver
3*a81c3ea0SDavid du Colombier *
4*a81c3ea0SDavid du Colombier * BUGS:
5*a81c3ea0SDavid du Colombier * - Missing isochronous input streams.
6*a81c3ea0SDavid du Colombier * - Too many delays and ilocks.
7*a81c3ea0SDavid du Colombier * - bandwidth admission control must be done per-frame.
8*a81c3ea0SDavid du Colombier * - Buffering could be handled like in uhci, to avoid
9*a81c3ea0SDavid du Colombier * needed block allocation and avoid allocs for small Tds.
10*a81c3ea0SDavid du Colombier * - must warn of power overruns.
11*a81c3ea0SDavid du Colombier */
12*a81c3ea0SDavid du Colombier
13*a81c3ea0SDavid du Colombier #include "u.h"
14*a81c3ea0SDavid du Colombier #include "../port/lib.h"
15*a81c3ea0SDavid du Colombier #include "mem.h"
16*a81c3ea0SDavid du Colombier #include "dat.h"
17*a81c3ea0SDavid du Colombier #include "fns.h"
18*a81c3ea0SDavid du Colombier #include "io.h"
19*a81c3ea0SDavid du Colombier #include "../port/error.h"
20*a81c3ea0SDavid du Colombier
21*a81c3ea0SDavid du Colombier #include "../port/usb.h"
22*a81c3ea0SDavid du Colombier
23*a81c3ea0SDavid du Colombier typedef struct Ctlio Ctlio;
24*a81c3ea0SDavid du Colombier typedef struct Ctlr Ctlr;
25*a81c3ea0SDavid du Colombier typedef struct Ed Ed;
26*a81c3ea0SDavid du Colombier typedef struct Edpool Edpool;
27*a81c3ea0SDavid du Colombier typedef struct Epx Epx;
28*a81c3ea0SDavid du Colombier typedef struct Hcca Hcca;
29*a81c3ea0SDavid du Colombier typedef struct Isoio Isoio;
30*a81c3ea0SDavid du Colombier typedef struct Ohci Ohci;
31*a81c3ea0SDavid du Colombier typedef struct Qio Qio;
32*a81c3ea0SDavid du Colombier typedef struct Qtree Qtree;
33*a81c3ea0SDavid du Colombier typedef struct Td Td;
34*a81c3ea0SDavid du Colombier typedef struct Tdpool Tdpool;
35*a81c3ea0SDavid du Colombier
36*a81c3ea0SDavid du Colombier enum
37*a81c3ea0SDavid du Colombier {
38*a81c3ea0SDavid du Colombier Incr = 64, /* for Td and Ed pools */
39*a81c3ea0SDavid du Colombier
40*a81c3ea0SDavid du Colombier Align = 0x20, /* OHCI only requires 0x10 */
41*a81c3ea0SDavid du Colombier /* use always a power of 2 */
42*a81c3ea0SDavid du Colombier
43*a81c3ea0SDavid du Colombier Abortdelay = 1, /* delay after cancelling Tds (ms) */
44*a81c3ea0SDavid du Colombier Tdatomic = 8, /* max nb. of Tds per bulk I/O op. */
45*a81c3ea0SDavid du Colombier Enabledelay = 100, /* waiting for a port to enable */
46*a81c3ea0SDavid du Colombier
47*a81c3ea0SDavid du Colombier
48*a81c3ea0SDavid du Colombier /* Queue states (software) */
49*a81c3ea0SDavid du Colombier Qidle = 0,
50*a81c3ea0SDavid du Colombier Qinstall,
51*a81c3ea0SDavid du Colombier Qrun,
52*a81c3ea0SDavid du Colombier Qdone,
53*a81c3ea0SDavid du Colombier Qclose,
54*a81c3ea0SDavid du Colombier Qfree,
55*a81c3ea0SDavid du Colombier
56*a81c3ea0SDavid du Colombier /* Ed control bits */
57*a81c3ea0SDavid du Colombier Edmpsmask = 0x7ff, /* max packet size */
58*a81c3ea0SDavid du Colombier Edmpsshift = 16,
59*a81c3ea0SDavid du Colombier Edlow = 1 << 13, /* low speed */
60*a81c3ea0SDavid du Colombier Edskip = 1 << 14, /* skip this ed */
61*a81c3ea0SDavid du Colombier Ediso = 1 << 15, /* iso Tds used */
62*a81c3ea0SDavid du Colombier Edtddir = 0, /* get dir from td */
63*a81c3ea0SDavid du Colombier Edin = 2 << 11, /* direction in */
64*a81c3ea0SDavid du Colombier Edout = 1 << 11, /* direction out */
65*a81c3ea0SDavid du Colombier Eddirmask = 3 << 11, /* direction bits */
66*a81c3ea0SDavid du Colombier Edhalt = 1, /* halted (in head ptr) */
67*a81c3ea0SDavid du Colombier Edtoggle = 2, /* toggle (in head ptr) 1 == data1 */
68*a81c3ea0SDavid du Colombier
69*a81c3ea0SDavid du Colombier /* Td control bits */
70*a81c3ea0SDavid du Colombier Tdround = 1<<18, /* (rounding) short packets ok */
71*a81c3ea0SDavid du Colombier Tdtoksetup = 0<<19, /* setup packet */
72*a81c3ea0SDavid du Colombier Tdtokin = 2<<19, /* in packet */
73*a81c3ea0SDavid du Colombier Tdtokout = 1<<19, /* out packet */
74*a81c3ea0SDavid du Colombier Tdtokmask = 3<<19, /* in/out/setup bits */
75*a81c3ea0SDavid du Colombier Tdnoioc = 7<<21, /* intr. cnt. value for no interrupt */
76*a81c3ea0SDavid du Colombier Tdusetog = 1<<25, /* use toggle from Td (1) or Ed (0) */
77*a81c3ea0SDavid du Colombier Tddata1 = 1<<24, /* data toggle (1 == data1) */
78*a81c3ea0SDavid du Colombier Tddata0 = 0<<24,
79*a81c3ea0SDavid du Colombier Tdfcmask = 7, /* frame count (iso) */
80*a81c3ea0SDavid du Colombier Tdfcshift = 24,
81*a81c3ea0SDavid du Colombier Tdsfmask = 0xFFFF, /* starting frame (iso) */
82*a81c3ea0SDavid du Colombier Tderrmask = 3, /* error counter */
83*a81c3ea0SDavid du Colombier Tderrshift = 26,
84*a81c3ea0SDavid du Colombier Tdccmask = 0xf, /* condition code (status) */
85*a81c3ea0SDavid du Colombier Tdccshift = 28,
86*a81c3ea0SDavid du Colombier Tdiccmask = 0xf, /* condition code (iso, offsets) */
87*a81c3ea0SDavid du Colombier Tdiccshift = 12,
88*a81c3ea0SDavid du Colombier
89*a81c3ea0SDavid du Colombier Ntdframes = 0x10000, /* # of different iso frame numbers */
90*a81c3ea0SDavid du Colombier
91*a81c3ea0SDavid du Colombier /* Td errors (condition code) */
92*a81c3ea0SDavid du Colombier Tdok = 0,
93*a81c3ea0SDavid du Colombier Tdcrc = 1,
94*a81c3ea0SDavid du Colombier Tdbitstuff = 2,
95*a81c3ea0SDavid du Colombier Tdbadtog = 3,
96*a81c3ea0SDavid du Colombier Tdstalled = 4,
97*a81c3ea0SDavid du Colombier Tdtmout = 5,
98*a81c3ea0SDavid du Colombier Tdpidchk = 6,
99*a81c3ea0SDavid du Colombier Tdbadpid = 7,
100*a81c3ea0SDavid du Colombier Tddataovr = 8,
101*a81c3ea0SDavid du Colombier Tddataund = 9,
102*a81c3ea0SDavid du Colombier Tdbufovr = 0xC,
103*a81c3ea0SDavid du Colombier Tdbufund = 0xD,
104*a81c3ea0SDavid du Colombier Tdnotacc = 0xE,
105*a81c3ea0SDavid du Colombier
106*a81c3ea0SDavid du Colombier /* control register */
107*a81c3ea0SDavid du Colombier Cple = 0x04, /* periodic list enable */
108*a81c3ea0SDavid du Colombier Cie = 0x08, /* iso. list enable */
109*a81c3ea0SDavid du Colombier Ccle = 0x10, /* ctl list enable */
110*a81c3ea0SDavid du Colombier Cble = 0x20, /* bulk list enable */
111*a81c3ea0SDavid du Colombier Cfsmask = 3 << 6, /* functional state... */
112*a81c3ea0SDavid du Colombier Cfsreset = 0 << 6,
113*a81c3ea0SDavid du Colombier Cfsresume = 1 << 6,
114*a81c3ea0SDavid du Colombier Cfsoper = 2 << 6,
115*a81c3ea0SDavid du Colombier Cfssuspend = 3 << 6,
116*a81c3ea0SDavid du Colombier
117*a81c3ea0SDavid du Colombier /* command status */
118*a81c3ea0SDavid du Colombier Sblf = 1 << 2, /* bulk list (load) flag */
119*a81c3ea0SDavid du Colombier Sclf = 1 << 1, /* control list (load) flag */
120*a81c3ea0SDavid du Colombier Shcr = 1 << 0, /* host controller reset */
121*a81c3ea0SDavid du Colombier
122*a81c3ea0SDavid du Colombier /* intr enable */
123*a81c3ea0SDavid du Colombier Mie = 1 << 31,
124*a81c3ea0SDavid du Colombier Oc = 1 << 30,
125*a81c3ea0SDavid du Colombier Rhsc = 1 << 6,
126*a81c3ea0SDavid du Colombier Fno = 1 << 5,
127*a81c3ea0SDavid du Colombier Ue = 1 << 4,
128*a81c3ea0SDavid du Colombier Rd = 1 << 3,
129*a81c3ea0SDavid du Colombier Sf = 1 << 2,
130*a81c3ea0SDavid du Colombier Wdh = 1 << 1,
131*a81c3ea0SDavid du Colombier So = 1 << 0,
132*a81c3ea0SDavid du Colombier
133*a81c3ea0SDavid du Colombier Fmaxpktmask = 0x7fff,
134*a81c3ea0SDavid du Colombier Fmaxpktshift = 16,
135*a81c3ea0SDavid du Colombier HcRhDescA_POTPGT_MASK = 0xff << 24,
136*a81c3ea0SDavid du Colombier HcRhDescA_POTPGT_SHIFT = 24,
137*a81c3ea0SDavid du Colombier
138*a81c3ea0SDavid du Colombier /* Rh status */
139*a81c3ea0SDavid du Colombier Lps = 1 << 0,
140*a81c3ea0SDavid du Colombier Cgp = 1 << 0,
141*a81c3ea0SDavid du Colombier Oci = 1 << 1,
142*a81c3ea0SDavid du Colombier Psm = 1 << 8,
143*a81c3ea0SDavid du Colombier Nps = 1 << 9,
144*a81c3ea0SDavid du Colombier Drwe = 1 << 15,
145*a81c3ea0SDavid du Colombier Srwe = 1 << 15,
146*a81c3ea0SDavid du Colombier Lpsc = 1 << 16,
147*a81c3ea0SDavid du Colombier Ccic = 1 << 17,
148*a81c3ea0SDavid du Colombier Crwe = 1 << 31,
149*a81c3ea0SDavid du Colombier
150*a81c3ea0SDavid du Colombier /* port status */
151*a81c3ea0SDavid du Colombier Ccs = 0x00001, /* current connect status */
152*a81c3ea0SDavid du Colombier Pes = 0x00002, /* port enable status */
153*a81c3ea0SDavid du Colombier Pss = 0x00004, /* port suspend status */
154*a81c3ea0SDavid du Colombier Poci = 0x00008, /* over current indicator */
155*a81c3ea0SDavid du Colombier Prs = 0x00010, /* port reset status */
156*a81c3ea0SDavid du Colombier Pps = 0x00100, /* port power status */
157*a81c3ea0SDavid du Colombier Lsda = 0x00200, /* low speed device attached */
158*a81c3ea0SDavid du Colombier Csc = 0x10000, /* connect status change */
159*a81c3ea0SDavid du Colombier Pesc = 0x20000, /* enable status change */
160*a81c3ea0SDavid du Colombier Pssc = 0x40000, /* suspend status change */
161*a81c3ea0SDavid du Colombier Ocic = 0x80000, /* over current ind. change */
162*a81c3ea0SDavid du Colombier Prsc = 0x100000, /* reset status change */
163*a81c3ea0SDavid du Colombier
164*a81c3ea0SDavid du Colombier /* port status write bits */
165*a81c3ea0SDavid du Colombier Cpe = 0x001, /* clear port enable */
166*a81c3ea0SDavid du Colombier Spe = 0x002, /* set port enable */
167*a81c3ea0SDavid du Colombier Spr = 0x010, /* set port reset */
168*a81c3ea0SDavid du Colombier Spp = 0x100, /* set port power */
169*a81c3ea0SDavid du Colombier Cpp = 0x200, /* clear port power */
170*a81c3ea0SDavid du Colombier
171*a81c3ea0SDavid du Colombier };
172*a81c3ea0SDavid du Colombier
173*a81c3ea0SDavid du Colombier /*
174*a81c3ea0SDavid du Colombier * Endpoint descriptor. (first 4 words used by hardware)
175*a81c3ea0SDavid du Colombier */
176*a81c3ea0SDavid du Colombier struct Ed {
177*a81c3ea0SDavid du Colombier ulong ctrl;
178*a81c3ea0SDavid du Colombier ulong tail; /* transfer descriptor */
179*a81c3ea0SDavid du Colombier ulong head;
180*a81c3ea0SDavid du Colombier ulong nexted;
181*a81c3ea0SDavid du Colombier
182*a81c3ea0SDavid du Colombier Ed* next; /* sw; in free list or next in list */
183*a81c3ea0SDavid du Colombier Td* tds; /* in use by current xfer; all for iso */
184*a81c3ea0SDavid du Colombier Ep* ep; /* debug/align */
185*a81c3ea0SDavid du Colombier Ed* inext; /* debug/align (dump interrupt eds). */
186*a81c3ea0SDavid du Colombier };
187*a81c3ea0SDavid du Colombier
188*a81c3ea0SDavid du Colombier /*
189*a81c3ea0SDavid du Colombier * Endpoint I/O state (software), per direction.
190*a81c3ea0SDavid du Colombier */
191*a81c3ea0SDavid du Colombier struct Qio
192*a81c3ea0SDavid du Colombier {
193*a81c3ea0SDavid du Colombier QLock; /* for the entire I/O process */
194*a81c3ea0SDavid du Colombier Rendez; /* wait for completion */
195*a81c3ea0SDavid du Colombier Ed* ed; /* to place Tds on it */
196*a81c3ea0SDavid du Colombier int sched; /* queue number (intr/iso) */
197*a81c3ea0SDavid du Colombier int toggle; /* Tddata0/Tddata1 */
198*a81c3ea0SDavid du Colombier ulong usbid; /* device/endpoint address */
199*a81c3ea0SDavid du Colombier int tok; /* Tdsetup, Tdtokin, Tdtokout */
200*a81c3ea0SDavid du Colombier long iotime; /* last I/O time; to hold interrupt polls */
201*a81c3ea0SDavid du Colombier int debug; /* for the endpoint */
202*a81c3ea0SDavid du Colombier char* err; /* error status */
203*a81c3ea0SDavid du Colombier int state; /* Qidle -> Qinstall -> Qrun -> Qdone | Qclose */
204*a81c3ea0SDavid du Colombier long bw; /* load (intr/iso) */
205*a81c3ea0SDavid du Colombier };
206*a81c3ea0SDavid du Colombier
207*a81c3ea0SDavid du Colombier struct Ctlio
208*a81c3ea0SDavid du Colombier {
209*a81c3ea0SDavid du Colombier Qio; /* single Ed for all transfers */
210*a81c3ea0SDavid du Colombier uchar* data; /* read from last ctl req. */
211*a81c3ea0SDavid du Colombier int ndata; /* number of bytes read */
212*a81c3ea0SDavid du Colombier };
213*a81c3ea0SDavid du Colombier
214*a81c3ea0SDavid du Colombier struct Isoio
215*a81c3ea0SDavid du Colombier {
216*a81c3ea0SDavid du Colombier Qio;
217*a81c3ea0SDavid du Colombier int nframes; /* number of frames for a full second */
218*a81c3ea0SDavid du Colombier Td* atds; /* Tds avail for further I/O */
219*a81c3ea0SDavid du Colombier int navail; /* number of avail Tds */
220*a81c3ea0SDavid du Colombier ulong frno; /* next frame number avail for I/O */
221*a81c3ea0SDavid du Colombier ulong left; /* remainder after rounding Hz to samples/ms */
222*a81c3ea0SDavid du Colombier int nerrs; /* consecutive errors on iso I/O */
223*a81c3ea0SDavid du Colombier };
224*a81c3ea0SDavid du Colombier
225*a81c3ea0SDavid du Colombier /*
226*a81c3ea0SDavid du Colombier * Transfer descriptor. Size must be multiple of 32
227*a81c3ea0SDavid du Colombier * First block is used by hardware (aligned to 32).
228*a81c3ea0SDavid du Colombier */
229*a81c3ea0SDavid du Colombier struct Td
230*a81c3ea0SDavid du Colombier {
231*a81c3ea0SDavid du Colombier ulong ctrl;
232*a81c3ea0SDavid du Colombier ulong cbp; /* current buffer pointer */
233*a81c3ea0SDavid du Colombier ulong nexttd;
234*a81c3ea0SDavid du Colombier ulong be;
235*a81c3ea0SDavid du Colombier ushort offsets[8]; /* used by Iso Tds only */
236*a81c3ea0SDavid du Colombier
237*a81c3ea0SDavid du Colombier Td* next; /* in free or Ed tds list */
238*a81c3ea0SDavid du Colombier Td* anext; /* in avail td list (iso) */
239*a81c3ea0SDavid du Colombier Ep* ep; /* using this Td for I/O */
240*a81c3ea0SDavid du Colombier Qio* io; /* using this Td for I/O */
241*a81c3ea0SDavid du Colombier Block* bp; /* data for this Td */
242*a81c3ea0SDavid du Colombier ulong nbytes; /* bytes in this Td */
243*a81c3ea0SDavid du Colombier ulong cbp0; /* initial value for cbp */
244*a81c3ea0SDavid du Colombier ulong last; /* true for last Td in Qio */
245*a81c3ea0SDavid du Colombier };
246*a81c3ea0SDavid du Colombier
247*a81c3ea0SDavid du Colombier /*
248*a81c3ea0SDavid du Colombier * Host controller communication area (hardware)
249*a81c3ea0SDavid du Colombier */
250*a81c3ea0SDavid du Colombier struct Hcca
251*a81c3ea0SDavid du Colombier {
252*a81c3ea0SDavid du Colombier ulong intrtable[32];
253*a81c3ea0SDavid du Colombier ushort framenumber;
254*a81c3ea0SDavid du Colombier ushort pad1;
255*a81c3ea0SDavid du Colombier ulong donehead;
256*a81c3ea0SDavid du Colombier uchar reserved[116];
257*a81c3ea0SDavid du Colombier };
258*a81c3ea0SDavid du Colombier
259*a81c3ea0SDavid du Colombier /*
260*a81c3ea0SDavid du Colombier * I/O registers
261*a81c3ea0SDavid du Colombier */
262*a81c3ea0SDavid du Colombier struct Ohci
263*a81c3ea0SDavid du Colombier {
264*a81c3ea0SDavid du Colombier /* control and status group */
265*a81c3ea0SDavid du Colombier ulong revision; /*00*/
266*a81c3ea0SDavid du Colombier ulong control; /*04*/
267*a81c3ea0SDavid du Colombier ulong cmdsts; /*08*/
268*a81c3ea0SDavid du Colombier ulong intrsts; /*0c*/
269*a81c3ea0SDavid du Colombier ulong intrenable; /*10*/
270*a81c3ea0SDavid du Colombier ulong intrdisable; /*14*/
271*a81c3ea0SDavid du Colombier
272*a81c3ea0SDavid du Colombier /* memory pointer group */
273*a81c3ea0SDavid du Colombier ulong hcca; /*18*/
274*a81c3ea0SDavid du Colombier ulong periodcurred; /*1c*/
275*a81c3ea0SDavid du Colombier ulong ctlheaded; /*20*/
276*a81c3ea0SDavid du Colombier ulong ctlcurred; /*24*/
277*a81c3ea0SDavid du Colombier ulong bulkheaded; /*28*/
278*a81c3ea0SDavid du Colombier ulong bulkcurred; /*2c*/
279*a81c3ea0SDavid du Colombier ulong donehead; /*30*/
280*a81c3ea0SDavid du Colombier
281*a81c3ea0SDavid du Colombier /* frame counter group */
282*a81c3ea0SDavid du Colombier ulong fminterval; /*34*/
283*a81c3ea0SDavid du Colombier ulong fmremaining; /*38*/
284*a81c3ea0SDavid du Colombier ulong fmnumber; /*3c*/
285*a81c3ea0SDavid du Colombier ulong periodicstart; /*40*/
286*a81c3ea0SDavid du Colombier ulong lsthreshold; /*44*/
287*a81c3ea0SDavid du Colombier
288*a81c3ea0SDavid du Colombier /* root hub group */
289*a81c3ea0SDavid du Colombier ulong rhdesca; /*48*/
290*a81c3ea0SDavid du Colombier ulong rhdescb; /*4c*/
291*a81c3ea0SDavid du Colombier ulong rhsts; /*50*/
292*a81c3ea0SDavid du Colombier ulong rhportsts[15]; /*54*/
293*a81c3ea0SDavid du Colombier ulong pad25[20]; /*90*/
294*a81c3ea0SDavid du Colombier
295*a81c3ea0SDavid du Colombier /* unknown */
296*a81c3ea0SDavid du Colombier ulong hostueaddr; /*e0*/
297*a81c3ea0SDavid du Colombier ulong hostuests; /*e4*/
298*a81c3ea0SDavid du Colombier ulong hosttimeoutctrl; /*e8*/
299*a81c3ea0SDavid du Colombier ulong pad59; /*ec*/
300*a81c3ea0SDavid du Colombier ulong pad60; /*f0*/
301*a81c3ea0SDavid du Colombier ulong hostrevision; /*f4*/
302*a81c3ea0SDavid du Colombier ulong pad62[2];
303*a81c3ea0SDavid du Colombier /*100*/
304*a81c3ea0SDavid du Colombier };
305*a81c3ea0SDavid du Colombier
306*a81c3ea0SDavid du Colombier /*
307*a81c3ea0SDavid du Colombier * Endpoint tree (software)
308*a81c3ea0SDavid du Colombier */
309*a81c3ea0SDavid du Colombier struct Qtree
310*a81c3ea0SDavid du Colombier {
311*a81c3ea0SDavid du Colombier int nel;
312*a81c3ea0SDavid du Colombier int depth;
313*a81c3ea0SDavid du Colombier ulong* bw;
314*a81c3ea0SDavid du Colombier Ed** root;
315*a81c3ea0SDavid du Colombier };
316*a81c3ea0SDavid du Colombier
317*a81c3ea0SDavid du Colombier struct Tdpool
318*a81c3ea0SDavid du Colombier {
319*a81c3ea0SDavid du Colombier Lock;
320*a81c3ea0SDavid du Colombier Td* free;
321*a81c3ea0SDavid du Colombier int nalloc;
322*a81c3ea0SDavid du Colombier int ninuse;
323*a81c3ea0SDavid du Colombier int nfree;
324*a81c3ea0SDavid du Colombier };
325*a81c3ea0SDavid du Colombier
326*a81c3ea0SDavid du Colombier struct Edpool
327*a81c3ea0SDavid du Colombier {
328*a81c3ea0SDavid du Colombier Lock;
329*a81c3ea0SDavid du Colombier Ed* free;
330*a81c3ea0SDavid du Colombier int nalloc;
331*a81c3ea0SDavid du Colombier int ninuse;
332*a81c3ea0SDavid du Colombier int nfree;
333*a81c3ea0SDavid du Colombier };
334*a81c3ea0SDavid du Colombier
335*a81c3ea0SDavid du Colombier struct Ctlr
336*a81c3ea0SDavid du Colombier {
337*a81c3ea0SDavid du Colombier Lock; /* for ilock; lists and basic ctlr I/O */
338*a81c3ea0SDavid du Colombier QLock resetl; /* lock controller during USB reset */
339*a81c3ea0SDavid du Colombier int active;
340*a81c3ea0SDavid du Colombier Ctlr* next;
341*a81c3ea0SDavid du Colombier int nports;
342*a81c3ea0SDavid du Colombier
343*a81c3ea0SDavid du Colombier Ohci* ohci; /* base I/O address */
344*a81c3ea0SDavid du Colombier Hcca* hcca; /* intr/done Td lists (used by hardware) */
345*a81c3ea0SDavid du Colombier int overrun; /* sched. overrun */
346*a81c3ea0SDavid du Colombier Ed* intrhd; /* list of intr. eds in tree */
347*a81c3ea0SDavid du Colombier Qtree* tree; /* tree for t Ep i/o */
348*a81c3ea0SDavid du Colombier int ntree; /* number of dummy Eds in tree */
349*a81c3ea0SDavid du Colombier Pcidev* pcidev;
350*a81c3ea0SDavid du Colombier };
351*a81c3ea0SDavid du Colombier
352*a81c3ea0SDavid du Colombier #define dqprint if(debug || io && io->debug)print
353*a81c3ea0SDavid du Colombier #define ddqprint if(debug>1 || (io && io->debug>1))print
354*a81c3ea0SDavid du Colombier #define diprint if(debug || iso && iso->debug)print
355*a81c3ea0SDavid du Colombier #define ddiprint if(debug>1 || (iso && iso->debug>1))print
356*a81c3ea0SDavid du Colombier #define TRUNC(x, sz) ((x) & ((sz)-1))
357*a81c3ea0SDavid du Colombier
358*a81c3ea0SDavid du Colombier static int ohciinterrupts[Nttypes];
359*a81c3ea0SDavid du Colombier static char* iosname[] = { "idle", "install", "run", "done", "close", "FREE" };
360*a81c3ea0SDavid du Colombier
361*a81c3ea0SDavid du Colombier static int debug;
362*a81c3ea0SDavid du Colombier static Edpool edpool;
363*a81c3ea0SDavid du Colombier static Tdpool tdpool;
364*a81c3ea0SDavid du Colombier static Ctlr* ctlrs[Nhcis];
365*a81c3ea0SDavid du Colombier
366*a81c3ea0SDavid du Colombier static QLock usbhstate; /* protects name space state */
367*a81c3ea0SDavid du Colombier
368*a81c3ea0SDavid du Colombier static int schedendpt(Ctlr *ub, Ep *ep);
369*a81c3ea0SDavid du Colombier static void unschedendpt(Ctlr *ub, Ep *ep);
370*a81c3ea0SDavid du Colombier static long qtd(Ctlr*, Ep*, int, Block*, uchar*, uchar*, int, ulong);
371*a81c3ea0SDavid du Colombier
372*a81c3ea0SDavid du Colombier static char* errmsgs[] =
373*a81c3ea0SDavid du Colombier {
374*a81c3ea0SDavid du Colombier [Tdcrc] "crc error",
375*a81c3ea0SDavid du Colombier [Tdbitstuff] "bit stuffing error",
376*a81c3ea0SDavid du Colombier [Tdbadtog] "bad toggle",
377*a81c3ea0SDavid du Colombier [Tdstalled] Estalled,
378*a81c3ea0SDavid du Colombier [Tdtmout] "timeout error",
379*a81c3ea0SDavid du Colombier [Tdpidchk] "pid check error",
380*a81c3ea0SDavid du Colombier [Tdbadpid] "bad pid",
381*a81c3ea0SDavid du Colombier [Tddataovr] "data overrun",
382*a81c3ea0SDavid du Colombier [Tddataund] "data underrun",
383*a81c3ea0SDavid du Colombier [Tdbufovr] "buffer overrun",
384*a81c3ea0SDavid du Colombier [Tdbufund] "buffer underrun",
385*a81c3ea0SDavid du Colombier [Tdnotacc] "not accessed"
386*a81c3ea0SDavid du Colombier };
387*a81c3ea0SDavid du Colombier
388*a81c3ea0SDavid du Colombier static void*
pa2ptr(ulong pa)389*a81c3ea0SDavid du Colombier pa2ptr(ulong pa)
390*a81c3ea0SDavid du Colombier {
391*a81c3ea0SDavid du Colombier if(pa == 0)
392*a81c3ea0SDavid du Colombier return nil;
393*a81c3ea0SDavid du Colombier else
394*a81c3ea0SDavid du Colombier return KSEG1ADDR(pa);
395*a81c3ea0SDavid du Colombier }
396*a81c3ea0SDavid du Colombier
397*a81c3ea0SDavid du Colombier static ulong
ptr2pa(void * p)398*a81c3ea0SDavid du Colombier ptr2pa(void *p)
399*a81c3ea0SDavid du Colombier {
400*a81c3ea0SDavid du Colombier if(p == nil)
401*a81c3ea0SDavid du Colombier return 0;
402*a81c3ea0SDavid du Colombier else
403*a81c3ea0SDavid du Colombier return PCIWADDR(p); // XXX
404*a81c3ea0SDavid du Colombier }
405*a81c3ea0SDavid du Colombier
406*a81c3ea0SDavid du Colombier static void
waitSOF(Ctlr * ub)407*a81c3ea0SDavid du Colombier waitSOF(Ctlr *ub)
408*a81c3ea0SDavid du Colombier {
409*a81c3ea0SDavid du Colombier int frame = ub->hcca->framenumber & 0x3f;
410*a81c3ea0SDavid du Colombier
411*a81c3ea0SDavid du Colombier do {
412*a81c3ea0SDavid du Colombier delay(2);
413*a81c3ea0SDavid du Colombier } while(frame == (ub->hcca->framenumber & 0x3f));
414*a81c3ea0SDavid du Colombier }
415*a81c3ea0SDavid du Colombier
416*a81c3ea0SDavid du Colombier static char*
errmsg(int err)417*a81c3ea0SDavid du Colombier errmsg(int err)
418*a81c3ea0SDavid du Colombier {
419*a81c3ea0SDavid du Colombier
420*a81c3ea0SDavid du Colombier if(err < nelem(errmsgs))
421*a81c3ea0SDavid du Colombier return errmsgs[err];
422*a81c3ea0SDavid du Colombier return nil;
423*a81c3ea0SDavid du Colombier }
424*a81c3ea0SDavid du Colombier
425*a81c3ea0SDavid du Colombier static Ed*
ctlhd(Ctlr * ctlr)426*a81c3ea0SDavid du Colombier ctlhd(Ctlr *ctlr)
427*a81c3ea0SDavid du Colombier {
428*a81c3ea0SDavid du Colombier return pa2ptr(ctlr->ohci->ctlheaded);
429*a81c3ea0SDavid du Colombier }
430*a81c3ea0SDavid du Colombier
431*a81c3ea0SDavid du Colombier static Ed*
bulkhd(Ctlr * ctlr)432*a81c3ea0SDavid du Colombier bulkhd(Ctlr *ctlr)
433*a81c3ea0SDavid du Colombier {
434*a81c3ea0SDavid du Colombier return pa2ptr(ctlr->ohci->bulkheaded);
435*a81c3ea0SDavid du Colombier }
436*a81c3ea0SDavid du Colombier
437*a81c3ea0SDavid du Colombier static void
edlinked(Ed * ed,Ed * next)438*a81c3ea0SDavid du Colombier edlinked(Ed *ed, Ed *next)
439*a81c3ea0SDavid du Colombier {
440*a81c3ea0SDavid du Colombier if(ed == nil)
441*a81c3ea0SDavid du Colombier print("edlinked: nil ed: pc %#p\n", getcallerpc(&ed));
442*a81c3ea0SDavid du Colombier ed->nexted = ptr2pa(next);
443*a81c3ea0SDavid du Colombier ed->next = next;
444*a81c3ea0SDavid du Colombier }
445*a81c3ea0SDavid du Colombier
446*a81c3ea0SDavid du Colombier static void
setctlhd(Ctlr * ctlr,Ed * ed)447*a81c3ea0SDavid du Colombier setctlhd(Ctlr *ctlr, Ed *ed)
448*a81c3ea0SDavid du Colombier {
449*a81c3ea0SDavid du Colombier ctlr->ohci->ctlheaded = ptr2pa(ed);
450*a81c3ea0SDavid du Colombier if(ed != nil)
451*a81c3ea0SDavid du Colombier ctlr->ohci->cmdsts |= Sclf; /* reload it on next pass */
452*a81c3ea0SDavid du Colombier }
453*a81c3ea0SDavid du Colombier
454*a81c3ea0SDavid du Colombier static void
setbulkhd(Ctlr * ctlr,Ed * ed)455*a81c3ea0SDavid du Colombier setbulkhd(Ctlr *ctlr, Ed *ed)
456*a81c3ea0SDavid du Colombier {
457*a81c3ea0SDavid du Colombier ctlr->ohci->bulkheaded = ptr2pa(ed);
458*a81c3ea0SDavid du Colombier if(ed != nil)
459*a81c3ea0SDavid du Colombier ctlr->ohci->cmdsts |= Sblf; /* reload it on next pass */
460*a81c3ea0SDavid du Colombier }
461*a81c3ea0SDavid du Colombier
462*a81c3ea0SDavid du Colombier static void
unlinkctl(Ctlr * ctlr,Ed * ed)463*a81c3ea0SDavid du Colombier unlinkctl(Ctlr *ctlr, Ed *ed)
464*a81c3ea0SDavid du Colombier {
465*a81c3ea0SDavid du Colombier Ed *this, *prev, *next;
466*a81c3ea0SDavid du Colombier
467*a81c3ea0SDavid du Colombier ctlr->ohci->control &= ~Ccle;
468*a81c3ea0SDavid du Colombier waitSOF(ctlr);
469*a81c3ea0SDavid du Colombier this = ctlhd(ctlr);
470*a81c3ea0SDavid du Colombier ctlr->ohci->ctlcurred = 0;
471*a81c3ea0SDavid du Colombier prev = nil;
472*a81c3ea0SDavid du Colombier while(this != nil && this != ed){
473*a81c3ea0SDavid du Colombier prev = this;
474*a81c3ea0SDavid du Colombier this = this->next;
475*a81c3ea0SDavid du Colombier }
476*a81c3ea0SDavid du Colombier if(this == nil){
477*a81c3ea0SDavid du Colombier print("unlinkctl: not found\n");
478*a81c3ea0SDavid du Colombier return;
479*a81c3ea0SDavid du Colombier }
480*a81c3ea0SDavid du Colombier next = this->next;
481*a81c3ea0SDavid du Colombier if(prev == nil)
482*a81c3ea0SDavid du Colombier setctlhd(ctlr, next);
483*a81c3ea0SDavid du Colombier else
484*a81c3ea0SDavid du Colombier edlinked(prev, next);
485*a81c3ea0SDavid du Colombier ctlr->ohci->control |= Ccle;
486*a81c3ea0SDavid du Colombier edlinked(ed, nil); /* wipe out next field */
487*a81c3ea0SDavid du Colombier }
488*a81c3ea0SDavid du Colombier
489*a81c3ea0SDavid du Colombier static void
unlinkbulk(Ctlr * ctlr,Ed * ed)490*a81c3ea0SDavid du Colombier unlinkbulk(Ctlr *ctlr, Ed *ed)
491*a81c3ea0SDavid du Colombier {
492*a81c3ea0SDavid du Colombier Ed *this, *prev, *next;
493*a81c3ea0SDavid du Colombier
494*a81c3ea0SDavid du Colombier ctlr->ohci->control &= ~Cble;
495*a81c3ea0SDavid du Colombier waitSOF(ctlr);
496*a81c3ea0SDavid du Colombier this = bulkhd(ctlr);
497*a81c3ea0SDavid du Colombier ctlr->ohci->bulkcurred = 0;
498*a81c3ea0SDavid du Colombier prev = nil;
499*a81c3ea0SDavid du Colombier while(this != nil && this != ed){
500*a81c3ea0SDavid du Colombier prev = this;
501*a81c3ea0SDavid du Colombier this = this->next;
502*a81c3ea0SDavid du Colombier }
503*a81c3ea0SDavid du Colombier if(this == nil){
504*a81c3ea0SDavid du Colombier print("unlinkbulk: not found\n");
505*a81c3ea0SDavid du Colombier return;
506*a81c3ea0SDavid du Colombier }
507*a81c3ea0SDavid du Colombier next = this->next;
508*a81c3ea0SDavid du Colombier if(prev == nil)
509*a81c3ea0SDavid du Colombier setbulkhd(ctlr, next);
510*a81c3ea0SDavid du Colombier else
511*a81c3ea0SDavid du Colombier edlinked(prev, next);
512*a81c3ea0SDavid du Colombier ctlr->ohci->control |= Cble;
513*a81c3ea0SDavid du Colombier edlinked(ed, nil); /* wipe out next field */
514*a81c3ea0SDavid du Colombier }
515*a81c3ea0SDavid du Colombier
516*a81c3ea0SDavid du Colombier static void
edsetaddr(Ed * ed,ulong addr)517*a81c3ea0SDavid du Colombier edsetaddr(Ed *ed, ulong addr)
518*a81c3ea0SDavid du Colombier {
519*a81c3ea0SDavid du Colombier ulong ctrl;
520*a81c3ea0SDavid du Colombier
521*a81c3ea0SDavid du Colombier ctrl = ed->ctrl & ~((Epmax<<7)|Devmax);
522*a81c3ea0SDavid du Colombier ctrl |= (addr & ((Epmax<<7)|Devmax));
523*a81c3ea0SDavid du Colombier ed->ctrl = ctrl;
524*a81c3ea0SDavid du Colombier }
525*a81c3ea0SDavid du Colombier
526*a81c3ea0SDavid du Colombier static void
edsettog(Ed * ed,int c)527*a81c3ea0SDavid du Colombier edsettog(Ed *ed, int c)
528*a81c3ea0SDavid du Colombier {
529*a81c3ea0SDavid du Colombier if(c != 0)
530*a81c3ea0SDavid du Colombier ed->head |= Edtoggle;
531*a81c3ea0SDavid du Colombier else
532*a81c3ea0SDavid du Colombier ed->head &= ~Edtoggle;
533*a81c3ea0SDavid du Colombier }
534*a81c3ea0SDavid du Colombier
535*a81c3ea0SDavid du Colombier static int
edtoggle(Ed * ed)536*a81c3ea0SDavid du Colombier edtoggle(Ed *ed)
537*a81c3ea0SDavid du Colombier {
538*a81c3ea0SDavid du Colombier return ed->head & Edtoggle;
539*a81c3ea0SDavid du Colombier }
540*a81c3ea0SDavid du Colombier
541*a81c3ea0SDavid du Colombier static int
edhalted(Ed * ed)542*a81c3ea0SDavid du Colombier edhalted(Ed *ed)
543*a81c3ea0SDavid du Colombier {
544*a81c3ea0SDavid du Colombier return ed->head & Edhalt;
545*a81c3ea0SDavid du Colombier }
546*a81c3ea0SDavid du Colombier
547*a81c3ea0SDavid du Colombier static int
edmaxpkt(Ed * ed)548*a81c3ea0SDavid du Colombier edmaxpkt(Ed *ed)
549*a81c3ea0SDavid du Colombier {
550*a81c3ea0SDavid du Colombier return (ed->ctrl >> Edmpsshift) & Edmpsmask;
551*a81c3ea0SDavid du Colombier }
552*a81c3ea0SDavid du Colombier
553*a81c3ea0SDavid du Colombier static void
edsetmaxpkt(Ed * ed,int m)554*a81c3ea0SDavid du Colombier edsetmaxpkt(Ed *ed, int m)
555*a81c3ea0SDavid du Colombier {
556*a81c3ea0SDavid du Colombier ulong c;
557*a81c3ea0SDavid du Colombier
558*a81c3ea0SDavid du Colombier c = ed->ctrl & ~(Edmpsmask << Edmpsshift);
559*a81c3ea0SDavid du Colombier ed->ctrl = c | ((m&Edmpsmask) << Edmpsshift);
560*a81c3ea0SDavid du Colombier }
561*a81c3ea0SDavid du Colombier
562*a81c3ea0SDavid du Colombier static int
tderrs(Td * td)563*a81c3ea0SDavid du Colombier tderrs(Td *td)
564*a81c3ea0SDavid du Colombier {
565*a81c3ea0SDavid du Colombier return (td->ctrl >> Tdccshift) & Tdccmask;
566*a81c3ea0SDavid du Colombier }
567*a81c3ea0SDavid du Colombier
568*a81c3ea0SDavid du Colombier static int
tdtok(Td * td)569*a81c3ea0SDavid du Colombier tdtok(Td *td)
570*a81c3ea0SDavid du Colombier {
571*a81c3ea0SDavid du Colombier return (td->ctrl & Tdtokmask);
572*a81c3ea0SDavid du Colombier }
573*a81c3ea0SDavid du Colombier
574*a81c3ea0SDavid du Colombier static Td*
tdalloc(void)575*a81c3ea0SDavid du Colombier tdalloc(void)
576*a81c3ea0SDavid du Colombier {
577*a81c3ea0SDavid du Colombier Td *td;
578*a81c3ea0SDavid du Colombier Td *pool;
579*a81c3ea0SDavid du Colombier int i;
580*a81c3ea0SDavid du Colombier
581*a81c3ea0SDavid du Colombier lock(&tdpool);
582*a81c3ea0SDavid du Colombier if(tdpool.free == nil){
583*a81c3ea0SDavid du Colombier ddprint("ohci: tdalloc %d Tds\n", Incr);
584*a81c3ea0SDavid du Colombier pool = xspanalloc(Incr*sizeof(Td), Align, 0);
585*a81c3ea0SDavid du Colombier if(pool == nil)
586*a81c3ea0SDavid du Colombier panic("tdalloc");
587*a81c3ea0SDavid du Colombier pool = KSEG1ADDR(pool); // XXX
588*a81c3ea0SDavid du Colombier for(i=Incr; --i>=0;){
589*a81c3ea0SDavid du Colombier pool[i].next = tdpool.free;
590*a81c3ea0SDavid du Colombier tdpool.free = &pool[i];
591*a81c3ea0SDavid du Colombier }
592*a81c3ea0SDavid du Colombier tdpool.nalloc += Incr;
593*a81c3ea0SDavid du Colombier tdpool.nfree += Incr;
594*a81c3ea0SDavid du Colombier }
595*a81c3ea0SDavid du Colombier tdpool.ninuse++;
596*a81c3ea0SDavid du Colombier tdpool.nfree--;
597*a81c3ea0SDavid du Colombier td = tdpool.free;
598*a81c3ea0SDavid du Colombier tdpool.free = td->next;
599*a81c3ea0SDavid du Colombier memset(td, 0, sizeof(Td));
600*a81c3ea0SDavid du Colombier unlock(&tdpool);
601*a81c3ea0SDavid du Colombier
602*a81c3ea0SDavid du Colombier assert(((uintptr)td & 0xF) == 0);
603*a81c3ea0SDavid du Colombier return td;
604*a81c3ea0SDavid du Colombier }
605*a81c3ea0SDavid du Colombier
606*a81c3ea0SDavid du Colombier static void
tdfree(Td * td)607*a81c3ea0SDavid du Colombier tdfree(Td *td)
608*a81c3ea0SDavid du Colombier {
609*a81c3ea0SDavid du Colombier if(td == 0)
610*a81c3ea0SDavid du Colombier return;
611*a81c3ea0SDavid du Colombier freeb(td->bp);
612*a81c3ea0SDavid du Colombier td->bp = nil;
613*a81c3ea0SDavid du Colombier lock(&tdpool);
614*a81c3ea0SDavid du Colombier if(td->nexttd == 0x77777777)
615*a81c3ea0SDavid du Colombier panic("ohci: tdfree: double free");
616*a81c3ea0SDavid du Colombier memset(td, 7, sizeof(Td)); /* poison */
617*a81c3ea0SDavid du Colombier td->next = tdpool.free;
618*a81c3ea0SDavid du Colombier tdpool.free = td;
619*a81c3ea0SDavid du Colombier tdpool.ninuse--;
620*a81c3ea0SDavid du Colombier tdpool.nfree++;
621*a81c3ea0SDavid du Colombier unlock(&tdpool);
622*a81c3ea0SDavid du Colombier }
623*a81c3ea0SDavid du Colombier
624*a81c3ea0SDavid du Colombier static Ed*
edalloc(void)625*a81c3ea0SDavid du Colombier edalloc(void)
626*a81c3ea0SDavid du Colombier {
627*a81c3ea0SDavid du Colombier Ed *ed, *pool;
628*a81c3ea0SDavid du Colombier int i;
629*a81c3ea0SDavid du Colombier
630*a81c3ea0SDavid du Colombier lock(&edpool);
631*a81c3ea0SDavid du Colombier if(edpool.free == nil){
632*a81c3ea0SDavid du Colombier ddprint("ohci: edalloc %d Eds\n", Incr);
633*a81c3ea0SDavid du Colombier pool = xspanalloc(Incr*sizeof(Ed), Align, 0);
634*a81c3ea0SDavid du Colombier if(pool == nil)
635*a81c3ea0SDavid du Colombier panic("edalloc");
636*a81c3ea0SDavid du Colombier pool = KSEG1ADDR(pool); // XXX
637*a81c3ea0SDavid du Colombier for(i=Incr; --i>=0;){
638*a81c3ea0SDavid du Colombier pool[i].next = edpool.free;
639*a81c3ea0SDavid du Colombier edpool.free = &pool[i];
640*a81c3ea0SDavid du Colombier }
641*a81c3ea0SDavid du Colombier edpool.nalloc += Incr;
642*a81c3ea0SDavid du Colombier edpool.nfree += Incr;
643*a81c3ea0SDavid du Colombier }
644*a81c3ea0SDavid du Colombier edpool.ninuse++;
645*a81c3ea0SDavid du Colombier edpool.nfree--;
646*a81c3ea0SDavid du Colombier ed = edpool.free;
647*a81c3ea0SDavid du Colombier edpool.free = ed->next;
648*a81c3ea0SDavid du Colombier memset(ed, 0, sizeof(Ed));
649*a81c3ea0SDavid du Colombier unlock(&edpool);
650*a81c3ea0SDavid du Colombier
651*a81c3ea0SDavid du Colombier return ed;
652*a81c3ea0SDavid du Colombier }
653*a81c3ea0SDavid du Colombier
654*a81c3ea0SDavid du Colombier static void
edfree(Ed * ed)655*a81c3ea0SDavid du Colombier edfree(Ed *ed)
656*a81c3ea0SDavid du Colombier {
657*a81c3ea0SDavid du Colombier Td *td, *next;
658*a81c3ea0SDavid du Colombier int i;
659*a81c3ea0SDavid du Colombier
660*a81c3ea0SDavid du Colombier if(ed == 0)
661*a81c3ea0SDavid du Colombier return;
662*a81c3ea0SDavid du Colombier i = 0;
663*a81c3ea0SDavid du Colombier for(td = ed->tds; td != nil; td = next){
664*a81c3ea0SDavid du Colombier next = td->next;
665*a81c3ea0SDavid du Colombier tdfree(td);
666*a81c3ea0SDavid du Colombier if(i++ > 2000){
667*a81c3ea0SDavid du Colombier print("ohci: bug: ed with more than 2000 tds\n");
668*a81c3ea0SDavid du Colombier break;
669*a81c3ea0SDavid du Colombier }
670*a81c3ea0SDavid du Colombier }
671*a81c3ea0SDavid du Colombier lock(&edpool);
672*a81c3ea0SDavid du Colombier if(ed->nexted == 0x99999999)
673*a81c3ea0SDavid du Colombier panic("ohci: edfree: double free");
674*a81c3ea0SDavid du Colombier memset(ed, 9, sizeof(Ed)); /* poison */
675*a81c3ea0SDavid du Colombier ed->next = edpool.free;
676*a81c3ea0SDavid du Colombier edpool.free = ed;
677*a81c3ea0SDavid du Colombier edpool.ninuse--;
678*a81c3ea0SDavid du Colombier edpool.nfree++;
679*a81c3ea0SDavid du Colombier unlock(&edpool);
680*a81c3ea0SDavid du Colombier ddprint("edfree: ed %#p\n", ed);
681*a81c3ea0SDavid du Colombier }
682*a81c3ea0SDavid du Colombier
683*a81c3ea0SDavid du Colombier /*
684*a81c3ea0SDavid du Colombier * return smallest power of 2 >= n
685*a81c3ea0SDavid du Colombier */
686*a81c3ea0SDavid du Colombier static int
flog2(int n)687*a81c3ea0SDavid du Colombier flog2(int n)
688*a81c3ea0SDavid du Colombier {
689*a81c3ea0SDavid du Colombier int i;
690*a81c3ea0SDavid du Colombier
691*a81c3ea0SDavid du Colombier for(i = 0; (1 << i) < n; i++)
692*a81c3ea0SDavid du Colombier ;
693*a81c3ea0SDavid du Colombier return i;
694*a81c3ea0SDavid du Colombier }
695*a81c3ea0SDavid du Colombier
696*a81c3ea0SDavid du Colombier /*
697*a81c3ea0SDavid du Colombier * return smallest power of 2 <= n
698*a81c3ea0SDavid du Colombier */
699*a81c3ea0SDavid du Colombier static int
flog2lower(int n)700*a81c3ea0SDavid du Colombier flog2lower(int n)
701*a81c3ea0SDavid du Colombier {
702*a81c3ea0SDavid du Colombier int i;
703*a81c3ea0SDavid du Colombier
704*a81c3ea0SDavid du Colombier for(i = 0; (1 << (i + 1)) <= n; i++)
705*a81c3ea0SDavid du Colombier ;
706*a81c3ea0SDavid du Colombier return i;
707*a81c3ea0SDavid du Colombier }
708*a81c3ea0SDavid du Colombier
709*a81c3ea0SDavid du Colombier static int
pickschedq(Qtree * qt,int pollival,ulong bw,ulong limit)710*a81c3ea0SDavid du Colombier pickschedq(Qtree *qt, int pollival, ulong bw, ulong limit)
711*a81c3ea0SDavid du Colombier {
712*a81c3ea0SDavid du Colombier int i, j, d, upperb, q;
713*a81c3ea0SDavid du Colombier ulong best, worst, total;
714*a81c3ea0SDavid du Colombier
715*a81c3ea0SDavid du Colombier d = flog2lower(pollival);
716*a81c3ea0SDavid du Colombier if(d > qt->depth)
717*a81c3ea0SDavid du Colombier d = qt->depth;
718*a81c3ea0SDavid du Colombier q = -1;
719*a81c3ea0SDavid du Colombier worst = 0;
720*a81c3ea0SDavid du Colombier best = ~0;
721*a81c3ea0SDavid du Colombier upperb = (1 << (d+1)) - 1;
722*a81c3ea0SDavid du Colombier for(i = (1 << d) - 1; i < upperb; i++){
723*a81c3ea0SDavid du Colombier total = qt->bw[0];
724*a81c3ea0SDavid du Colombier for(j = i; j > 0; j = (j - 1) / 2)
725*a81c3ea0SDavid du Colombier total += qt->bw[j];
726*a81c3ea0SDavid du Colombier if(total < best){
727*a81c3ea0SDavid du Colombier best = total;
728*a81c3ea0SDavid du Colombier q = i;
729*a81c3ea0SDavid du Colombier }
730*a81c3ea0SDavid du Colombier if(total > worst)
731*a81c3ea0SDavid du Colombier worst = total;
732*a81c3ea0SDavid du Colombier }
733*a81c3ea0SDavid du Colombier if(worst + bw >= limit)
734*a81c3ea0SDavid du Colombier return -1;
735*a81c3ea0SDavid du Colombier return q;
736*a81c3ea0SDavid du Colombier }
737*a81c3ea0SDavid du Colombier
738*a81c3ea0SDavid du Colombier static int
schedq(Ctlr * ctlr,Qio * io,int pollival)739*a81c3ea0SDavid du Colombier schedq(Ctlr *ctlr, Qio *io, int pollival)
740*a81c3ea0SDavid du Colombier {
741*a81c3ea0SDavid du Colombier int q;
742*a81c3ea0SDavid du Colombier Ed *ted;
743*a81c3ea0SDavid du Colombier
744*a81c3ea0SDavid du Colombier q = pickschedq(ctlr->tree, pollival, io->bw, ~0);
745*a81c3ea0SDavid du Colombier ddqprint("ohci: sched %#p q %d, ival %d, bw %ld\n", io, q, pollival, io->bw);
746*a81c3ea0SDavid du Colombier if(q < 0){
747*a81c3ea0SDavid du Colombier print("ohci: no room for ed\n");
748*a81c3ea0SDavid du Colombier return -1;
749*a81c3ea0SDavid du Colombier }
750*a81c3ea0SDavid du Colombier ctlr->tree->bw[q] += io->bw;
751*a81c3ea0SDavid du Colombier ted = ctlr->tree->root[q];
752*a81c3ea0SDavid du Colombier io->sched = q;
753*a81c3ea0SDavid du Colombier edlinked(io->ed, ted->next);
754*a81c3ea0SDavid du Colombier edlinked(ted, io->ed);
755*a81c3ea0SDavid du Colombier io->ed->inext = ctlr->intrhd;
756*a81c3ea0SDavid du Colombier ctlr->intrhd = io->ed;
757*a81c3ea0SDavid du Colombier return 0;
758*a81c3ea0SDavid du Colombier }
759*a81c3ea0SDavid du Colombier
760*a81c3ea0SDavid du Colombier static void
unschedq(Ctlr * ctlr,Qio * qio)761*a81c3ea0SDavid du Colombier unschedq(Ctlr *ctlr, Qio *qio)
762*a81c3ea0SDavid du Colombier {
763*a81c3ea0SDavid du Colombier int q;
764*a81c3ea0SDavid du Colombier Ed *prev, *this, *next;
765*a81c3ea0SDavid du Colombier Ed **l;
766*a81c3ea0SDavid du Colombier
767*a81c3ea0SDavid du Colombier q = qio->sched;
768*a81c3ea0SDavid du Colombier if(q < 0)
769*a81c3ea0SDavid du Colombier return;
770*a81c3ea0SDavid du Colombier ctlr->tree->bw[q] -= qio->bw;
771*a81c3ea0SDavid du Colombier
772*a81c3ea0SDavid du Colombier prev = ctlr->tree->root[q];
773*a81c3ea0SDavid du Colombier this = prev->next;
774*a81c3ea0SDavid du Colombier while(this != nil && this != qio->ed){
775*a81c3ea0SDavid du Colombier prev = this;
776*a81c3ea0SDavid du Colombier this = this->next;
777*a81c3ea0SDavid du Colombier }
778*a81c3ea0SDavid du Colombier if(this == nil)
779*a81c3ea0SDavid du Colombier print("ohci: unschedq %d: not found\n", q);
780*a81c3ea0SDavid du Colombier else{
781*a81c3ea0SDavid du Colombier next = this->next;
782*a81c3ea0SDavid du Colombier edlinked(prev, next);
783*a81c3ea0SDavid du Colombier }
784*a81c3ea0SDavid du Colombier waitSOF(ctlr);
785*a81c3ea0SDavid du Colombier for(l = &ctlr->intrhd; *l != nil; l = &(*l)->inext)
786*a81c3ea0SDavid du Colombier if(*l == qio->ed){
787*a81c3ea0SDavid du Colombier *l = (*l)->inext;
788*a81c3ea0SDavid du Colombier return;
789*a81c3ea0SDavid du Colombier }
790*a81c3ea0SDavid du Colombier print("ohci: unschedq: ed %#p not found\n", qio->ed);
791*a81c3ea0SDavid du Colombier }
792*a81c3ea0SDavid du Colombier
793*a81c3ea0SDavid du Colombier static char*
seprinttdtok(char * s,char * e,int tok)794*a81c3ea0SDavid du Colombier seprinttdtok(char *s, char *e, int tok)
795*a81c3ea0SDavid du Colombier {
796*a81c3ea0SDavid du Colombier switch(tok){
797*a81c3ea0SDavid du Colombier case Tdtoksetup:
798*a81c3ea0SDavid du Colombier s = seprint(s, e, " setup");
799*a81c3ea0SDavid du Colombier break;
800*a81c3ea0SDavid du Colombier case Tdtokin:
801*a81c3ea0SDavid du Colombier s = seprint(s, e, " in");
802*a81c3ea0SDavid du Colombier break;
803*a81c3ea0SDavid du Colombier case Tdtokout:
804*a81c3ea0SDavid du Colombier s = seprint(s, e, " out");
805*a81c3ea0SDavid du Colombier break;
806*a81c3ea0SDavid du Colombier }
807*a81c3ea0SDavid du Colombier return s;
808*a81c3ea0SDavid du Colombier }
809*a81c3ea0SDavid du Colombier
810*a81c3ea0SDavid du Colombier
811*a81c3ea0SDavid du Colombier static char*
seprinttd(char * s,char * e,Td * td,int iso)812*a81c3ea0SDavid du Colombier seprinttd(char *s, char *e, Td *td, int iso)
813*a81c3ea0SDavid du Colombier {
814*a81c3ea0SDavid du Colombier int i;
815*a81c3ea0SDavid du Colombier Block *bp;
816*a81c3ea0SDavid du Colombier
817*a81c3ea0SDavid du Colombier if(td == nil)
818*a81c3ea0SDavid du Colombier return seprint(s, e, "<nil td>\n");
819*a81c3ea0SDavid du Colombier s = seprint(s, e, "%#p ep %#p ctrl %#p", td, td->ep, td->ctrl);
820*a81c3ea0SDavid du Colombier s = seprint(s, e, " cc=%#ulx", (td->ctrl >> Tdccshift) & Tdccmask);
821*a81c3ea0SDavid du Colombier if(iso == 0){
822*a81c3ea0SDavid du Colombier if((td->ctrl & Tdround) != 0)
823*a81c3ea0SDavid du Colombier s = seprint(s, e, " rnd");
824*a81c3ea0SDavid du Colombier s = seprinttdtok(s, e, td->ctrl & Tdtokmask);
825*a81c3ea0SDavid du Colombier if((td->ctrl & Tdusetog) != 0)
826*a81c3ea0SDavid du Colombier s = seprint(s, e, " d%d", (td->ctrl & Tddata1) ? 1 : 0);
827*a81c3ea0SDavid du Colombier else
828*a81c3ea0SDavid du Colombier s = seprint(s, e, " d-");
829*a81c3ea0SDavid du Colombier s = seprint(s, e, " ec=%uld", (td->ctrl >> Tderrshift) & Tderrmask);
830*a81c3ea0SDavid du Colombier }else{
831*a81c3ea0SDavid du Colombier s = seprint(s, e, " fc=%uld", (td->ctrl >> Tdfcshift) & Tdfcmask);
832*a81c3ea0SDavid du Colombier s = seprint(s, e, " sf=%uld", td->ctrl & Tdsfmask);
833*a81c3ea0SDavid du Colombier }
834*a81c3ea0SDavid du Colombier s = seprint(s, e, " cbp0 %#p cbp %#p next %#p be %#p %s",
835*a81c3ea0SDavid du Colombier td->cbp0, td->cbp, td->nexttd, td->be, td->last ? "last" : "");
836*a81c3ea0SDavid du Colombier s = seprint(s, e, "\n\t\t%ld bytes", td->nbytes);
837*a81c3ea0SDavid du Colombier if((bp = td->bp) != nil){
838*a81c3ea0SDavid du Colombier s = seprint(s, e, " rp %#p wp %#p ", bp->rp, bp->wp);
839*a81c3ea0SDavid du Colombier if(BLEN(bp) > 0)
840*a81c3ea0SDavid du Colombier s = seprintdata(s, e, bp->rp, bp->wp - bp->rp);
841*a81c3ea0SDavid du Colombier }
842*a81c3ea0SDavid du Colombier if(iso == 0)
843*a81c3ea0SDavid du Colombier return seprint(s, e, "\n");
844*a81c3ea0SDavid du Colombier s = seprint(s, e, "\n\t\t");
845*a81c3ea0SDavid du Colombier /* we use only offsets[0] */
846*a81c3ea0SDavid du Colombier i = 0;
847*a81c3ea0SDavid du Colombier s = seprint(s, e, "[%d] %#ux cc=%#ux sz=%ud\n", i, td->offsets[i],
848*a81c3ea0SDavid du Colombier (td->offsets[i] >> Tdiccshift) & Tdiccmask,
849*a81c3ea0SDavid du Colombier td->offsets[i] & 0x7FF);
850*a81c3ea0SDavid du Colombier return s;
851*a81c3ea0SDavid du Colombier }
852*a81c3ea0SDavid du Colombier
853*a81c3ea0SDavid du Colombier static void
dumptd(Td * td,char * p,int iso)854*a81c3ea0SDavid du Colombier dumptd(Td *td, char *p, int iso)
855*a81c3ea0SDavid du Colombier {
856*a81c3ea0SDavid du Colombier static char buf[512]; /* Too much */
857*a81c3ea0SDavid du Colombier char *s;
858*a81c3ea0SDavid du Colombier
859*a81c3ea0SDavid du Colombier s = seprint(buf, buf+sizeof(buf), "%s: ", p);
860*a81c3ea0SDavid du Colombier s = seprinttd(s, buf+sizeof(buf), td, iso);
861*a81c3ea0SDavid du Colombier if(s > buf && s[-1] != '\n')
862*a81c3ea0SDavid du Colombier s[-1] = '\n';
863*a81c3ea0SDavid du Colombier print("\t%s", buf);
864*a81c3ea0SDavid du Colombier }
865*a81c3ea0SDavid du Colombier
866*a81c3ea0SDavid du Colombier static void
dumptds(Td * td,char * p,int iso)867*a81c3ea0SDavid du Colombier dumptds(Td *td, char *p, int iso)
868*a81c3ea0SDavid du Colombier {
869*a81c3ea0SDavid du Colombier int i;
870*a81c3ea0SDavid du Colombier
871*a81c3ea0SDavid du Colombier for(i = 0; td != nil; td = td->next){
872*a81c3ea0SDavid du Colombier dumptd(td, p, iso);
873*a81c3ea0SDavid du Colombier if(td->last)
874*a81c3ea0SDavid du Colombier break;
875*a81c3ea0SDavid du Colombier if(tdtok(td) == Tdtokin && ++i > 2){
876*a81c3ea0SDavid du Colombier print("\t\t...\n");
877*a81c3ea0SDavid du Colombier break;
878*a81c3ea0SDavid du Colombier }
879*a81c3ea0SDavid du Colombier }
880*a81c3ea0SDavid du Colombier }
881*a81c3ea0SDavid du Colombier
882*a81c3ea0SDavid du Colombier static void
dumped(Ed * ed)883*a81c3ea0SDavid du Colombier dumped(Ed *ed)
884*a81c3ea0SDavid du Colombier {
885*a81c3ea0SDavid du Colombier char *buf, *s, *e;
886*a81c3ea0SDavid du Colombier
887*a81c3ea0SDavid du Colombier if(ed == nil){
888*a81c3ea0SDavid du Colombier print("<null ed>\n");
889*a81c3ea0SDavid du Colombier return;
890*a81c3ea0SDavid du Colombier }
891*a81c3ea0SDavid du Colombier buf = malloc(512);
892*a81c3ea0SDavid du Colombier /* no waserror; may want to use from interrupt context */
893*a81c3ea0SDavid du Colombier if(buf == nil)
894*a81c3ea0SDavid du Colombier return;
895*a81c3ea0SDavid du Colombier e = buf+512;
896*a81c3ea0SDavid du Colombier s = seprint(buf, e, "\ted %#p: ctrl %#p", ed, ed->ctrl);
897*a81c3ea0SDavid du Colombier if((ed->ctrl & Edskip) != 0)
898*a81c3ea0SDavid du Colombier s = seprint(s, e, " skip");
899*a81c3ea0SDavid du Colombier if((ed->ctrl & Ediso) != 0)
900*a81c3ea0SDavid du Colombier s = seprint(s, e, " iso");
901*a81c3ea0SDavid du Colombier if((ed->ctrl & Edlow) != 0)
902*a81c3ea0SDavid du Colombier s = seprint(s, e, " low");
903*a81c3ea0SDavid du Colombier s = seprint(s, e, " d%d", (ed->head & Edtoggle) ? 1 : 0);
904*a81c3ea0SDavid du Colombier if((ed->ctrl & Eddirmask) == Edin)
905*a81c3ea0SDavid du Colombier s = seprint(s, e, " in");
906*a81c3ea0SDavid du Colombier if((ed->ctrl & Eddirmask) == Edout)
907*a81c3ea0SDavid du Colombier s = seprint(s, e, " out");
908*a81c3ea0SDavid du Colombier if(edhalted(ed))
909*a81c3ea0SDavid du Colombier s = seprint(s, e, " hlt");
910*a81c3ea0SDavid du Colombier s = seprint(s, e, " ep%uld.%uld", (ed->ctrl>>7)&Epmax, ed->ctrl&0x7f);
911*a81c3ea0SDavid du Colombier s = seprint(s, e, " maxpkt %uld", (ed->ctrl>>Edmpsshift)&Edmpsmask);
912*a81c3ea0SDavid du Colombier seprint(s, e, " tail %#p head %#p next %#p\n",ed->tail,ed->head,ed->nexted);
913*a81c3ea0SDavid du Colombier print("%s", buf);
914*a81c3ea0SDavid du Colombier free(buf);
915*a81c3ea0SDavid du Colombier if(ed->tds != nil && (ed->ctrl & Ediso) == 0)
916*a81c3ea0SDavid du Colombier dumptds(ed->tds, "td", 0);
917*a81c3ea0SDavid du Colombier }
918*a81c3ea0SDavid du Colombier
919*a81c3ea0SDavid du Colombier static char*
seprintio(char * s,char * e,Qio * io,char * pref)920*a81c3ea0SDavid du Colombier seprintio(char *s, char *e, Qio *io, char *pref)
921*a81c3ea0SDavid du Colombier {
922*a81c3ea0SDavid du Colombier s = seprint(s, e, "%s qio %#p ed %#p", pref, io, io->ed);
923*a81c3ea0SDavid du Colombier s = seprint(s, e, " tog %d iot %ld err %s id %#ulx",
924*a81c3ea0SDavid du Colombier io->toggle, io->iotime, io->err, io->usbid);
925*a81c3ea0SDavid du Colombier s = seprinttdtok(s, e, io->tok);
926*a81c3ea0SDavid du Colombier s = seprint(s, e, " %s\n", iosname[io->state]);
927*a81c3ea0SDavid du Colombier return s;
928*a81c3ea0SDavid du Colombier }
929*a81c3ea0SDavid du Colombier
930*a81c3ea0SDavid du Colombier static char*
seprintep(char * s,char * e,Ep * ep)931*a81c3ea0SDavid du Colombier seprintep(char* s, char* e, Ep *ep)
932*a81c3ea0SDavid du Colombier {
933*a81c3ea0SDavid du Colombier Isoio *iso;
934*a81c3ea0SDavid du Colombier Qio *io;
935*a81c3ea0SDavid du Colombier Ctlio *cio;
936*a81c3ea0SDavid du Colombier
937*a81c3ea0SDavid du Colombier if(ep == nil)
938*a81c3ea0SDavid du Colombier return seprint(s, e, "<nil ep>\n");
939*a81c3ea0SDavid du Colombier if(ep->aux == nil)
940*a81c3ea0SDavid du Colombier return seprint(s, e, "no mdep\n");
941*a81c3ea0SDavid du Colombier switch(ep->ttype){
942*a81c3ea0SDavid du Colombier case Tctl:
943*a81c3ea0SDavid du Colombier cio = ep->aux;
944*a81c3ea0SDavid du Colombier s = seprintio(s, e, cio, "c");
945*a81c3ea0SDavid du Colombier s = seprint(s, e, "\trepl %d ndata %d\n", ep->rhrepl, cio->ndata);
946*a81c3ea0SDavid du Colombier break;
947*a81c3ea0SDavid du Colombier case Tbulk:
948*a81c3ea0SDavid du Colombier case Tintr:
949*a81c3ea0SDavid du Colombier io = ep->aux;
950*a81c3ea0SDavid du Colombier if(ep->mode != OWRITE)
951*a81c3ea0SDavid du Colombier s = seprintio(s, e, &io[OREAD], "r");
952*a81c3ea0SDavid du Colombier if(ep->mode != OREAD)
953*a81c3ea0SDavid du Colombier s = seprintio(s, e, &io[OWRITE], "w");
954*a81c3ea0SDavid du Colombier break;
955*a81c3ea0SDavid du Colombier case Tiso:
956*a81c3ea0SDavid du Colombier iso = ep->aux;
957*a81c3ea0SDavid du Colombier s = seprintio(s, e, iso, "w");
958*a81c3ea0SDavid du Colombier s = seprint(s, e, "\tntds %d avail %d frno %uld left %uld next avail %#p\n",
959*a81c3ea0SDavid du Colombier iso->nframes, iso->navail, iso->frno, iso->left, iso->atds);
960*a81c3ea0SDavid du Colombier break;
961*a81c3ea0SDavid du Colombier }
962*a81c3ea0SDavid du Colombier return s;
963*a81c3ea0SDavid du Colombier }
964*a81c3ea0SDavid du Colombier
965*a81c3ea0SDavid du Colombier static char*
seprintctl(char * s,char * se,ulong ctl)966*a81c3ea0SDavid du Colombier seprintctl(char *s, char *se, ulong ctl)
967*a81c3ea0SDavid du Colombier {
968*a81c3ea0SDavid du Colombier s = seprint(s, se, "en=");
969*a81c3ea0SDavid du Colombier if((ctl&Cple) != 0)
970*a81c3ea0SDavid du Colombier s = seprint(s, se, "p");
971*a81c3ea0SDavid du Colombier if((ctl&Cie) != 0)
972*a81c3ea0SDavid du Colombier s = seprint(s, se, "i");
973*a81c3ea0SDavid du Colombier if((ctl&Ccle) != 0)
974*a81c3ea0SDavid du Colombier s = seprint(s, se, "c");
975*a81c3ea0SDavid du Colombier if((ctl&Cble) != 0)
976*a81c3ea0SDavid du Colombier s = seprint(s, se, "b");
977*a81c3ea0SDavid du Colombier switch(ctl & Cfsmask){
978*a81c3ea0SDavid du Colombier case Cfsreset:
979*a81c3ea0SDavid du Colombier return seprint(s, se, " reset");
980*a81c3ea0SDavid du Colombier case Cfsresume:
981*a81c3ea0SDavid du Colombier return seprint(s, se, " resume");
982*a81c3ea0SDavid du Colombier case Cfsoper:
983*a81c3ea0SDavid du Colombier return seprint(s, se, " run");
984*a81c3ea0SDavid du Colombier case Cfssuspend:
985*a81c3ea0SDavid du Colombier return seprint(s, se, " suspend");
986*a81c3ea0SDavid du Colombier default:
987*a81c3ea0SDavid du Colombier return seprint(s, se, " ???");
988*a81c3ea0SDavid du Colombier }
989*a81c3ea0SDavid du Colombier }
990*a81c3ea0SDavid du Colombier
991*a81c3ea0SDavid du Colombier static void
dump(Hci * hp)992*a81c3ea0SDavid du Colombier dump(Hci *hp)
993*a81c3ea0SDavid du Colombier {
994*a81c3ea0SDavid du Colombier Ctlr *ctlr;
995*a81c3ea0SDavid du Colombier Ed *ed;
996*a81c3ea0SDavid du Colombier char cs[20];
997*a81c3ea0SDavid du Colombier
998*a81c3ea0SDavid du Colombier ctlr = hp->aux;
999*a81c3ea0SDavid du Colombier ilock(ctlr);
1000*a81c3ea0SDavid du Colombier seprintctl(cs, cs+sizeof(cs), ctlr->ohci->control);
1001*a81c3ea0SDavid du Colombier print("ohci ctlr %#p: frno %#ux ctl %#lux %s sts %#lux intr %#lux\n",
1002*a81c3ea0SDavid du Colombier ctlr, ctlr->hcca->framenumber, ctlr->ohci->control, cs,
1003*a81c3ea0SDavid du Colombier ctlr->ohci->cmdsts, ctlr->ohci->intrsts);
1004*a81c3ea0SDavid du Colombier print("ctlhd %#ulx cur %#ulx bulkhd %#ulx cur %#ulx done %#ulx\n",
1005*a81c3ea0SDavid du Colombier ctlr->ohci->ctlheaded, ctlr->ohci->ctlcurred,
1006*a81c3ea0SDavid du Colombier ctlr->ohci->bulkheaded, ctlr->ohci->bulkcurred,
1007*a81c3ea0SDavid du Colombier ctlr->ohci->donehead);
1008*a81c3ea0SDavid du Colombier if(ctlhd(ctlr) != nil)
1009*a81c3ea0SDavid du Colombier print("[ctl]\n");
1010*a81c3ea0SDavid du Colombier for(ed = ctlhd(ctlr); ed != nil; ed = ed->next)
1011*a81c3ea0SDavid du Colombier dumped(ed);
1012*a81c3ea0SDavid du Colombier if(bulkhd(ctlr) != nil)
1013*a81c3ea0SDavid du Colombier print("[bulk]\n");
1014*a81c3ea0SDavid du Colombier for(ed = bulkhd(ctlr); ed != nil; ed = ed->next)
1015*a81c3ea0SDavid du Colombier dumped(ed);
1016*a81c3ea0SDavid du Colombier if(ctlr->intrhd != nil)
1017*a81c3ea0SDavid du Colombier print("[intr]\n");
1018*a81c3ea0SDavid du Colombier for(ed = ctlr->intrhd; ed != nil; ed = ed->inext)
1019*a81c3ea0SDavid du Colombier dumped(ed);
1020*a81c3ea0SDavid du Colombier if(ctlr->tree->root[0]->next != nil)
1021*a81c3ea0SDavid du Colombier print("[iso]");
1022*a81c3ea0SDavid du Colombier for(ed = ctlr->tree->root[0]->next; ed != nil; ed = ed->next)
1023*a81c3ea0SDavid du Colombier dumped(ed);
1024*a81c3ea0SDavid du Colombier print("%d eds in tree\n", ctlr->ntree);
1025*a81c3ea0SDavid du Colombier iunlock(ctlr);
1026*a81c3ea0SDavid du Colombier lock(&tdpool);
1027*a81c3ea0SDavid du Colombier print("%d tds allocated = %d in use + %d free\n",
1028*a81c3ea0SDavid du Colombier tdpool.nalloc, tdpool.ninuse, tdpool.nfree);
1029*a81c3ea0SDavid du Colombier unlock(&tdpool);
1030*a81c3ea0SDavid du Colombier lock(&edpool);
1031*a81c3ea0SDavid du Colombier print("%d eds allocated = %d in use + %d free\n",
1032*a81c3ea0SDavid du Colombier edpool.nalloc, edpool.ninuse, edpool.nfree);
1033*a81c3ea0SDavid du Colombier unlock(&edpool);
1034*a81c3ea0SDavid du Colombier }
1035*a81c3ea0SDavid du Colombier
1036*a81c3ea0SDavid du Colombier /*
1037*a81c3ea0SDavid du Colombier * Compute size for the next iso Td and setup its
1038*a81c3ea0SDavid du Colombier * descriptor for I/O according to the buffer size.
1039*a81c3ea0SDavid du Colombier */
1040*a81c3ea0SDavid du Colombier static void
isodtdinit(Ep * ep,Isoio * iso,Td * td)1041*a81c3ea0SDavid du Colombier isodtdinit(Ep *ep, Isoio *iso, Td *td)
1042*a81c3ea0SDavid du Colombier {
1043*a81c3ea0SDavid du Colombier Block *bp;
1044*a81c3ea0SDavid du Colombier long size;
1045*a81c3ea0SDavid du Colombier int i;
1046*a81c3ea0SDavid du Colombier
1047*a81c3ea0SDavid du Colombier bp = td->bp;
1048*a81c3ea0SDavid du Colombier assert(bp != nil && BLEN(bp) == 0);
1049*a81c3ea0SDavid du Colombier size = (ep->hz+iso->left) * ep->pollival / 1000;
1050*a81c3ea0SDavid du Colombier iso->left = (ep->hz+iso->left) * ep->pollival % 1000;
1051*a81c3ea0SDavid du Colombier size *= ep->samplesz;
1052*a81c3ea0SDavid du Colombier if(size > ep->maxpkt){
1053*a81c3ea0SDavid du Colombier print("ohci: ep%d.%d: size > maxpkt\n",
1054*a81c3ea0SDavid du Colombier ep->dev->nb, ep->nb);
1055*a81c3ea0SDavid du Colombier print("size = %uld max = %ld\n", size, ep->maxpkt);
1056*a81c3ea0SDavid du Colombier size = ep->maxpkt;
1057*a81c3ea0SDavid du Colombier }
1058*a81c3ea0SDavid du Colombier td->nbytes = size;
1059*a81c3ea0SDavid du Colombier memset(KSEG1ADDR(bp->wp), 0, size); /* in case we don't fill it on time */
1060*a81c3ea0SDavid du Colombier td->cbp0 = td->cbp = ptr2pa(bp->rp) & ~0xFFF;
1061*a81c3ea0SDavid du Colombier td->ctrl = TRUNC(iso->frno, Ntdframes);
1062*a81c3ea0SDavid du Colombier td->offsets[0] = (ptr2pa(bp->rp) & 0xFFF);
1063*a81c3ea0SDavid du Colombier td->offsets[0] |= (Tdnotacc << Tdiccshift);
1064*a81c3ea0SDavid du Colombier /* in case the controller checks out the offests... */
1065*a81c3ea0SDavid du Colombier for(i = 1; i < nelem(td->offsets); i++)
1066*a81c3ea0SDavid du Colombier td->offsets[i] = td->offsets[0];
1067*a81c3ea0SDavid du Colombier td->be = ptr2pa(bp->rp + size - 1);
1068*a81c3ea0SDavid du Colombier td->ctrl |= (0 << Tdfcshift); /* frame count is 1 */
1069*a81c3ea0SDavid du Colombier
1070*a81c3ea0SDavid du Colombier iso->frno = TRUNC(iso->frno + ep->pollival, Ntdframes);
1071*a81c3ea0SDavid du Colombier }
1072*a81c3ea0SDavid du Colombier
1073*a81c3ea0SDavid du Colombier /*
1074*a81c3ea0SDavid du Colombier * start I/O on the dummy td and setup a new dummy to fill up.
1075*a81c3ea0SDavid du Colombier */
1076*a81c3ea0SDavid du Colombier static void
isoadvance(Ep * ep,Isoio * iso,Td * td)1077*a81c3ea0SDavid du Colombier isoadvance(Ep *ep, Isoio *iso, Td *td)
1078*a81c3ea0SDavid du Colombier {
1079*a81c3ea0SDavid du Colombier Td *dtd;
1080*a81c3ea0SDavid du Colombier
1081*a81c3ea0SDavid du Colombier dtd = iso->atds;
1082*a81c3ea0SDavid du Colombier iso->atds = dtd->anext;
1083*a81c3ea0SDavid du Colombier iso->navail--;
1084*a81c3ea0SDavid du Colombier dtd->anext = nil;
1085*a81c3ea0SDavid du Colombier dtd->bp->wp = dtd->bp->rp;
1086*a81c3ea0SDavid du Colombier dtd->nexttd = 0;
1087*a81c3ea0SDavid du Colombier td->nexttd = ptr2pa(dtd);
1088*a81c3ea0SDavid du Colombier isodtdinit(ep, iso, dtd);
1089*a81c3ea0SDavid du Colombier iso->ed->tail = ptr2pa(dtd);
1090*a81c3ea0SDavid du Colombier }
1091*a81c3ea0SDavid du Colombier
1092*a81c3ea0SDavid du Colombier static int
isocanwrite(void * a)1093*a81c3ea0SDavid du Colombier isocanwrite(void *a)
1094*a81c3ea0SDavid du Colombier {
1095*a81c3ea0SDavid du Colombier Isoio *iso;
1096*a81c3ea0SDavid du Colombier
1097*a81c3ea0SDavid du Colombier iso = a;
1098*a81c3ea0SDavid du Colombier return iso->state == Qclose || iso->err != nil ||
1099*a81c3ea0SDavid du Colombier iso->navail > iso->nframes / 2;
1100*a81c3ea0SDavid du Colombier }
1101*a81c3ea0SDavid du Colombier
1102*a81c3ea0SDavid du Colombier /*
1103*a81c3ea0SDavid du Colombier * Service a completed/failed Td from the done queue.
1104*a81c3ea0SDavid du Colombier * It may be of any transfer type.
1105*a81c3ea0SDavid du Colombier * The queue is not in completion order.
1106*a81c3ea0SDavid du Colombier * (It's actually in reverse completion order).
1107*a81c3ea0SDavid du Colombier *
1108*a81c3ea0SDavid du Colombier * When an error, a short packet, or a last Td is found
1109*a81c3ea0SDavid du Colombier * we awake the process waiting for the transfer.
1110*a81c3ea0SDavid du Colombier * Although later we will process other Tds completed
1111*a81c3ea0SDavid du Colombier * before, epio won't be able to touch the current Td
1112*a81c3ea0SDavid du Colombier * until interrupt returns and releases the lock on the
1113*a81c3ea0SDavid du Colombier * controller.
1114*a81c3ea0SDavid du Colombier */
1115*a81c3ea0SDavid du Colombier static void
qhinterrupt(Ctlr *,Ep * ep,Qio * io,Td * td,int)1116*a81c3ea0SDavid du Colombier qhinterrupt(Ctlr *, Ep *ep, Qio *io, Td *td, int)
1117*a81c3ea0SDavid du Colombier {
1118*a81c3ea0SDavid du Colombier Block *bp;
1119*a81c3ea0SDavid du Colombier int mode, err;
1120*a81c3ea0SDavid du Colombier Ed *ed;
1121*a81c3ea0SDavid du Colombier
1122*a81c3ea0SDavid du Colombier ed = io->ed;
1123*a81c3ea0SDavid du Colombier if(io->state != Qrun)
1124*a81c3ea0SDavid du Colombier return;
1125*a81c3ea0SDavid du Colombier if(tdtok(td) == Tdtokin)
1126*a81c3ea0SDavid du Colombier mode = OREAD;
1127*a81c3ea0SDavid du Colombier else
1128*a81c3ea0SDavid du Colombier mode = OWRITE;
1129*a81c3ea0SDavid du Colombier bp = td->bp;
1130*a81c3ea0SDavid du Colombier err = tderrs(td);
1131*a81c3ea0SDavid du Colombier
1132*a81c3ea0SDavid du Colombier switch(err){
1133*a81c3ea0SDavid du Colombier case Tddataovr: /* Overrun is not an error */
1134*a81c3ea0SDavid du Colombier break;
1135*a81c3ea0SDavid du Colombier case Tdok:
1136*a81c3ea0SDavid du Colombier /* virtualbox doesn't always report underflow on short packets */
1137*a81c3ea0SDavid du Colombier if(td->cbp == 0)
1138*a81c3ea0SDavid du Colombier break;
1139*a81c3ea0SDavid du Colombier /* fall through */
1140*a81c3ea0SDavid du Colombier case Tddataund:
1141*a81c3ea0SDavid du Colombier /* short input packets are ok */
1142*a81c3ea0SDavid du Colombier if(mode == OREAD){
1143*a81c3ea0SDavid du Colombier if(td->cbp == 0)
1144*a81c3ea0SDavid du Colombier panic("ohci: short packet but cbp == 0");
1145*a81c3ea0SDavid du Colombier /*
1146*a81c3ea0SDavid du Colombier * td->cbp and td->cbp0 are the real addresses
1147*a81c3ea0SDavid du Colombier * corresponding to virtual addresses bp->wp and
1148*a81c3ea0SDavid du Colombier * bp->rp respectively.
1149*a81c3ea0SDavid du Colombier */
1150*a81c3ea0SDavid du Colombier bp->wp = bp->rp + (td->cbp - td->cbp0);
1151*a81c3ea0SDavid du Colombier if(bp->wp < bp->rp)
1152*a81c3ea0SDavid du Colombier panic("ohci: wp < rp");
1153*a81c3ea0SDavid du Colombier /*
1154*a81c3ea0SDavid du Colombier * It's ok. clear error and flag as last in xfer.
1155*a81c3ea0SDavid du Colombier * epio must ignore following Tds.
1156*a81c3ea0SDavid du Colombier */
1157*a81c3ea0SDavid du Colombier td->last = 1;
1158*a81c3ea0SDavid du Colombier td->ctrl &= ~(Tdccmask << Tdccshift);
1159*a81c3ea0SDavid du Colombier break;
1160*a81c3ea0SDavid du Colombier }
1161*a81c3ea0SDavid du Colombier /* else fall; it's an error */
1162*a81c3ea0SDavid du Colombier case Tdcrc:
1163*a81c3ea0SDavid du Colombier case Tdbitstuff:
1164*a81c3ea0SDavid du Colombier case Tdbadtog:
1165*a81c3ea0SDavid du Colombier case Tdstalled:
1166*a81c3ea0SDavid du Colombier case Tdtmout:
1167*a81c3ea0SDavid du Colombier case Tdpidchk:
1168*a81c3ea0SDavid du Colombier case Tdbadpid:
1169*a81c3ea0SDavid du Colombier bp->wp = bp->rp; /* no bytes in xfer. */
1170*a81c3ea0SDavid du Colombier io->err = errmsg(err);
1171*a81c3ea0SDavid du Colombier if(debug || ep->debug){
1172*a81c3ea0SDavid du Colombier print("tdinterrupt: failed err %d (%s)\n", err, io->err);
1173*a81c3ea0SDavid du Colombier dumptd(td, "failed", ed->ctrl & Ediso);
1174*a81c3ea0SDavid du Colombier }
1175*a81c3ea0SDavid du Colombier td->last = 1;
1176*a81c3ea0SDavid du Colombier break;
1177*a81c3ea0SDavid du Colombier default:
1178*a81c3ea0SDavid du Colombier panic("ohci: td cc %ud unknown", err);
1179*a81c3ea0SDavid du Colombier }
1180*a81c3ea0SDavid du Colombier
1181*a81c3ea0SDavid du Colombier if(td->last != 0){
1182*a81c3ea0SDavid du Colombier /*
1183*a81c3ea0SDavid du Colombier * clear td list and halt flag.
1184*a81c3ea0SDavid du Colombier */
1185*a81c3ea0SDavid du Colombier ed->head = (ed->head & Edtoggle) | ed->tail;
1186*a81c3ea0SDavid du Colombier ed->tds = pa2ptr(ed->tail);
1187*a81c3ea0SDavid du Colombier io->state = Qdone;
1188*a81c3ea0SDavid du Colombier wakeup(io);
1189*a81c3ea0SDavid du Colombier }
1190*a81c3ea0SDavid du Colombier }
1191*a81c3ea0SDavid du Colombier
1192*a81c3ea0SDavid du Colombier /*
1193*a81c3ea0SDavid du Colombier * BUG: Iso input streams are not implemented.
1194*a81c3ea0SDavid du Colombier */
1195*a81c3ea0SDavid du Colombier static void
isointerrupt(Ctlr * ctlr,Ep * ep,Qio * io,Td * td,int)1196*a81c3ea0SDavid du Colombier isointerrupt(Ctlr *ctlr, Ep *ep, Qio *io, Td *td, int)
1197*a81c3ea0SDavid du Colombier {
1198*a81c3ea0SDavid du Colombier Isoio *iso;
1199*a81c3ea0SDavid du Colombier Block *bp;
1200*a81c3ea0SDavid du Colombier Ed *ed;
1201*a81c3ea0SDavid du Colombier int err, isoerr;
1202*a81c3ea0SDavid du Colombier
1203*a81c3ea0SDavid du Colombier iso = ep->aux;
1204*a81c3ea0SDavid du Colombier ed = io->ed;
1205*a81c3ea0SDavid du Colombier if(io->state == Qclose)
1206*a81c3ea0SDavid du Colombier return;
1207*a81c3ea0SDavid du Colombier bp = td->bp;
1208*a81c3ea0SDavid du Colombier /*
1209*a81c3ea0SDavid du Colombier * When we get more than half the frames consecutive errors
1210*a81c3ea0SDavid du Colombier * we signal an actual error. Errors in the entire Td are
1211*a81c3ea0SDavid du Colombier * more serious and are always singaled.
1212*a81c3ea0SDavid du Colombier * Errors like overrun are not really errors. In fact, for
1213*a81c3ea0SDavid du Colombier * output, errors cannot be really detected. The driver will
1214*a81c3ea0SDavid du Colombier * hopefully notice I/O errors on input endpoints and detach the device.
1215*a81c3ea0SDavid du Colombier */
1216*a81c3ea0SDavid du Colombier err = tderrs(td);
1217*a81c3ea0SDavid du Colombier isoerr = (td->offsets[0] >> Tdiccshift) & Tdiccmask;
1218*a81c3ea0SDavid du Colombier if(isoerr == Tdok || isoerr == Tdnotacc)
1219*a81c3ea0SDavid du Colombier iso->nerrs = 0;
1220*a81c3ea0SDavid du Colombier else if(iso->nerrs++ > iso->nframes/2)
1221*a81c3ea0SDavid du Colombier err = Tdstalled;
1222*a81c3ea0SDavid du Colombier if(err != Tdok && err != Tddataovr){
1223*a81c3ea0SDavid du Colombier bp->wp = bp->rp;
1224*a81c3ea0SDavid du Colombier io->err = errmsg(err);
1225*a81c3ea0SDavid du Colombier if(debug || ep->debug){
1226*a81c3ea0SDavid du Colombier print("ohci: isointerrupt: ep%d.%d: err %d (%s) frnum 0x%lux\n",
1227*a81c3ea0SDavid du Colombier ep->dev->nb, ep->nb,
1228*a81c3ea0SDavid du Colombier err, errmsg(err), ctlr->ohci->fmnumber);
1229*a81c3ea0SDavid du Colombier dumptd(td, "failed", ed->ctrl & Ediso);
1230*a81c3ea0SDavid du Colombier }
1231*a81c3ea0SDavid du Colombier }
1232*a81c3ea0SDavid du Colombier td->bp->wp = td->bp->rp;
1233*a81c3ea0SDavid du Colombier td->nbytes = 0;
1234*a81c3ea0SDavid du Colombier td->anext = iso->atds;
1235*a81c3ea0SDavid du Colombier iso->atds = td;
1236*a81c3ea0SDavid du Colombier iso->navail++;
1237*a81c3ea0SDavid du Colombier /*
1238*a81c3ea0SDavid du Colombier * If almost all Tds are avail the user is not doing I/O at the
1239*a81c3ea0SDavid du Colombier * required rate. We put another Td in place to keep the polling rate.
1240*a81c3ea0SDavid du Colombier */
1241*a81c3ea0SDavid du Colombier if(iso->err == nil && iso->navail > iso->nframes - 10)
1242*a81c3ea0SDavid du Colombier isoadvance(ep, iso, pa2ptr(iso->ed->tail));
1243*a81c3ea0SDavid du Colombier /*
1244*a81c3ea0SDavid du Colombier * If there's enough buffering futher I/O can be done.
1245*a81c3ea0SDavid du Colombier */
1246*a81c3ea0SDavid du Colombier if(isocanwrite(iso))
1247*a81c3ea0SDavid du Colombier wakeup(iso);
1248*a81c3ea0SDavid du Colombier }
1249*a81c3ea0SDavid du Colombier
1250*a81c3ea0SDavid du Colombier static void
interrupt(Ureg *,void * arg)1251*a81c3ea0SDavid du Colombier interrupt(Ureg *, void *arg)
1252*a81c3ea0SDavid du Colombier {
1253*a81c3ea0SDavid du Colombier Td *td, *ntd;
1254*a81c3ea0SDavid du Colombier Hci *hp;
1255*a81c3ea0SDavid du Colombier Ctlr *ctlr;
1256*a81c3ea0SDavid du Colombier ulong status, curred;
1257*a81c3ea0SDavid du Colombier int i, frno;
1258*a81c3ea0SDavid du Colombier
1259*a81c3ea0SDavid du Colombier hp = arg;
1260*a81c3ea0SDavid du Colombier ctlr = hp->aux;
1261*a81c3ea0SDavid du Colombier ilock(ctlr);
1262*a81c3ea0SDavid du Colombier ctlr->ohci->intrdisable = Mie;
1263*a81c3ea0SDavid du Colombier coherence();
1264*a81c3ea0SDavid du Colombier status = ctlr->ohci->intrsts & ctlr->ohci->intrenable;
1265*a81c3ea0SDavid du Colombier status &= Oc|Rhsc|Fno|Ue|Rd|Sf|Wdh|So;
1266*a81c3ea0SDavid du Colombier frno = TRUNC(ctlr->ohci->fmnumber, Ntdframes);
1267*a81c3ea0SDavid du Colombier if(status & Wdh){
1268*a81c3ea0SDavid du Colombier /* lsb of donehead has bit to flag other intrs. */
1269*a81c3ea0SDavid du Colombier td = pa2ptr(ctlr->hcca->donehead & ~0xF);
1270*a81c3ea0SDavid du Colombier
1271*a81c3ea0SDavid du Colombier for(i = 0; td != nil && i < 1024; i++){
1272*a81c3ea0SDavid du Colombier // if(0)ddprint("ohci tdinterrupt: td %#p\n", td);
1273*a81c3ea0SDavid du Colombier ntd = pa2ptr(td->nexttd & ~0xF);
1274*a81c3ea0SDavid du Colombier td->nexttd = 0;
1275*a81c3ea0SDavid du Colombier if(td->ep == nil || td->io == nil)
1276*a81c3ea0SDavid du Colombier panic("ohci: interrupt: ep %#p io %#p",
1277*a81c3ea0SDavid du Colombier td->ep, td->io);
1278*a81c3ea0SDavid du Colombier ohciinterrupts[td->ep->ttype]++;
1279*a81c3ea0SDavid du Colombier if(td->ep->ttype == Tiso)
1280*a81c3ea0SDavid du Colombier isointerrupt(ctlr, td->ep, td->io, td, frno);
1281*a81c3ea0SDavid du Colombier else
1282*a81c3ea0SDavid du Colombier qhinterrupt(ctlr, td->ep, td->io, td, frno);
1283*a81c3ea0SDavid du Colombier td = ntd;
1284*a81c3ea0SDavid du Colombier }
1285*a81c3ea0SDavid du Colombier if(i >= 1024)
1286*a81c3ea0SDavid du Colombier print("ohci: bug: more than 1024 done Tds?\n");
1287*a81c3ea0SDavid du Colombier ctlr->hcca->donehead = 0;
1288*a81c3ea0SDavid du Colombier }
1289*a81c3ea0SDavid du Colombier
1290*a81c3ea0SDavid du Colombier ctlr->ohci->intrsts = status;
1291*a81c3ea0SDavid du Colombier status &= ~Wdh;
1292*a81c3ea0SDavid du Colombier status &= ~Sf;
1293*a81c3ea0SDavid du Colombier if(status & So){
1294*a81c3ea0SDavid du Colombier print("ohci: sched overrun: too much load\n");
1295*a81c3ea0SDavid du Colombier ctlr->overrun++;
1296*a81c3ea0SDavid du Colombier status &= ~So;
1297*a81c3ea0SDavid du Colombier }
1298*a81c3ea0SDavid du Colombier if((status & Ue) != 0){
1299*a81c3ea0SDavid du Colombier curred = ctlr->ohci->periodcurred;
1300*a81c3ea0SDavid du Colombier print("ohci: unrecoverable error frame 0x%.8lux ed 0x%.8lux, "
1301*a81c3ea0SDavid du Colombier "ints %d %d %d %d\n",
1302*a81c3ea0SDavid du Colombier ctlr->ohci->fmnumber, curred,
1303*a81c3ea0SDavid du Colombier ohciinterrupts[Tctl], ohciinterrupts[Tintr],
1304*a81c3ea0SDavid du Colombier ohciinterrupts[Tbulk], ohciinterrupts[Tiso]);
1305*a81c3ea0SDavid du Colombier if(curred != 0)
1306*a81c3ea0SDavid du Colombier dumped(pa2ptr(curred));
1307*a81c3ea0SDavid du Colombier status &= ~Ue;
1308*a81c3ea0SDavid du Colombier }
1309*a81c3ea0SDavid du Colombier if(status != 0)
1310*a81c3ea0SDavid du Colombier print("ohci interrupt: unhandled sts 0x%.8lux\n", status);
1311*a81c3ea0SDavid du Colombier ctlr->ohci->intrenable = Mie | Wdh | Ue;
1312*a81c3ea0SDavid du Colombier iunlock(ctlr);
1313*a81c3ea0SDavid du Colombier }
1314*a81c3ea0SDavid du Colombier
1315*a81c3ea0SDavid du Colombier /*
1316*a81c3ea0SDavid du Colombier * The old dummy Td is used to implement the new Td.
1317*a81c3ea0SDavid du Colombier * A new dummy is linked at the end of the old one and
1318*a81c3ea0SDavid du Colombier * returned, to link further Tds if needed.
1319*a81c3ea0SDavid du Colombier */
1320*a81c3ea0SDavid du Colombier static Td*
epgettd(Ep * ep,Qio * io,Td ** dtdp,int flags,void * a,int count)1321*a81c3ea0SDavid du Colombier epgettd(Ep *ep, Qio *io, Td **dtdp, int flags, void *a, int count)
1322*a81c3ea0SDavid du Colombier {
1323*a81c3ea0SDavid du Colombier Td *td, *dtd;
1324*a81c3ea0SDavid du Colombier Block *bp;
1325*a81c3ea0SDavid du Colombier
1326*a81c3ea0SDavid du Colombier if(count <= BY2PG)
1327*a81c3ea0SDavid du Colombier bp = allocb(count);
1328*a81c3ea0SDavid du Colombier else{
1329*a81c3ea0SDavid du Colombier if(count > 2*BY2PG)
1330*a81c3ea0SDavid du Colombier panic("ohci: transfer > two pages");
1331*a81c3ea0SDavid du Colombier /* maximum of one physical page crossing allowed */
1332*a81c3ea0SDavid du Colombier bp = allocb(count+BY2PG);
1333*a81c3ea0SDavid du Colombier bp->rp = (uchar*)PGROUND((uintptr)bp->rp);
1334*a81c3ea0SDavid du Colombier bp->wp = bp->rp;
1335*a81c3ea0SDavid du Colombier }
1336*a81c3ea0SDavid du Colombier dtd = *dtdp;
1337*a81c3ea0SDavid du Colombier td = dtd;
1338*a81c3ea0SDavid du Colombier td->bp = bp;
1339*a81c3ea0SDavid du Colombier if(count > 0){
1340*a81c3ea0SDavid du Colombier td->cbp0 = td->cbp = ptr2pa(bp->wp);
1341*a81c3ea0SDavid du Colombier td->be = ptr2pa(bp->wp + count - 1);
1342*a81c3ea0SDavid du Colombier if(a != nil){
1343*a81c3ea0SDavid du Colombier /* validaddr((uintptr)a, count, 0); DEBUG */
1344*a81c3ea0SDavid du Colombier memmove(KSEG1ADDR(bp->wp), a, count);
1345*a81c3ea0SDavid du Colombier }
1346*a81c3ea0SDavid du Colombier bp->wp += count;
1347*a81c3ea0SDavid du Colombier }
1348*a81c3ea0SDavid du Colombier td->nbytes = count;
1349*a81c3ea0SDavid du Colombier td->ctrl = io->tok|Tdusetog|io->toggle|flags;
1350*a81c3ea0SDavid du Colombier if(io->toggle == Tddata0)
1351*a81c3ea0SDavid du Colombier io->toggle = Tddata1;
1352*a81c3ea0SDavid du Colombier else
1353*a81c3ea0SDavid du Colombier io->toggle = Tddata0;
1354*a81c3ea0SDavid du Colombier assert(td->ep == ep);
1355*a81c3ea0SDavid du Colombier td->io = io;
1356*a81c3ea0SDavid du Colombier dtd = tdalloc(); /* new dummy */
1357*a81c3ea0SDavid du Colombier dtd->ep = ep;
1358*a81c3ea0SDavid du Colombier td->nexttd = ptr2pa(dtd);
1359*a81c3ea0SDavid du Colombier td->next = dtd;
1360*a81c3ea0SDavid du Colombier *dtdp = dtd;
1361*a81c3ea0SDavid du Colombier return td;
1362*a81c3ea0SDavid du Colombier }
1363*a81c3ea0SDavid du Colombier
1364*a81c3ea0SDavid du Colombier /*
1365*a81c3ea0SDavid du Colombier * Try to get them idle
1366*a81c3ea0SDavid du Colombier */
1367*a81c3ea0SDavid du Colombier static void
aborttds(Qio * io)1368*a81c3ea0SDavid du Colombier aborttds(Qio *io)
1369*a81c3ea0SDavid du Colombier {
1370*a81c3ea0SDavid du Colombier Ed *ed;
1371*a81c3ea0SDavid du Colombier Td *td;
1372*a81c3ea0SDavid du Colombier
1373*a81c3ea0SDavid du Colombier ed = io->ed;
1374*a81c3ea0SDavid du Colombier if(ed == nil)
1375*a81c3ea0SDavid du Colombier return;
1376*a81c3ea0SDavid du Colombier ed->ctrl |= Edskip;
1377*a81c3ea0SDavid du Colombier for(td = ed->tds; td != nil; td = td->next)
1378*a81c3ea0SDavid du Colombier if(td->bp != nil)
1379*a81c3ea0SDavid du Colombier td->bp->wp = td->bp->rp;
1380*a81c3ea0SDavid du Colombier ed->head = (ed->head&0xF) | ed->tail;
1381*a81c3ea0SDavid du Colombier if((ed->ctrl & Ediso) == 0)
1382*a81c3ea0SDavid du Colombier ed->tds = pa2ptr(ed->tail);
1383*a81c3ea0SDavid du Colombier }
1384*a81c3ea0SDavid du Colombier
1385*a81c3ea0SDavid du Colombier static int
epiodone(void * a)1386*a81c3ea0SDavid du Colombier epiodone(void *a)
1387*a81c3ea0SDavid du Colombier {
1388*a81c3ea0SDavid du Colombier Qio *io;
1389*a81c3ea0SDavid du Colombier
1390*a81c3ea0SDavid du Colombier io = a;
1391*a81c3ea0SDavid du Colombier return io->state != Qrun;
1392*a81c3ea0SDavid du Colombier }
1393*a81c3ea0SDavid du Colombier
1394*a81c3ea0SDavid du Colombier static void
epiowait(Ctlr * ctlr,Qio * io,int tmout,ulong)1395*a81c3ea0SDavid du Colombier epiowait(Ctlr *ctlr, Qio *io, int tmout, ulong)
1396*a81c3ea0SDavid du Colombier {
1397*a81c3ea0SDavid du Colombier Ed *ed;
1398*a81c3ea0SDavid du Colombier int timedout;
1399*a81c3ea0SDavid du Colombier
1400*a81c3ea0SDavid du Colombier ed = io->ed;
1401*a81c3ea0SDavid du Colombier // if(0)ddqprint("ohci io %#p sleep on ed %#p state %s\n",
1402*a81c3ea0SDavid du Colombier // io, ed, iosname[io->state]);
1403*a81c3ea0SDavid du Colombier timedout = 0;
1404*a81c3ea0SDavid du Colombier if(waserror()){
1405*a81c3ea0SDavid du Colombier dqprint("ohci io %#p ed %#p timed out\n", io, ed);
1406*a81c3ea0SDavid du Colombier timedout++;
1407*a81c3ea0SDavid du Colombier }else{
1408*a81c3ea0SDavid du Colombier if(tmout == 0)
1409*a81c3ea0SDavid du Colombier sleep(io, epiodone, io);
1410*a81c3ea0SDavid du Colombier else
1411*a81c3ea0SDavid du Colombier tsleep(io, epiodone, io, tmout);
1412*a81c3ea0SDavid du Colombier poperror();
1413*a81c3ea0SDavid du Colombier }
1414*a81c3ea0SDavid du Colombier ilock(ctlr);
1415*a81c3ea0SDavid du Colombier if(io->state == Qrun)
1416*a81c3ea0SDavid du Colombier timedout = 1;
1417*a81c3ea0SDavid du Colombier else if(io->state != Qdone && io->state != Qclose)
1418*a81c3ea0SDavid du Colombier panic("epio: ed not done and not closed");
1419*a81c3ea0SDavid du Colombier if(timedout){
1420*a81c3ea0SDavid du Colombier aborttds(io);
1421*a81c3ea0SDavid du Colombier io->err = "request timed out";
1422*a81c3ea0SDavid du Colombier iunlock(ctlr);
1423*a81c3ea0SDavid du Colombier if(!waserror()){
1424*a81c3ea0SDavid du Colombier tsleep(&up->sleep, return0, 0, Abortdelay);
1425*a81c3ea0SDavid du Colombier poperror();
1426*a81c3ea0SDavid du Colombier }
1427*a81c3ea0SDavid du Colombier ilock(ctlr);
1428*a81c3ea0SDavid du Colombier }
1429*a81c3ea0SDavid du Colombier if(io->state != Qclose)
1430*a81c3ea0SDavid du Colombier io->state = Qidle;
1431*a81c3ea0SDavid du Colombier iunlock(ctlr);
1432*a81c3ea0SDavid du Colombier }
1433*a81c3ea0SDavid du Colombier
1434*a81c3ea0SDavid du Colombier /*
1435*a81c3ea0SDavid du Colombier * Non iso I/O.
1436*a81c3ea0SDavid du Colombier * To make it work for control transfers, the caller may
1437*a81c3ea0SDavid du Colombier * lock the Qio for the entire control transfer.
1438*a81c3ea0SDavid du Colombier */
1439*a81c3ea0SDavid du Colombier static long
epio(Ep * ep,Qio * io,void * a,long count,int mustlock)1440*a81c3ea0SDavid du Colombier epio(Ep *ep, Qio *io, void *a, long count, int mustlock)
1441*a81c3ea0SDavid du Colombier {
1442*a81c3ea0SDavid du Colombier Ed *ed;
1443*a81c3ea0SDavid du Colombier Ctlr *ctlr;
1444*a81c3ea0SDavid du Colombier char buf[80];
1445*a81c3ea0SDavid du Colombier char *err;
1446*a81c3ea0SDavid du Colombier uchar *c;
1447*a81c3ea0SDavid du Colombier Td *td, *ltd, *ntd, *td0;
1448*a81c3ea0SDavid du Colombier int last, ntds, tmout;
1449*a81c3ea0SDavid du Colombier long tot, n;
1450*a81c3ea0SDavid du Colombier ulong load;
1451*a81c3ea0SDavid du Colombier
1452*a81c3ea0SDavid du Colombier ed = io->ed;
1453*a81c3ea0SDavid du Colombier ctlr = ep->hp->aux;
1454*a81c3ea0SDavid du Colombier io->debug = ep->debug;
1455*a81c3ea0SDavid du Colombier tmout = ep->tmout;
1456*a81c3ea0SDavid du Colombier ddeprint("ohci: %s ep%d.%d io %#p count %ld\n",
1457*a81c3ea0SDavid du Colombier io->tok == Tdtokin ? "in" : "out",
1458*a81c3ea0SDavid du Colombier ep->dev->nb, ep->nb, io, count);
1459*a81c3ea0SDavid du Colombier if((debug > 1 || ep->debug > 1) && io->tok != Tdtokin){
1460*a81c3ea0SDavid du Colombier seprintdata(buf, buf+sizeof(buf), a, count);
1461*a81c3ea0SDavid du Colombier print("\t%s\n", buf);
1462*a81c3ea0SDavid du Colombier }
1463*a81c3ea0SDavid du Colombier if(mustlock){
1464*a81c3ea0SDavid du Colombier qlock(io);
1465*a81c3ea0SDavid du Colombier if(waserror()){
1466*a81c3ea0SDavid du Colombier qunlock(io);
1467*a81c3ea0SDavid du Colombier nexterror();
1468*a81c3ea0SDavid du Colombier }
1469*a81c3ea0SDavid du Colombier }
1470*a81c3ea0SDavid du Colombier io->err = nil;
1471*a81c3ea0SDavid du Colombier ilock(ctlr);
1472*a81c3ea0SDavid du Colombier if(io->state == Qclose){ /* Tds released by cancelio */
1473*a81c3ea0SDavid du Colombier iunlock(ctlr);
1474*a81c3ea0SDavid du Colombier error(io->err ? io->err : Eio);
1475*a81c3ea0SDavid du Colombier }
1476*a81c3ea0SDavid du Colombier if(io->state != Qidle)
1477*a81c3ea0SDavid du Colombier panic("epio: qio not idle");
1478*a81c3ea0SDavid du Colombier io->state = Qinstall;
1479*a81c3ea0SDavid du Colombier
1480*a81c3ea0SDavid du Colombier c = a;
1481*a81c3ea0SDavid du Colombier ltd = td0 = ed->tds;
1482*a81c3ea0SDavid du Colombier load = tot = 0;
1483*a81c3ea0SDavid du Colombier do{
1484*a81c3ea0SDavid du Colombier n = 2*BY2PG;
1485*a81c3ea0SDavid du Colombier if(count-tot < n)
1486*a81c3ea0SDavid du Colombier n = count-tot;
1487*a81c3ea0SDavid du Colombier if(c != nil && io->tok != Tdtokin)
1488*a81c3ea0SDavid du Colombier td = epgettd(ep, io, <d, 0, c+tot, n);
1489*a81c3ea0SDavid du Colombier else
1490*a81c3ea0SDavid du Colombier td = epgettd(ep, io, <d, 0, nil, n);
1491*a81c3ea0SDavid du Colombier tot += n;
1492*a81c3ea0SDavid du Colombier load += ep->load;
1493*a81c3ea0SDavid du Colombier }while(tot < count);
1494*a81c3ea0SDavid du Colombier if(td0 == nil || ltd == nil || td0 == ltd)
1495*a81c3ea0SDavid du Colombier panic("epio: no td");
1496*a81c3ea0SDavid du Colombier td->last = 1;
1497*a81c3ea0SDavid du Colombier if(debug > 2 || ep->debug > 2)
1498*a81c3ea0SDavid du Colombier dumptds(td0, "put td", ep->ttype == Tiso);
1499*a81c3ea0SDavid du Colombier iunlock(ctlr);
1500*a81c3ea0SDavid du Colombier
1501*a81c3ea0SDavid du Colombier ilock(ctlr);
1502*a81c3ea0SDavid du Colombier if(io->state != Qclose){
1503*a81c3ea0SDavid du Colombier io->iotime = TK2MS(MACHP(0)->ticks);
1504*a81c3ea0SDavid du Colombier io->state = Qrun;
1505*a81c3ea0SDavid du Colombier ed->tail = ptr2pa(ltd);
1506*a81c3ea0SDavid du Colombier if(ep->ttype == Tctl)
1507*a81c3ea0SDavid du Colombier ctlr->ohci->cmdsts |= Sclf;
1508*a81c3ea0SDavid du Colombier else if(ep->ttype == Tbulk)
1509*a81c3ea0SDavid du Colombier ctlr->ohci->cmdsts |= Sblf;
1510*a81c3ea0SDavid du Colombier }
1511*a81c3ea0SDavid du Colombier iunlock(ctlr);
1512*a81c3ea0SDavid du Colombier
1513*a81c3ea0SDavid du Colombier epiowait(ctlr, io, tmout, load);
1514*a81c3ea0SDavid du Colombier ilock(ctlr);
1515*a81c3ea0SDavid du Colombier if(debug > 1 || ep->debug > 1)
1516*a81c3ea0SDavid du Colombier dumptds(td0, "got td", 0);
1517*a81c3ea0SDavid du Colombier iunlock(ctlr);
1518*a81c3ea0SDavid du Colombier
1519*a81c3ea0SDavid du Colombier tot = 0;
1520*a81c3ea0SDavid du Colombier c = a;
1521*a81c3ea0SDavid du Colombier ntds = last = 0;
1522*a81c3ea0SDavid du Colombier for(td = td0; td != ltd; td = ntd){
1523*a81c3ea0SDavid du Colombier ntds++;
1524*a81c3ea0SDavid du Colombier /*
1525*a81c3ea0SDavid du Colombier * If the Td is flagged as last we must
1526*a81c3ea0SDavid du Colombier * ignore any following Td. The block may
1527*a81c3ea0SDavid du Colombier * seem to have bytes but interrupt has not seen
1528*a81c3ea0SDavid du Colombier * those Tds through the done queue, and they are void.
1529*a81c3ea0SDavid du Colombier */
1530*a81c3ea0SDavid du Colombier if(last == 0 && tderrs(td) == Tdok){
1531*a81c3ea0SDavid du Colombier n = BLEN(td->bp);
1532*a81c3ea0SDavid du Colombier tot += n;
1533*a81c3ea0SDavid du Colombier if(c != nil && tdtok(td) == Tdtokin && n > 0){
1534*a81c3ea0SDavid du Colombier memmove(c, KSEG1ADDR(td->bp->rp), n);
1535*a81c3ea0SDavid du Colombier c += n;
1536*a81c3ea0SDavid du Colombier }
1537*a81c3ea0SDavid du Colombier }
1538*a81c3ea0SDavid du Colombier last |= td->last;
1539*a81c3ea0SDavid du Colombier ntd = td->next;
1540*a81c3ea0SDavid du Colombier tdfree(td);
1541*a81c3ea0SDavid du Colombier }
1542*a81c3ea0SDavid du Colombier if(edtoggle(ed) == 0)
1543*a81c3ea0SDavid du Colombier io->toggle = Tddata0;
1544*a81c3ea0SDavid du Colombier else
1545*a81c3ea0SDavid du Colombier io->toggle = Tddata1;
1546*a81c3ea0SDavid du Colombier
1547*a81c3ea0SDavid du Colombier err = io->err;
1548*a81c3ea0SDavid du Colombier if(mustlock){
1549*a81c3ea0SDavid du Colombier qunlock(io);
1550*a81c3ea0SDavid du Colombier poperror();
1551*a81c3ea0SDavid du Colombier }
1552*a81c3ea0SDavid du Colombier ddeprint("ohci: io %#p: %d tds: return %ld err '%s'\n\n",
1553*a81c3ea0SDavid du Colombier io, ntds, tot, err);
1554*a81c3ea0SDavid du Colombier if(err != nil)
1555*a81c3ea0SDavid du Colombier error(err);
1556*a81c3ea0SDavid du Colombier if(tot < 0)
1557*a81c3ea0SDavid du Colombier error(Eio);
1558*a81c3ea0SDavid du Colombier return tot;
1559*a81c3ea0SDavid du Colombier }
1560*a81c3ea0SDavid du Colombier
1561*a81c3ea0SDavid du Colombier /*
1562*a81c3ea0SDavid du Colombier * halt condition was cleared on the endpoint. update our toggles.
1563*a81c3ea0SDavid du Colombier */
1564*a81c3ea0SDavid du Colombier static void
clrhalt(Ep * ep)1565*a81c3ea0SDavid du Colombier clrhalt(Ep *ep)
1566*a81c3ea0SDavid du Colombier {
1567*a81c3ea0SDavid du Colombier Qio *io;
1568*a81c3ea0SDavid du Colombier
1569*a81c3ea0SDavid du Colombier ep->clrhalt = 0;
1570*a81c3ea0SDavid du Colombier switch(ep->ttype){
1571*a81c3ea0SDavid du Colombier case Tbulk:
1572*a81c3ea0SDavid du Colombier case Tintr:
1573*a81c3ea0SDavid du Colombier io = ep->aux;
1574*a81c3ea0SDavid du Colombier if(ep->mode != OREAD){
1575*a81c3ea0SDavid du Colombier qlock(&io[OWRITE]);
1576*a81c3ea0SDavid du Colombier io[OWRITE].toggle = Tddata0;
1577*a81c3ea0SDavid du Colombier deprint("ep clrhalt for io %#p\n", io+OWRITE);
1578*a81c3ea0SDavid du Colombier qunlock(&io[OWRITE]);
1579*a81c3ea0SDavid du Colombier }
1580*a81c3ea0SDavid du Colombier if(ep->mode != OWRITE){
1581*a81c3ea0SDavid du Colombier qlock(&io[OREAD]);
1582*a81c3ea0SDavid du Colombier io[OREAD].toggle = Tddata0;
1583*a81c3ea0SDavid du Colombier deprint("ep clrhalt for io %#p\n", io+OREAD);
1584*a81c3ea0SDavid du Colombier qunlock(&io[OREAD]);
1585*a81c3ea0SDavid du Colombier }
1586*a81c3ea0SDavid du Colombier break;
1587*a81c3ea0SDavid du Colombier }
1588*a81c3ea0SDavid du Colombier }
1589*a81c3ea0SDavid du Colombier
1590*a81c3ea0SDavid du Colombier static long
epread(Ep * ep,void * a,long count)1591*a81c3ea0SDavid du Colombier epread(Ep *ep, void *a, long count)
1592*a81c3ea0SDavid du Colombier {
1593*a81c3ea0SDavid du Colombier Ctlio *cio;
1594*a81c3ea0SDavid du Colombier Qio *io;
1595*a81c3ea0SDavid du Colombier char buf[80];
1596*a81c3ea0SDavid du Colombier ulong delta;
1597*a81c3ea0SDavid du Colombier
1598*a81c3ea0SDavid du Colombier if(ep->aux == nil)
1599*a81c3ea0SDavid du Colombier panic("epread: not open");
1600*a81c3ea0SDavid du Colombier
1601*a81c3ea0SDavid du Colombier switch(ep->ttype){
1602*a81c3ea0SDavid du Colombier case Tctl:
1603*a81c3ea0SDavid du Colombier cio = ep->aux;
1604*a81c3ea0SDavid du Colombier qlock(cio);
1605*a81c3ea0SDavid du Colombier if(waserror()){
1606*a81c3ea0SDavid du Colombier qunlock(cio);
1607*a81c3ea0SDavid du Colombier nexterror();
1608*a81c3ea0SDavid du Colombier }
1609*a81c3ea0SDavid du Colombier ddeprint("epread ctl ndata %d\n", cio->ndata);
1610*a81c3ea0SDavid du Colombier if(cio->ndata < 0)
1611*a81c3ea0SDavid du Colombier error("request expected");
1612*a81c3ea0SDavid du Colombier else if(cio->ndata == 0){
1613*a81c3ea0SDavid du Colombier cio->ndata = -1;
1614*a81c3ea0SDavid du Colombier count = 0;
1615*a81c3ea0SDavid du Colombier }else{
1616*a81c3ea0SDavid du Colombier if(count > cio->ndata)
1617*a81c3ea0SDavid du Colombier count = cio->ndata;
1618*a81c3ea0SDavid du Colombier if(count > 0)
1619*a81c3ea0SDavid du Colombier memmove(a, KSEG1ADDR(cio->data), count);
1620*a81c3ea0SDavid du Colombier /* BUG for big transfers */
1621*a81c3ea0SDavid du Colombier free(cio->data);
1622*a81c3ea0SDavid du Colombier cio->data = nil;
1623*a81c3ea0SDavid du Colombier cio->ndata = 0; /* signal EOF next time */
1624*a81c3ea0SDavid du Colombier }
1625*a81c3ea0SDavid du Colombier qunlock(cio);
1626*a81c3ea0SDavid du Colombier poperror();
1627*a81c3ea0SDavid du Colombier if(debug>1 || ep->debug){
1628*a81c3ea0SDavid du Colombier seprintdata(buf, buf+sizeof(buf), a, count);
1629*a81c3ea0SDavid du Colombier print("epread: %s\n", buf);
1630*a81c3ea0SDavid du Colombier }
1631*a81c3ea0SDavid du Colombier return count;
1632*a81c3ea0SDavid du Colombier case Tbulk:
1633*a81c3ea0SDavid du Colombier io = ep->aux;
1634*a81c3ea0SDavid du Colombier if(ep->clrhalt)
1635*a81c3ea0SDavid du Colombier clrhalt(ep);
1636*a81c3ea0SDavid du Colombier return epio(ep, &io[OREAD], a, count, 1);
1637*a81c3ea0SDavid du Colombier case Tintr:
1638*a81c3ea0SDavid du Colombier io = ep->aux;
1639*a81c3ea0SDavid du Colombier delta = TK2MS(MACHP(0)->ticks) - io[OREAD].iotime + 1;
1640*a81c3ea0SDavid du Colombier if(delta < ep->pollival / 2)
1641*a81c3ea0SDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival/2 - delta);
1642*a81c3ea0SDavid du Colombier if(ep->clrhalt)
1643*a81c3ea0SDavid du Colombier clrhalt(ep);
1644*a81c3ea0SDavid du Colombier return epio(ep, &io[OREAD], a, count, 1);
1645*a81c3ea0SDavid du Colombier case Tiso:
1646*a81c3ea0SDavid du Colombier panic("ohci: iso read not implemented");
1647*a81c3ea0SDavid du Colombier break;
1648*a81c3ea0SDavid du Colombier default:
1649*a81c3ea0SDavid du Colombier panic("epread: bad ep ttype %d", ep->ttype);
1650*a81c3ea0SDavid du Colombier }
1651*a81c3ea0SDavid du Colombier return -1;
1652*a81c3ea0SDavid du Colombier }
1653*a81c3ea0SDavid du Colombier
1654*a81c3ea0SDavid du Colombier /*
1655*a81c3ea0SDavid du Colombier * Control transfers are one setup write (data0)
1656*a81c3ea0SDavid du Colombier * plus zero or more reads/writes (data1, data0, ...)
1657*a81c3ea0SDavid du Colombier * plus a final write/read with data1 to ack.
1658*a81c3ea0SDavid du Colombier * For both host to device and device to host we perform
1659*a81c3ea0SDavid du Colombier * the entire transfer when the user writes the request,
1660*a81c3ea0SDavid du Colombier * and keep any data read from the device for a later read.
1661*a81c3ea0SDavid du Colombier * We call epio three times instead of placing all Tds at
1662*a81c3ea0SDavid du Colombier * the same time because doing so leads to crc/tmout errors
1663*a81c3ea0SDavid du Colombier * for some devices.
1664*a81c3ea0SDavid du Colombier * Upon errors on the data phase we must still run the status
1665*a81c3ea0SDavid du Colombier * phase or the device may cease responding in the future.
1666*a81c3ea0SDavid du Colombier */
1667*a81c3ea0SDavid du Colombier static long
epctlio(Ep * ep,Ctlio * cio,void * a,long count)1668*a81c3ea0SDavid du Colombier epctlio(Ep *ep, Ctlio *cio, void *a, long count)
1669*a81c3ea0SDavid du Colombier {
1670*a81c3ea0SDavid du Colombier uchar *c;
1671*a81c3ea0SDavid du Colombier long len;
1672*a81c3ea0SDavid du Colombier
1673*a81c3ea0SDavid du Colombier ddeprint("epctlio: cio %#p ep%d.%d count %ld\n",
1674*a81c3ea0SDavid du Colombier cio, ep->dev->nb, ep->nb, count);
1675*a81c3ea0SDavid du Colombier if(count < Rsetuplen)
1676*a81c3ea0SDavid du Colombier error("short usb command");
1677*a81c3ea0SDavid du Colombier qlock(cio);
1678*a81c3ea0SDavid du Colombier free(cio->data);
1679*a81c3ea0SDavid du Colombier cio->data = nil;
1680*a81c3ea0SDavid du Colombier cio->ndata = 0;
1681*a81c3ea0SDavid du Colombier if(waserror()){
1682*a81c3ea0SDavid du Colombier qunlock(cio);
1683*a81c3ea0SDavid du Colombier free(cio->data);
1684*a81c3ea0SDavid du Colombier cio->data = nil;
1685*a81c3ea0SDavid du Colombier cio->ndata = 0;
1686*a81c3ea0SDavid du Colombier nexterror();
1687*a81c3ea0SDavid du Colombier }
1688*a81c3ea0SDavid du Colombier
1689*a81c3ea0SDavid du Colombier /* set the address if unset and out of configuration state */
1690*a81c3ea0SDavid du Colombier if(ep->dev->state != Dconfig && ep->dev->state != Dreset)
1691*a81c3ea0SDavid du Colombier if(cio->usbid == 0){
1692*a81c3ea0SDavid du Colombier cio->usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
1693*a81c3ea0SDavid du Colombier edsetaddr(cio->ed, cio->usbid);
1694*a81c3ea0SDavid du Colombier }
1695*a81c3ea0SDavid du Colombier /* adjust maxpkt if the user has learned a different one */
1696*a81c3ea0SDavid du Colombier if(edmaxpkt(cio->ed) != ep->maxpkt)
1697*a81c3ea0SDavid du Colombier edsetmaxpkt(cio->ed, ep->maxpkt);
1698*a81c3ea0SDavid du Colombier c = a;
1699*a81c3ea0SDavid du Colombier cio->tok = Tdtoksetup;
1700*a81c3ea0SDavid du Colombier cio->toggle = Tddata0;
1701*a81c3ea0SDavid du Colombier if(epio(ep, cio, a, Rsetuplen, 0) < Rsetuplen)
1702*a81c3ea0SDavid du Colombier error(Eio);
1703*a81c3ea0SDavid du Colombier
1704*a81c3ea0SDavid du Colombier a = c + Rsetuplen;
1705*a81c3ea0SDavid du Colombier count -= Rsetuplen;
1706*a81c3ea0SDavid du Colombier
1707*a81c3ea0SDavid du Colombier cio->toggle = Tddata1;
1708*a81c3ea0SDavid du Colombier if(c[Rtype] & Rd2h){
1709*a81c3ea0SDavid du Colombier cio->tok = Tdtokin;
1710*a81c3ea0SDavid du Colombier len = GET2(c+Rcount);
1711*a81c3ea0SDavid du Colombier if(len <= 0)
1712*a81c3ea0SDavid du Colombier error("bad length in d2h request");
1713*a81c3ea0SDavid du Colombier if(len > Maxctllen)
1714*a81c3ea0SDavid du Colombier error("d2h data too large to fit in ohci");
1715*a81c3ea0SDavid du Colombier a = cio->data = smalloc(len+1);
1716*a81c3ea0SDavid du Colombier }else{
1717*a81c3ea0SDavid du Colombier cio->tok = Tdtokout;
1718*a81c3ea0SDavid du Colombier len = count;
1719*a81c3ea0SDavid du Colombier }
1720*a81c3ea0SDavid du Colombier if(len > 0)
1721*a81c3ea0SDavid du Colombier if(waserror())
1722*a81c3ea0SDavid du Colombier len = -1;
1723*a81c3ea0SDavid du Colombier else{
1724*a81c3ea0SDavid du Colombier len = epio(ep, cio, a, len, 0);
1725*a81c3ea0SDavid du Colombier poperror();
1726*a81c3ea0SDavid du Colombier }
1727*a81c3ea0SDavid du Colombier if(c[Rtype] & Rd2h){
1728*a81c3ea0SDavid du Colombier count = Rsetuplen;
1729*a81c3ea0SDavid du Colombier cio->ndata = len;
1730*a81c3ea0SDavid du Colombier cio->tok = Tdtokout;
1731*a81c3ea0SDavid du Colombier }else{
1732*a81c3ea0SDavid du Colombier if(len < 0)
1733*a81c3ea0SDavid du Colombier count = -1;
1734*a81c3ea0SDavid du Colombier else
1735*a81c3ea0SDavid du Colombier count = Rsetuplen + len;
1736*a81c3ea0SDavid du Colombier cio->tok = Tdtokin;
1737*a81c3ea0SDavid du Colombier }
1738*a81c3ea0SDavid du Colombier cio->toggle = Tddata1;
1739*a81c3ea0SDavid du Colombier epio(ep, cio, nil, 0, 0);
1740*a81c3ea0SDavid du Colombier qunlock(cio);
1741*a81c3ea0SDavid du Colombier poperror();
1742*a81c3ea0SDavid du Colombier ddeprint("epctlio cio %#p return %ld\n", cio, count);
1743*a81c3ea0SDavid du Colombier return count;
1744*a81c3ea0SDavid du Colombier }
1745*a81c3ea0SDavid du Colombier
1746*a81c3ea0SDavid du Colombier /*
1747*a81c3ea0SDavid du Colombier * Put new samples in the dummy Td.
1748*a81c3ea0SDavid du Colombier * BUG: This does only a transfer per Td. We could do up to 8.
1749*a81c3ea0SDavid du Colombier */
1750*a81c3ea0SDavid du Colombier static long
putsamples(Ctlr * ctlr,Ep * ep,Isoio * iso,uchar * b,long count)1751*a81c3ea0SDavid du Colombier putsamples(Ctlr *ctlr, Ep *ep, Isoio *iso, uchar *b, long count)
1752*a81c3ea0SDavid du Colombier {
1753*a81c3ea0SDavid du Colombier Td *td;
1754*a81c3ea0SDavid du Colombier ulong n;
1755*a81c3ea0SDavid du Colombier
1756*a81c3ea0SDavid du Colombier td = pa2ptr(iso->ed->tail);
1757*a81c3ea0SDavid du Colombier n = count;
1758*a81c3ea0SDavid du Colombier if(n > td->nbytes - BLEN(td->bp))
1759*a81c3ea0SDavid du Colombier n = td->nbytes - BLEN(td->bp);
1760*a81c3ea0SDavid du Colombier assert(td->bp->wp + n <= td->bp->lim);
1761*a81c3ea0SDavid du Colombier memmove(KSEG1ADDR(td->bp->wp), b, n);
1762*a81c3ea0SDavid du Colombier td->bp->wp += n;
1763*a81c3ea0SDavid du Colombier if(BLEN(td->bp) == td->nbytes){ /* full Td: activate it */
1764*a81c3ea0SDavid du Colombier ilock(ctlr);
1765*a81c3ea0SDavid du Colombier isoadvance(ep, iso, td);
1766*a81c3ea0SDavid du Colombier iunlock(ctlr);
1767*a81c3ea0SDavid du Colombier }
1768*a81c3ea0SDavid du Colombier return n;
1769*a81c3ea0SDavid du Colombier }
1770*a81c3ea0SDavid du Colombier
1771*a81c3ea0SDavid du Colombier static long
episowrite(Ep * ep,void * a,long count)1772*a81c3ea0SDavid du Colombier episowrite(Ep *ep, void *a, long count)
1773*a81c3ea0SDavid du Colombier {
1774*a81c3ea0SDavid du Colombier long tot, nw;
1775*a81c3ea0SDavid du Colombier char *err;
1776*a81c3ea0SDavid du Colombier uchar *b;
1777*a81c3ea0SDavid du Colombier Ctlr *ctlr;
1778*a81c3ea0SDavid du Colombier Isoio *iso;
1779*a81c3ea0SDavid du Colombier
1780*a81c3ea0SDavid du Colombier ctlr = ep->hp->aux;
1781*a81c3ea0SDavid du Colombier iso = ep->aux;
1782*a81c3ea0SDavid du Colombier iso->debug = ep->debug;
1783*a81c3ea0SDavid du Colombier
1784*a81c3ea0SDavid du Colombier qlock(iso);
1785*a81c3ea0SDavid du Colombier if(waserror()){
1786*a81c3ea0SDavid du Colombier qunlock(iso);
1787*a81c3ea0SDavid du Colombier nexterror();
1788*a81c3ea0SDavid du Colombier }
1789*a81c3ea0SDavid du Colombier diprint("ohci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb);
1790*a81c3ea0SDavid du Colombier ilock(ctlr);
1791*a81c3ea0SDavid du Colombier if(iso->state == Qclose){
1792*a81c3ea0SDavid du Colombier iunlock(ctlr);
1793*a81c3ea0SDavid du Colombier error(iso->err ? iso->err : Eio);
1794*a81c3ea0SDavid du Colombier }
1795*a81c3ea0SDavid du Colombier iso->state = Qrun;
1796*a81c3ea0SDavid du Colombier b = a;
1797*a81c3ea0SDavid du Colombier for(tot = 0; tot < count; tot += nw){
1798*a81c3ea0SDavid du Colombier while(isocanwrite(iso) == 0){
1799*a81c3ea0SDavid du Colombier iunlock(ctlr);
1800*a81c3ea0SDavid du Colombier diprint("ohci: episowrite: %#p sleep\n", iso);
1801*a81c3ea0SDavid du Colombier if(waserror()){
1802*a81c3ea0SDavid du Colombier if(iso->err == nil)
1803*a81c3ea0SDavid du Colombier iso->err = "I/O timed out";
1804*a81c3ea0SDavid du Colombier ilock(ctlr);
1805*a81c3ea0SDavid du Colombier break;
1806*a81c3ea0SDavid du Colombier }
1807*a81c3ea0SDavid du Colombier tsleep(iso, isocanwrite, iso, ep->tmout);
1808*a81c3ea0SDavid du Colombier poperror();
1809*a81c3ea0SDavid du Colombier ilock(ctlr);
1810*a81c3ea0SDavid du Colombier }
1811*a81c3ea0SDavid du Colombier err = iso->err;
1812*a81c3ea0SDavid du Colombier iso->err = nil;
1813*a81c3ea0SDavid du Colombier if(iso->state == Qclose || err != nil){
1814*a81c3ea0SDavid du Colombier iunlock(ctlr);
1815*a81c3ea0SDavid du Colombier error(err ? err : Eio);
1816*a81c3ea0SDavid du Colombier }
1817*a81c3ea0SDavid du Colombier if(iso->state != Qrun)
1818*a81c3ea0SDavid du Colombier panic("episowrite: iso not running");
1819*a81c3ea0SDavid du Colombier iunlock(ctlr); /* We could page fault here */
1820*a81c3ea0SDavid du Colombier nw = putsamples(ctlr, ep, iso, b+tot, count-tot);
1821*a81c3ea0SDavid du Colombier ilock(ctlr);
1822*a81c3ea0SDavid du Colombier }
1823*a81c3ea0SDavid du Colombier if(iso->state != Qclose)
1824*a81c3ea0SDavid du Colombier iso->state = Qdone;
1825*a81c3ea0SDavid du Colombier iunlock(ctlr);
1826*a81c3ea0SDavid du Colombier err = iso->err; /* in case it failed early */
1827*a81c3ea0SDavid du Colombier iso->err = nil;
1828*a81c3ea0SDavid du Colombier qunlock(iso);
1829*a81c3ea0SDavid du Colombier poperror();
1830*a81c3ea0SDavid du Colombier if(err != nil)
1831*a81c3ea0SDavid du Colombier error(err);
1832*a81c3ea0SDavid du Colombier diprint("ohci: episowrite: %#p %ld bytes\n", iso, tot);
1833*a81c3ea0SDavid du Colombier return tot;
1834*a81c3ea0SDavid du Colombier }
1835*a81c3ea0SDavid du Colombier
1836*a81c3ea0SDavid du Colombier static long
epwrite(Ep * ep,void * a,long count)1837*a81c3ea0SDavid du Colombier epwrite(Ep *ep, void *a, long count)
1838*a81c3ea0SDavid du Colombier {
1839*a81c3ea0SDavid du Colombier Qio *io;
1840*a81c3ea0SDavid du Colombier Ctlio *cio;
1841*a81c3ea0SDavid du Colombier ulong delta;
1842*a81c3ea0SDavid du Colombier uchar *b;
1843*a81c3ea0SDavid du Colombier long tot, nw;
1844*a81c3ea0SDavid du Colombier
1845*a81c3ea0SDavid du Colombier if(ep->aux == nil)
1846*a81c3ea0SDavid du Colombier panic("ohci: epwrite: not open");
1847*a81c3ea0SDavid du Colombier switch(ep->ttype){
1848*a81c3ea0SDavid du Colombier case Tctl:
1849*a81c3ea0SDavid du Colombier cio = ep->aux;
1850*a81c3ea0SDavid du Colombier return epctlio(ep, cio, a, count);
1851*a81c3ea0SDavid du Colombier case Tbulk:
1852*a81c3ea0SDavid du Colombier io = ep->aux;
1853*a81c3ea0SDavid du Colombier if(ep->clrhalt)
1854*a81c3ea0SDavid du Colombier clrhalt(ep);
1855*a81c3ea0SDavid du Colombier /*
1856*a81c3ea0SDavid du Colombier * Put at most Tdatomic Tds (512 bytes) at a time.
1857*a81c3ea0SDavid du Colombier * Otherwise some devices produce babble errors.
1858*a81c3ea0SDavid du Colombier */
1859*a81c3ea0SDavid du Colombier b = a;
1860*a81c3ea0SDavid du Colombier assert(a != nil);
1861*a81c3ea0SDavid du Colombier for(tot = 0; tot < count ; tot += nw){
1862*a81c3ea0SDavid du Colombier nw = count - tot;
1863*a81c3ea0SDavid du Colombier if(nw > Tdatomic * ep->maxpkt)
1864*a81c3ea0SDavid du Colombier nw = Tdatomic * ep->maxpkt;
1865*a81c3ea0SDavid du Colombier nw = epio(ep, &io[OWRITE], b+tot, nw, 1);
1866*a81c3ea0SDavid du Colombier }
1867*a81c3ea0SDavid du Colombier return tot;
1868*a81c3ea0SDavid du Colombier case Tintr:
1869*a81c3ea0SDavid du Colombier io = ep->aux;
1870*a81c3ea0SDavid du Colombier delta = TK2MS(MACHP(0)->ticks) - io[OWRITE].iotime + 1;
1871*a81c3ea0SDavid du Colombier if(delta < ep->pollival)
1872*a81c3ea0SDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival - delta);
1873*a81c3ea0SDavid du Colombier if(ep->clrhalt)
1874*a81c3ea0SDavid du Colombier clrhalt(ep);
1875*a81c3ea0SDavid du Colombier return epio(ep, &io[OWRITE], a, count, 1);
1876*a81c3ea0SDavid du Colombier case Tiso:
1877*a81c3ea0SDavid du Colombier return episowrite(ep, a, count);
1878*a81c3ea0SDavid du Colombier default:
1879*a81c3ea0SDavid du Colombier panic("ohci: epwrite: bad ep ttype %d", ep->ttype);
1880*a81c3ea0SDavid du Colombier }
1881*a81c3ea0SDavid du Colombier return -1;
1882*a81c3ea0SDavid du Colombier }
1883*a81c3ea0SDavid du Colombier
1884*a81c3ea0SDavid du Colombier static Ed*
newed(Ctlr * ctlr,Ep * ep,Qio * io,char *)1885*a81c3ea0SDavid du Colombier newed(Ctlr *ctlr, Ep *ep, Qio *io, char *)
1886*a81c3ea0SDavid du Colombier {
1887*a81c3ea0SDavid du Colombier Ed *ed;
1888*a81c3ea0SDavid du Colombier Td *td;
1889*a81c3ea0SDavid du Colombier
1890*a81c3ea0SDavid du Colombier ed = io->ed = edalloc(); /* no errors raised here, really */
1891*a81c3ea0SDavid du Colombier td = tdalloc();
1892*a81c3ea0SDavid du Colombier td->ep = ep;
1893*a81c3ea0SDavid du Colombier td->io = io;
1894*a81c3ea0SDavid du Colombier ed->tail = ptr2pa(td);
1895*a81c3ea0SDavid du Colombier ed->head = ptr2pa(td);
1896*a81c3ea0SDavid du Colombier ed->tds = td;
1897*a81c3ea0SDavid du Colombier ed->ep = ep;
1898*a81c3ea0SDavid du Colombier ed->ctrl = (ep->maxpkt & Edmpsmask) << Edmpsshift;
1899*a81c3ea0SDavid du Colombier if(ep->ttype == Tiso)
1900*a81c3ea0SDavid du Colombier ed->ctrl |= Ediso;
1901*a81c3ea0SDavid du Colombier if(waserror()){
1902*a81c3ea0SDavid du Colombier edfree(ed);
1903*a81c3ea0SDavid du Colombier io->ed = nil;
1904*a81c3ea0SDavid du Colombier nexterror();
1905*a81c3ea0SDavid du Colombier }
1906*a81c3ea0SDavid du Colombier /* For setup endpoints we start with the config address */
1907*a81c3ea0SDavid du Colombier if(ep->ttype != Tctl)
1908*a81c3ea0SDavid du Colombier edsetaddr(io->ed, io->usbid);
1909*a81c3ea0SDavid du Colombier if(ep->dev->speed == Lowspeed)
1910*a81c3ea0SDavid du Colombier ed->ctrl |= Edlow;
1911*a81c3ea0SDavid du Colombier switch(io->tok){
1912*a81c3ea0SDavid du Colombier case Tdtokin:
1913*a81c3ea0SDavid du Colombier ed->ctrl |= Edin;
1914*a81c3ea0SDavid du Colombier break;
1915*a81c3ea0SDavid du Colombier case Tdtokout:
1916*a81c3ea0SDavid du Colombier ed->ctrl |= Edout;
1917*a81c3ea0SDavid du Colombier break;
1918*a81c3ea0SDavid du Colombier default:
1919*a81c3ea0SDavid du Colombier ed->ctrl |= Edtddir; /* Td will say */
1920*a81c3ea0SDavid du Colombier break;
1921*a81c3ea0SDavid du Colombier }
1922*a81c3ea0SDavid du Colombier
1923*a81c3ea0SDavid du Colombier switch(ep->ttype){
1924*a81c3ea0SDavid du Colombier case Tctl:
1925*a81c3ea0SDavid du Colombier ilock(ctlr);
1926*a81c3ea0SDavid du Colombier edlinked(ed, ctlhd(ctlr));
1927*a81c3ea0SDavid du Colombier setctlhd(ctlr, ed);
1928*a81c3ea0SDavid du Colombier iunlock(ctlr);
1929*a81c3ea0SDavid du Colombier break;
1930*a81c3ea0SDavid du Colombier case Tbulk:
1931*a81c3ea0SDavid du Colombier ilock(ctlr);
1932*a81c3ea0SDavid du Colombier edlinked(ed, bulkhd(ctlr));
1933*a81c3ea0SDavid du Colombier setbulkhd(ctlr, ed);
1934*a81c3ea0SDavid du Colombier iunlock(ctlr);
1935*a81c3ea0SDavid du Colombier break;
1936*a81c3ea0SDavid du Colombier case Tintr:
1937*a81c3ea0SDavid du Colombier case Tiso:
1938*a81c3ea0SDavid du Colombier ilock(ctlr);
1939*a81c3ea0SDavid du Colombier schedq(ctlr, io, ep->pollival);
1940*a81c3ea0SDavid du Colombier iunlock(ctlr);
1941*a81c3ea0SDavid du Colombier break;
1942*a81c3ea0SDavid du Colombier default:
1943*a81c3ea0SDavid du Colombier panic("ohci: newed: bad ttype");
1944*a81c3ea0SDavid du Colombier }
1945*a81c3ea0SDavid du Colombier poperror();
1946*a81c3ea0SDavid du Colombier return ed;
1947*a81c3ea0SDavid du Colombier }
1948*a81c3ea0SDavid du Colombier
1949*a81c3ea0SDavid du Colombier static void
isoopen(Ctlr * ctlr,Ep * ep)1950*a81c3ea0SDavid du Colombier isoopen(Ctlr *ctlr, Ep *ep)
1951*a81c3ea0SDavid du Colombier {
1952*a81c3ea0SDavid du Colombier Td *td, *edtds;
1953*a81c3ea0SDavid du Colombier Isoio *iso;
1954*a81c3ea0SDavid du Colombier int i;
1955*a81c3ea0SDavid du Colombier
1956*a81c3ea0SDavid du Colombier iso = ep->aux;
1957*a81c3ea0SDavid du Colombier iso->usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
1958*a81c3ea0SDavid du Colombier iso->bw = ep->hz * ep->samplesz; /* bytes/sec */
1959*a81c3ea0SDavid du Colombier if(ep->mode != OWRITE){
1960*a81c3ea0SDavid du Colombier print("ohci: bug: iso input streams not implemented\n");
1961*a81c3ea0SDavid du Colombier error("ohci iso input streams not implemented");
1962*a81c3ea0SDavid du Colombier }else
1963*a81c3ea0SDavid du Colombier iso->tok = Tdtokout;
1964*a81c3ea0SDavid du Colombier
1965*a81c3ea0SDavid du Colombier iso->left = 0;
1966*a81c3ea0SDavid du Colombier iso->nerrs = 0;
1967*a81c3ea0SDavid du Colombier iso->frno = TRUNC(ctlr->ohci->fmnumber + 10, Ntdframes);
1968*a81c3ea0SDavid du Colombier iso->nframes = 1000 / ep->pollival;
1969*a81c3ea0SDavid du Colombier if(iso->nframes < 10){
1970*a81c3ea0SDavid du Colombier print("ohci: isoopen: less than 10 frames; using 10.\n");
1971*a81c3ea0SDavid du Colombier iso->nframes = 10;
1972*a81c3ea0SDavid du Colombier }
1973*a81c3ea0SDavid du Colombier iso->navail = iso->nframes;
1974*a81c3ea0SDavid du Colombier iso->atds = edtds = nil;
1975*a81c3ea0SDavid du Colombier for(i = 0; i < iso->nframes-1; i++){ /* -1 for dummy */
1976*a81c3ea0SDavid du Colombier td = tdalloc();
1977*a81c3ea0SDavid du Colombier td->ep = ep;
1978*a81c3ea0SDavid du Colombier td->io = iso;
1979*a81c3ea0SDavid du Colombier td->bp = allocb(ep->maxpkt);
1980*a81c3ea0SDavid du Colombier td->anext = iso->atds; /* link as avail */
1981*a81c3ea0SDavid du Colombier iso->atds = td;
1982*a81c3ea0SDavid du Colombier td->next = edtds;
1983*a81c3ea0SDavid du Colombier edtds = td;
1984*a81c3ea0SDavid du Colombier }
1985*a81c3ea0SDavid du Colombier newed(ctlr, ep, iso, "iso"); /* allocates a dummy td */
1986*a81c3ea0SDavid du Colombier iso->ed->tds->bp = allocb(ep->maxpkt); /* but not its block */
1987*a81c3ea0SDavid du Colombier iso->ed->tds->next = edtds;
1988*a81c3ea0SDavid du Colombier isodtdinit(ep, iso, iso->ed->tds);
1989*a81c3ea0SDavid du Colombier }
1990*a81c3ea0SDavid du Colombier
1991*a81c3ea0SDavid du Colombier /*
1992*a81c3ea0SDavid du Colombier * Allocate the endpoint and set it up for I/O
1993*a81c3ea0SDavid du Colombier * in the controller. This must follow what's said
1994*a81c3ea0SDavid du Colombier * in Ep regarding configuration, including perhaps
1995*a81c3ea0SDavid du Colombier * the saved toggles (saved on a previous close of
1996*a81c3ea0SDavid du Colombier * the endpoint data file by epclose).
1997*a81c3ea0SDavid du Colombier */
1998*a81c3ea0SDavid du Colombier static void
epopen(Ep * ep)1999*a81c3ea0SDavid du Colombier epopen(Ep *ep)
2000*a81c3ea0SDavid du Colombier {
2001*a81c3ea0SDavid du Colombier Ctlr *ctlr;
2002*a81c3ea0SDavid du Colombier Qio *io;
2003*a81c3ea0SDavid du Colombier Ctlio *cio;
2004*a81c3ea0SDavid du Colombier ulong usbid;
2005*a81c3ea0SDavid du Colombier
2006*a81c3ea0SDavid du Colombier ctlr = ep->hp->aux;
2007*a81c3ea0SDavid du Colombier deprint("ohci: epopen ep%d.%d\n", ep->dev->nb, ep->nb);
2008*a81c3ea0SDavid du Colombier if(ep->aux != nil)
2009*a81c3ea0SDavid du Colombier panic("ohci: epopen called with open ep");
2010*a81c3ea0SDavid du Colombier if(waserror()){
2011*a81c3ea0SDavid du Colombier free(ep->aux);
2012*a81c3ea0SDavid du Colombier ep->aux = nil;
2013*a81c3ea0SDavid du Colombier nexterror();
2014*a81c3ea0SDavid du Colombier }
2015*a81c3ea0SDavid du Colombier switch(ep->ttype){
2016*a81c3ea0SDavid du Colombier case Tnone:
2017*a81c3ea0SDavid du Colombier error("endpoint not configured");
2018*a81c3ea0SDavid du Colombier case Tiso:
2019*a81c3ea0SDavid du Colombier ep->aux = smalloc(sizeof(Isoio));
2020*a81c3ea0SDavid du Colombier isoopen(ctlr, ep);
2021*a81c3ea0SDavid du Colombier break;
2022*a81c3ea0SDavid du Colombier case Tctl:
2023*a81c3ea0SDavid du Colombier cio = ep->aux = smalloc(sizeof(Ctlio));
2024*a81c3ea0SDavid du Colombier cio->debug = ep->debug;
2025*a81c3ea0SDavid du Colombier cio->ndata = -1;
2026*a81c3ea0SDavid du Colombier cio->data = nil;
2027*a81c3ea0SDavid du Colombier cio->tok = -1; /* invalid; Tds will say */
2028*a81c3ea0SDavid du Colombier if(ep->dev->isroot != 0 && ep->nb == 0) /* root hub */
2029*a81c3ea0SDavid du Colombier break;
2030*a81c3ea0SDavid du Colombier newed(ctlr, ep, cio, "epc");
2031*a81c3ea0SDavid du Colombier break;
2032*a81c3ea0SDavid du Colombier case Tbulk:
2033*a81c3ea0SDavid du Colombier ep->pollival = 1; /* assume this; doesn't really matter */
2034*a81c3ea0SDavid du Colombier /* and fall... */
2035*a81c3ea0SDavid du Colombier case Tintr:
2036*a81c3ea0SDavid du Colombier io = ep->aux = smalloc(sizeof(Qio)*2);
2037*a81c3ea0SDavid du Colombier io[OREAD].debug = io[OWRITE].debug = ep->debug;
2038*a81c3ea0SDavid du Colombier usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
2039*a81c3ea0SDavid du Colombier if(ep->mode != OREAD){
2040*a81c3ea0SDavid du Colombier if(ep->toggle[OWRITE] != 0)
2041*a81c3ea0SDavid du Colombier io[OWRITE].toggle = Tddata1;
2042*a81c3ea0SDavid du Colombier else
2043*a81c3ea0SDavid du Colombier io[OWRITE].toggle = Tddata0;
2044*a81c3ea0SDavid du Colombier io[OWRITE].tok = Tdtokout;
2045*a81c3ea0SDavid du Colombier io[OWRITE].usbid = usbid;
2046*a81c3ea0SDavid du Colombier io[OWRITE].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */
2047*a81c3ea0SDavid du Colombier newed(ctlr, ep, io+OWRITE, "epw");
2048*a81c3ea0SDavid du Colombier }
2049*a81c3ea0SDavid du Colombier if(ep->mode != OWRITE){
2050*a81c3ea0SDavid du Colombier if(ep->toggle[OREAD] != 0)
2051*a81c3ea0SDavid du Colombier io[OREAD].toggle = Tddata1;
2052*a81c3ea0SDavid du Colombier else
2053*a81c3ea0SDavid du Colombier io[OREAD].toggle = Tddata0;
2054*a81c3ea0SDavid du Colombier io[OREAD].tok = Tdtokin;
2055*a81c3ea0SDavid du Colombier io[OREAD].usbid = usbid;
2056*a81c3ea0SDavid du Colombier io[OREAD].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */
2057*a81c3ea0SDavid du Colombier newed(ctlr, ep, io+OREAD, "epr");
2058*a81c3ea0SDavid du Colombier }
2059*a81c3ea0SDavid du Colombier break;
2060*a81c3ea0SDavid du Colombier }
2061*a81c3ea0SDavid du Colombier deprint("ohci: epopen done:\n");
2062*a81c3ea0SDavid du Colombier if(debug || ep->debug)
2063*a81c3ea0SDavid du Colombier dump(ep->hp);
2064*a81c3ea0SDavid du Colombier poperror();
2065*a81c3ea0SDavid du Colombier }
2066*a81c3ea0SDavid du Colombier
2067*a81c3ea0SDavid du Colombier static void
cancelio(Ep * ep,Qio * io)2068*a81c3ea0SDavid du Colombier cancelio(Ep *ep, Qio *io)
2069*a81c3ea0SDavid du Colombier {
2070*a81c3ea0SDavid du Colombier Ed *ed;
2071*a81c3ea0SDavid du Colombier Ctlr *ctlr;
2072*a81c3ea0SDavid du Colombier
2073*a81c3ea0SDavid du Colombier ctlr = ep->hp->aux;
2074*a81c3ea0SDavid du Colombier
2075*a81c3ea0SDavid du Colombier ilock(ctlr);
2076*a81c3ea0SDavid du Colombier if(io == nil || io->state == Qclose){
2077*a81c3ea0SDavid du Colombier assert(io == nil || io->ed == nil);
2078*a81c3ea0SDavid du Colombier iunlock(ctlr);
2079*a81c3ea0SDavid du Colombier return;
2080*a81c3ea0SDavid du Colombier }
2081*a81c3ea0SDavid du Colombier ed = io->ed;
2082*a81c3ea0SDavid du Colombier io->state = Qclose;
2083*a81c3ea0SDavid du Colombier io->err = Eio;
2084*a81c3ea0SDavid du Colombier aborttds(io);
2085*a81c3ea0SDavid du Colombier iunlock(ctlr);
2086*a81c3ea0SDavid du Colombier if(!waserror()){
2087*a81c3ea0SDavid du Colombier tsleep(&up->sleep, return0, 0, Abortdelay);
2088*a81c3ea0SDavid du Colombier poperror();
2089*a81c3ea0SDavid du Colombier }
2090*a81c3ea0SDavid du Colombier
2091*a81c3ea0SDavid du Colombier wakeup(io);
2092*a81c3ea0SDavid du Colombier qlock(io);
2093*a81c3ea0SDavid du Colombier /* wait for epio if running */
2094*a81c3ea0SDavid du Colombier qunlock(io);
2095*a81c3ea0SDavid du Colombier
2096*a81c3ea0SDavid du Colombier ilock(ctlr);
2097*a81c3ea0SDavid du Colombier switch(ep->ttype){
2098*a81c3ea0SDavid du Colombier case Tctl:
2099*a81c3ea0SDavid du Colombier unlinkctl(ctlr, ed);
2100*a81c3ea0SDavid du Colombier break;
2101*a81c3ea0SDavid du Colombier case Tbulk:
2102*a81c3ea0SDavid du Colombier unlinkbulk(ctlr, ed);
2103*a81c3ea0SDavid du Colombier break;
2104*a81c3ea0SDavid du Colombier case Tintr:
2105*a81c3ea0SDavid du Colombier case Tiso:
2106*a81c3ea0SDavid du Colombier unschedq(ctlr, io);
2107*a81c3ea0SDavid du Colombier break;
2108*a81c3ea0SDavid du Colombier default:
2109*a81c3ea0SDavid du Colombier panic("ohci cancelio: bad ttype");
2110*a81c3ea0SDavid du Colombier }
2111*a81c3ea0SDavid du Colombier iunlock(ctlr);
2112*a81c3ea0SDavid du Colombier edfree(io->ed);
2113*a81c3ea0SDavid du Colombier io->ed = nil;
2114*a81c3ea0SDavid du Colombier }
2115*a81c3ea0SDavid du Colombier
2116*a81c3ea0SDavid du Colombier static void
epclose(Ep * ep)2117*a81c3ea0SDavid du Colombier epclose(Ep *ep)
2118*a81c3ea0SDavid du Colombier {
2119*a81c3ea0SDavid du Colombier Ctlio *cio;
2120*a81c3ea0SDavid du Colombier Isoio *iso;
2121*a81c3ea0SDavid du Colombier Qio *io;
2122*a81c3ea0SDavid du Colombier
2123*a81c3ea0SDavid du Colombier deprint("ohci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
2124*a81c3ea0SDavid du Colombier if(ep->aux == nil)
2125*a81c3ea0SDavid du Colombier panic("ohci: epclose called with closed ep");
2126*a81c3ea0SDavid du Colombier switch(ep->ttype){
2127*a81c3ea0SDavid du Colombier case Tctl:
2128*a81c3ea0SDavid du Colombier cio = ep->aux;
2129*a81c3ea0SDavid du Colombier cancelio(ep, cio);
2130*a81c3ea0SDavid du Colombier free(cio->data);
2131*a81c3ea0SDavid du Colombier cio->data = nil;
2132*a81c3ea0SDavid du Colombier break;
2133*a81c3ea0SDavid du Colombier case Tbulk:
2134*a81c3ea0SDavid du Colombier case Tintr:
2135*a81c3ea0SDavid du Colombier io = ep->aux;
2136*a81c3ea0SDavid du Colombier if(ep->mode != OWRITE){
2137*a81c3ea0SDavid du Colombier cancelio(ep, &io[OREAD]);
2138*a81c3ea0SDavid du Colombier if(io[OREAD].toggle == Tddata1)
2139*a81c3ea0SDavid du Colombier ep->toggle[OREAD] = 1;
2140*a81c3ea0SDavid du Colombier }
2141*a81c3ea0SDavid du Colombier if(ep->mode != OREAD){
2142*a81c3ea0SDavid du Colombier cancelio(ep, &io[OWRITE]);
2143*a81c3ea0SDavid du Colombier if(io[OWRITE].toggle == Tddata1)
2144*a81c3ea0SDavid du Colombier ep->toggle[OWRITE] = 1;
2145*a81c3ea0SDavid du Colombier }
2146*a81c3ea0SDavid du Colombier break;
2147*a81c3ea0SDavid du Colombier case Tiso:
2148*a81c3ea0SDavid du Colombier iso = ep->aux;
2149*a81c3ea0SDavid du Colombier cancelio(ep, iso);
2150*a81c3ea0SDavid du Colombier break;
2151*a81c3ea0SDavid du Colombier default:
2152*a81c3ea0SDavid du Colombier panic("epclose: bad ttype %d", ep->ttype);
2153*a81c3ea0SDavid du Colombier }
2154*a81c3ea0SDavid du Colombier
2155*a81c3ea0SDavid du Colombier deprint("ohci: epclose ep%d.%d: done\n", ep->dev->nb, ep->nb);
2156*a81c3ea0SDavid du Colombier free(ep->aux);
2157*a81c3ea0SDavid du Colombier ep->aux = nil;
2158*a81c3ea0SDavid du Colombier }
2159*a81c3ea0SDavid du Colombier
2160*a81c3ea0SDavid du Colombier static int
portreset(Hci * hp,int port,int on)2161*a81c3ea0SDavid du Colombier portreset(Hci *hp, int port, int on)
2162*a81c3ea0SDavid du Colombier {
2163*a81c3ea0SDavid du Colombier Ctlr *ctlr;
2164*a81c3ea0SDavid du Colombier Ohci *ohci;
2165*a81c3ea0SDavid du Colombier
2166*a81c3ea0SDavid du Colombier if(on == 0)
2167*a81c3ea0SDavid du Colombier return 0;
2168*a81c3ea0SDavid du Colombier
2169*a81c3ea0SDavid du Colombier ctlr = hp->aux;
2170*a81c3ea0SDavid du Colombier qlock(&ctlr->resetl);
2171*a81c3ea0SDavid du Colombier if(waserror()){
2172*a81c3ea0SDavid du Colombier qunlock(&ctlr->resetl);
2173*a81c3ea0SDavid du Colombier nexterror();
2174*a81c3ea0SDavid du Colombier }
2175*a81c3ea0SDavid du Colombier ilock(ctlr);
2176*a81c3ea0SDavid du Colombier ohci = ctlr->ohci;
2177*a81c3ea0SDavid du Colombier ohci->rhportsts[port - 1] = Spp;
2178*a81c3ea0SDavid du Colombier if((ohci->rhportsts[port - 1] & Ccs) == 0){
2179*a81c3ea0SDavid du Colombier iunlock(ctlr);
2180*a81c3ea0SDavid du Colombier error("port not connected");
2181*a81c3ea0SDavid du Colombier }
2182*a81c3ea0SDavid du Colombier ohci->rhportsts[port - 1] = Spr;
2183*a81c3ea0SDavid du Colombier while((ohci->rhportsts[port - 1] & Prsc) == 0){
2184*a81c3ea0SDavid du Colombier iunlock(ctlr);
2185*a81c3ea0SDavid du Colombier dprint("ohci: portreset, wait for reset complete\n");
2186*a81c3ea0SDavid du Colombier ilock(ctlr);
2187*a81c3ea0SDavid du Colombier }
2188*a81c3ea0SDavid du Colombier ohci->rhportsts[port - 1] = Prsc;
2189*a81c3ea0SDavid du Colombier iunlock(ctlr);
2190*a81c3ea0SDavid du Colombier poperror();
2191*a81c3ea0SDavid du Colombier qunlock(&ctlr->resetl);
2192*a81c3ea0SDavid du Colombier return 0;
2193*a81c3ea0SDavid du Colombier }
2194*a81c3ea0SDavid du Colombier
2195*a81c3ea0SDavid du Colombier static int
portenable(Hci * hp,int port,int on)2196*a81c3ea0SDavid du Colombier portenable(Hci *hp, int port, int on)
2197*a81c3ea0SDavid du Colombier {
2198*a81c3ea0SDavid du Colombier Ctlr *ctlr;
2199*a81c3ea0SDavid du Colombier
2200*a81c3ea0SDavid du Colombier ctlr = hp->aux;
2201*a81c3ea0SDavid du Colombier dprint("ohci: %#p port %d enable=%d\n", ctlr->ohci, port, on);
2202*a81c3ea0SDavid du Colombier qlock(&ctlr->resetl);
2203*a81c3ea0SDavid du Colombier if(waserror()){
2204*a81c3ea0SDavid du Colombier qunlock(&ctlr->resetl);
2205*a81c3ea0SDavid du Colombier nexterror();
2206*a81c3ea0SDavid du Colombier }
2207*a81c3ea0SDavid du Colombier ilock(ctlr);
2208*a81c3ea0SDavid du Colombier if(on)
2209*a81c3ea0SDavid du Colombier ctlr->ohci->rhportsts[port - 1] = Spe | Spp;
2210*a81c3ea0SDavid du Colombier else
2211*a81c3ea0SDavid du Colombier ctlr->ohci->rhportsts[port - 1] = Cpe;
2212*a81c3ea0SDavid du Colombier iunlock(ctlr);
2213*a81c3ea0SDavid du Colombier tsleep(&up->sleep, return0, 0, Enabledelay);
2214*a81c3ea0SDavid du Colombier poperror();
2215*a81c3ea0SDavid du Colombier qunlock(&ctlr->resetl);
2216*a81c3ea0SDavid du Colombier return 0;
2217*a81c3ea0SDavid du Colombier }
2218*a81c3ea0SDavid du Colombier
2219*a81c3ea0SDavid du Colombier static int
portstatus(Hci * hp,int port)2220*a81c3ea0SDavid du Colombier portstatus(Hci *hp, int port)
2221*a81c3ea0SDavid du Colombier {
2222*a81c3ea0SDavid du Colombier int v;
2223*a81c3ea0SDavid du Colombier Ctlr *ub;
2224*a81c3ea0SDavid du Colombier ulong ohcistatus;
2225*a81c3ea0SDavid du Colombier
2226*a81c3ea0SDavid du Colombier /*
2227*a81c3ea0SDavid du Colombier * We must return status bits as a
2228*a81c3ea0SDavid du Colombier * get port status hub request would do.
2229*a81c3ea0SDavid du Colombier */
2230*a81c3ea0SDavid du Colombier ub = hp->aux;
2231*a81c3ea0SDavid du Colombier ohcistatus = ub->ohci->rhportsts[port - 1];
2232*a81c3ea0SDavid du Colombier v = 0;
2233*a81c3ea0SDavid du Colombier if(ohcistatus & Ccs)
2234*a81c3ea0SDavid du Colombier v |= HPpresent;
2235*a81c3ea0SDavid du Colombier if(ohcistatus & Pes)
2236*a81c3ea0SDavid du Colombier v |= HPenable;
2237*a81c3ea0SDavid du Colombier if(ohcistatus & Pss)
2238*a81c3ea0SDavid du Colombier v |= HPsuspend;
2239*a81c3ea0SDavid du Colombier if(ohcistatus & Prs)
2240*a81c3ea0SDavid du Colombier v |= HPreset;
2241*a81c3ea0SDavid du Colombier else {
2242*a81c3ea0SDavid du Colombier /* port is not in reset; these potential writes are ok */
2243*a81c3ea0SDavid du Colombier if(ohcistatus & Csc){
2244*a81c3ea0SDavid du Colombier v |= HPstatuschg;
2245*a81c3ea0SDavid du Colombier ub->ohci->rhportsts[port - 1] = Csc;
2246*a81c3ea0SDavid du Colombier }
2247*a81c3ea0SDavid du Colombier if(ohcistatus & Pesc){
2248*a81c3ea0SDavid du Colombier v |= HPchange;
2249*a81c3ea0SDavid du Colombier ub->ohci->rhportsts[port - 1] = Pesc;
2250*a81c3ea0SDavid du Colombier }
2251*a81c3ea0SDavid du Colombier }
2252*a81c3ea0SDavid du Colombier if(ohcistatus & Lsda)
2253*a81c3ea0SDavid du Colombier v |= HPslow;
2254*a81c3ea0SDavid du Colombier if(v & (HPstatuschg|HPchange))
2255*a81c3ea0SDavid du Colombier ddprint("ohci port %d sts %#ulx hub sts %#x\n", port, ohcistatus, v);
2256*a81c3ea0SDavid du Colombier return v;
2257*a81c3ea0SDavid du Colombier }
2258*a81c3ea0SDavid du Colombier
2259*a81c3ea0SDavid du Colombier static void
dumpohci(Ctlr * ctlr)2260*a81c3ea0SDavid du Colombier dumpohci(Ctlr *ctlr)
2261*a81c3ea0SDavid du Colombier {
2262*a81c3ea0SDavid du Colombier int i;
2263*a81c3ea0SDavid du Colombier ulong *ohci;
2264*a81c3ea0SDavid du Colombier
2265*a81c3ea0SDavid du Colombier ohci = &ctlr->ohci->revision;
2266*a81c3ea0SDavid du Colombier print("ohci registers: \n");
2267*a81c3ea0SDavid du Colombier for(i = 0; i < sizeof(Ohci)/sizeof(ulong); i++)
2268*a81c3ea0SDavid du Colombier if(i < 3 || ohci[i] != 0)
2269*a81c3ea0SDavid du Colombier print("\t[%#2.2x]\t%#8.8ulx\n", i * 4, ohci[i]);
2270*a81c3ea0SDavid du Colombier print("\n");
2271*a81c3ea0SDavid du Colombier }
2272*a81c3ea0SDavid du Colombier
2273*a81c3ea0SDavid du Colombier static void
init(Hci * hp)2274*a81c3ea0SDavid du Colombier init(Hci *hp)
2275*a81c3ea0SDavid du Colombier {
2276*a81c3ea0SDavid du Colombier Ctlr *ctlr;
2277*a81c3ea0SDavid du Colombier Ohci *ohci;
2278*a81c3ea0SDavid du Colombier int i;
2279*a81c3ea0SDavid du Colombier ulong ival, ctrl, fmi;
2280*a81c3ea0SDavid du Colombier
2281*a81c3ea0SDavid du Colombier ctlr = hp->aux;
2282*a81c3ea0SDavid du Colombier dprint("ohci %#p init\n", ctlr->ohci);
2283*a81c3ea0SDavid du Colombier ohci = ctlr->ohci;
2284*a81c3ea0SDavid du Colombier
2285*a81c3ea0SDavid du Colombier fmi = ctlr->ohci->fminterval;
2286*a81c3ea0SDavid du Colombier ctlr->ohci->cmdsts = Shcr; /* reset the block */
2287*a81c3ea0SDavid du Colombier while(ctlr->ohci->cmdsts & Shcr)
2288*a81c3ea0SDavid du Colombier delay(1); /* wait till reset complete, Ohci says 10us max. */
2289*a81c3ea0SDavid du Colombier ctlr->ohci->fminterval = fmi;
2290*a81c3ea0SDavid du Colombier
2291*a81c3ea0SDavid du Colombier /*
2292*a81c3ea0SDavid du Colombier * now that soft reset is done we are in suspend state.
2293*a81c3ea0SDavid du Colombier * Setup registers which take in suspend state
2294*a81c3ea0SDavid du Colombier * (will only be here for 2ms).
2295*a81c3ea0SDavid du Colombier */
2296*a81c3ea0SDavid du Colombier
2297*a81c3ea0SDavid du Colombier ctlr->ohci->hcca = ptr2pa(ctlr->hcca);
2298*a81c3ea0SDavid du Colombier setctlhd(ctlr, nil);
2299*a81c3ea0SDavid du Colombier ctlr->ohci->ctlcurred = 0;
2300*a81c3ea0SDavid du Colombier setbulkhd(ctlr, nil);
2301*a81c3ea0SDavid du Colombier ctlr->ohci->bulkcurred = 0;
2302*a81c3ea0SDavid du Colombier
2303*a81c3ea0SDavid du Colombier ohci->intrenable = Mie | Wdh | Ue;
2304*a81c3ea0SDavid du Colombier ohci->control |= Ccle | Cble | Cple | Cie | Cfsoper;
2305*a81c3ea0SDavid du Colombier
2306*a81c3ea0SDavid du Colombier /* set frame after operational */
2307*a81c3ea0SDavid du Colombier ohci->rhdesca = Nps; /* no power switching */
2308*a81c3ea0SDavid du Colombier if(ohci->rhdesca & Nps){
2309*a81c3ea0SDavid du Colombier dprint("ohci: ports are not power switched\n");
2310*a81c3ea0SDavid du Colombier }else{
2311*a81c3ea0SDavid du Colombier dprint("ohci: ports are power switched\n");
2312*a81c3ea0SDavid du Colombier ohci->rhdesca &= ~Psm;
2313*a81c3ea0SDavid du Colombier ohci->rhsts &= ~Lpsc;
2314*a81c3ea0SDavid du Colombier }
2315*a81c3ea0SDavid du Colombier for(i = 0; i < ctlr->nports; i++) /* paranoia */
2316*a81c3ea0SDavid du Colombier ohci->rhportsts[i] = 0; /* this has no effect */
2317*a81c3ea0SDavid du Colombier delay(50);
2318*a81c3ea0SDavid du Colombier
2319*a81c3ea0SDavid du Colombier for(i = 0; i < ctlr->nports; i++){
2320*a81c3ea0SDavid du Colombier ohci->rhportsts[i] = Spp;
2321*a81c3ea0SDavid du Colombier if((ohci->rhportsts[i] & Ccs) != 0)
2322*a81c3ea0SDavid du Colombier ohci->rhportsts[i] |= Spr;
2323*a81c3ea0SDavid du Colombier }
2324*a81c3ea0SDavid du Colombier delay(100);
2325*a81c3ea0SDavid du Colombier
2326*a81c3ea0SDavid du Colombier ctrl = ohci->control;
2327*a81c3ea0SDavid du Colombier if((ctrl & Cfsmask) != Cfsoper){
2328*a81c3ea0SDavid du Colombier ctrl = (ctrl & ~Cfsmask) | Cfsoper;
2329*a81c3ea0SDavid du Colombier ohci->control = ctrl;
2330*a81c3ea0SDavid du Colombier ohci->rhsts = Lpsc;
2331*a81c3ea0SDavid du Colombier }
2332*a81c3ea0SDavid du Colombier ival = ohci->fminterval & ~(Fmaxpktmask << Fmaxpktshift);
2333*a81c3ea0SDavid du Colombier ohci->fminterval = ival | (5120 << Fmaxpktshift);
2334*a81c3ea0SDavid du Colombier
2335*a81c3ea0SDavid du Colombier if(debug > 1)
2336*a81c3ea0SDavid du Colombier dumpohci(ctlr);
2337*a81c3ea0SDavid du Colombier }
2338*a81c3ea0SDavid du Colombier
2339*a81c3ea0SDavid du Colombier static void
scanpci(void)2340*a81c3ea0SDavid du Colombier scanpci(void)
2341*a81c3ea0SDavid du Colombier {
2342*a81c3ea0SDavid du Colombier ulong mem;
2343*a81c3ea0SDavid du Colombier Ctlr *ctlr;
2344*a81c3ea0SDavid du Colombier Pcidev *p;
2345*a81c3ea0SDavid du Colombier int i;
2346*a81c3ea0SDavid du Colombier static int already = 0;
2347*a81c3ea0SDavid du Colombier
2348*a81c3ea0SDavid du Colombier if(already)
2349*a81c3ea0SDavid du Colombier return;
2350*a81c3ea0SDavid du Colombier already = 1;
2351*a81c3ea0SDavid du Colombier p = nil;
2352*a81c3ea0SDavid du Colombier while(p = pcimatch(p, 0, 0)) {
2353*a81c3ea0SDavid du Colombier /*
2354*a81c3ea0SDavid du Colombier * Find Ohci controllers (Programming Interface = 0x10).
2355*a81c3ea0SDavid du Colombier */
2356*a81c3ea0SDavid du Colombier if(p->ccrb != Pcibcserial || p->ccru != Pciscusb ||
2357*a81c3ea0SDavid du Colombier p->ccrp != 0x10)
2358*a81c3ea0SDavid du Colombier continue;
2359*a81c3ea0SDavid du Colombier mem = p->mem[0].bar & ~0x0F;
2360*a81c3ea0SDavid du Colombier dprint("ohci: %x/%x port 0x%lux size 0x%x irq %d\n",
2361*a81c3ea0SDavid du Colombier p->vid, p->did, mem, p->mem[0].size, p->intl);
2362*a81c3ea0SDavid du Colombier if(mem == 0){
2363*a81c3ea0SDavid du Colombier print("ohci: failed to map registers\n");
2364*a81c3ea0SDavid du Colombier continue;
2365*a81c3ea0SDavid du Colombier }
2366*a81c3ea0SDavid du Colombier // if(p->intl == 0xFF || p->intl == 0) {
2367*a81c3ea0SDavid du Colombier // print("ohci: no irq assigned for port %#lux\n", mem);
2368*a81c3ea0SDavid du Colombier // continue;
2369*a81c3ea0SDavid du Colombier // }
2370*a81c3ea0SDavid du Colombier
2371*a81c3ea0SDavid du Colombier ctlr = malloc(sizeof(Ctlr));
2372*a81c3ea0SDavid du Colombier if (ctlr == nil)
2373*a81c3ea0SDavid du Colombier panic("ohci: out of memory");
2374*a81c3ea0SDavid du Colombier ctlr->pcidev = p;
2375*a81c3ea0SDavid du Colombier ctlr->ohci = KSEG1ADDR(PCIMEMADDR(mem));
2376*a81c3ea0SDavid du Colombier dprint("scanpci: ctlr %#p, ohci %#p\n", ctlr, ctlr->ohci);
2377*a81c3ea0SDavid du Colombier pcisetbme(p);
2378*a81c3ea0SDavid du Colombier pcisetpms(p, 0);
2379*a81c3ea0SDavid du Colombier for(i = 0; i < Nhcis; i++)
2380*a81c3ea0SDavid du Colombier if(ctlrs[i] == nil){
2381*a81c3ea0SDavid du Colombier ctlrs[i] = ctlr;
2382*a81c3ea0SDavid du Colombier break;
2383*a81c3ea0SDavid du Colombier }
2384*a81c3ea0SDavid du Colombier if(i == Nhcis)
2385*a81c3ea0SDavid du Colombier print("ohci: bug: no more controllers\n");
2386*a81c3ea0SDavid du Colombier }
2387*a81c3ea0SDavid du Colombier }
2388*a81c3ea0SDavid du Colombier
2389*a81c3ea0SDavid du Colombier static void
usbdebug(Hci *,int d)2390*a81c3ea0SDavid du Colombier usbdebug(Hci*, int d)
2391*a81c3ea0SDavid du Colombier {
2392*a81c3ea0SDavid du Colombier debug = d;
2393*a81c3ea0SDavid du Colombier }
2394*a81c3ea0SDavid du Colombier
2395*a81c3ea0SDavid du Colombier /*
2396*a81c3ea0SDavid du Colombier * build the periodic scheduling tree:
2397*a81c3ea0SDavid du Colombier * framesize must be a multiple of the tree size
2398*a81c3ea0SDavid du Colombier */
2399*a81c3ea0SDavid du Colombier static void
mkqhtree(Ctlr * ctlr)2400*a81c3ea0SDavid du Colombier mkqhtree(Ctlr *ctlr)
2401*a81c3ea0SDavid du Colombier {
2402*a81c3ea0SDavid du Colombier int i, n, d, o, leaf0, depth;
2403*a81c3ea0SDavid du Colombier Ed **tree;
2404*a81c3ea0SDavid du Colombier Qtree *qt;
2405*a81c3ea0SDavid du Colombier
2406*a81c3ea0SDavid du Colombier depth = flog2(32);
2407*a81c3ea0SDavid du Colombier n = (1 << (depth+1)) - 1;
2408*a81c3ea0SDavid du Colombier qt = mallocz(sizeof(*qt), 1);
2409*a81c3ea0SDavid du Colombier if(qt == nil)
2410*a81c3ea0SDavid du Colombier panic("usb: can't allocate scheduling tree");
2411*a81c3ea0SDavid du Colombier qt->nel = n;
2412*a81c3ea0SDavid du Colombier qt->depth = depth;
2413*a81c3ea0SDavid du Colombier qt->bw = mallocz(n * sizeof(qt->bw), 1);
2414*a81c3ea0SDavid du Colombier qt->root = tree = mallocz(n * sizeof(Ed *), 1);
2415*a81c3ea0SDavid du Colombier if(qt->bw == nil || qt->root == nil)
2416*a81c3ea0SDavid du Colombier panic("usb: can't allocate scheduling tree");
2417*a81c3ea0SDavid du Colombier for(i = 0; i < n; i++){
2418*a81c3ea0SDavid du Colombier if((tree[i] = edalloc()) == nil)
2419*a81c3ea0SDavid du Colombier panic("mkqhtree");
2420*a81c3ea0SDavid du Colombier tree[i]->ctrl = (8 << Edmpsshift); /* not needed */
2421*a81c3ea0SDavid du Colombier tree[i]->ctrl |= Edskip;
2422*a81c3ea0SDavid du Colombier
2423*a81c3ea0SDavid du Colombier if(i > 0)
2424*a81c3ea0SDavid du Colombier edlinked(tree[i], tree[(i-1)/2]);
2425*a81c3ea0SDavid du Colombier else
2426*a81c3ea0SDavid du Colombier edlinked(tree[i], nil);
2427*a81c3ea0SDavid du Colombier }
2428*a81c3ea0SDavid du Colombier ctlr->ntree = i;
2429*a81c3ea0SDavid du Colombier dprint("ohci: tree: %d endpoints allocated\n", i);
2430*a81c3ea0SDavid du Colombier
2431*a81c3ea0SDavid du Colombier /* distribute leaves evenly round the frame list */
2432*a81c3ea0SDavid du Colombier leaf0 = n / 2;
2433*a81c3ea0SDavid du Colombier for(i = 0; i < 32; i++){
2434*a81c3ea0SDavid du Colombier o = 0;
2435*a81c3ea0SDavid du Colombier for(d = 0; d < depth; d++){
2436*a81c3ea0SDavid du Colombier o <<= 1;
2437*a81c3ea0SDavid du Colombier if(i & (1 << d))
2438*a81c3ea0SDavid du Colombier o |= 1;
2439*a81c3ea0SDavid du Colombier }
2440*a81c3ea0SDavid du Colombier if(leaf0 + o >= n){
2441*a81c3ea0SDavid du Colombier print("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n);
2442*a81c3ea0SDavid du Colombier break;
2443*a81c3ea0SDavid du Colombier }
2444*a81c3ea0SDavid du Colombier ctlr->hcca->intrtable[i] = ptr2pa(tree[leaf0 + o]);
2445*a81c3ea0SDavid du Colombier }
2446*a81c3ea0SDavid du Colombier ctlr->tree = qt;
2447*a81c3ea0SDavid du Colombier }
2448*a81c3ea0SDavid du Colombier
2449*a81c3ea0SDavid du Colombier static void
ohcimeminit(Ctlr * ctlr)2450*a81c3ea0SDavid du Colombier ohcimeminit(Ctlr *ctlr)
2451*a81c3ea0SDavid du Colombier {
2452*a81c3ea0SDavid du Colombier Hcca *hcca;
2453*a81c3ea0SDavid du Colombier
2454*a81c3ea0SDavid du Colombier edfree(edalloc()); /* allocate pools now */
2455*a81c3ea0SDavid du Colombier tdfree(tdalloc());
2456*a81c3ea0SDavid du Colombier
2457*a81c3ea0SDavid du Colombier hcca = xspanalloc(sizeof(Hcca), 256, 0);
2458*a81c3ea0SDavid du Colombier if(hcca == nil)
2459*a81c3ea0SDavid du Colombier panic("usbhreset: no memory for Hcca");
2460*a81c3ea0SDavid du Colombier hcca = KSEG1ADDR(hcca); // XXX
2461*a81c3ea0SDavid du Colombier memset(hcca, 0, sizeof(*hcca));
2462*a81c3ea0SDavid du Colombier ctlr->hcca = hcca;
2463*a81c3ea0SDavid du Colombier
2464*a81c3ea0SDavid du Colombier mkqhtree(ctlr);
2465*a81c3ea0SDavid du Colombier }
2466*a81c3ea0SDavid du Colombier
2467*a81c3ea0SDavid du Colombier static void
ohcireset(Ctlr * ctlr)2468*a81c3ea0SDavid du Colombier ohcireset(Ctlr *ctlr)
2469*a81c3ea0SDavid du Colombier {
2470*a81c3ea0SDavid du Colombier ilock(ctlr);
2471*a81c3ea0SDavid du Colombier dprint("ohci %#p reset\n", ctlr->ohci);
2472*a81c3ea0SDavid du Colombier
2473*a81c3ea0SDavid du Colombier /*
2474*a81c3ea0SDavid du Colombier * usually enter here in reset, wait till its through,
2475*a81c3ea0SDavid du Colombier * then do our own so we are on known timing conditions.
2476*a81c3ea0SDavid du Colombier * Is this needed?
2477*a81c3ea0SDavid du Colombier */
2478*a81c3ea0SDavid du Colombier delay(100);
2479*a81c3ea0SDavid du Colombier ctlr->ohci->control = 0;
2480*a81c3ea0SDavid du Colombier delay(100);
2481*a81c3ea0SDavid du Colombier
2482*a81c3ea0SDavid du Colombier /* legacy support register: turn off lunacy mode */
2483*a81c3ea0SDavid du Colombier pcicfgw16(ctlr->pcidev, 0xc0, 0x2000);
2484*a81c3ea0SDavid du Colombier
2485*a81c3ea0SDavid du Colombier iunlock(ctlr);
2486*a81c3ea0SDavid du Colombier }
2487*a81c3ea0SDavid du Colombier
2488*a81c3ea0SDavid du Colombier static void
shutdown(Hci * hp)2489*a81c3ea0SDavid du Colombier shutdown(Hci *hp)
2490*a81c3ea0SDavid du Colombier {
2491*a81c3ea0SDavid du Colombier Ctlr *ctlr;
2492*a81c3ea0SDavid du Colombier
2493*a81c3ea0SDavid du Colombier ctlr = hp->aux;
2494*a81c3ea0SDavid du Colombier
2495*a81c3ea0SDavid du Colombier ilock(ctlr);
2496*a81c3ea0SDavid du Colombier ctlr->ohci->intrdisable = Mie;
2497*a81c3ea0SDavid du Colombier ctlr->ohci->control = 0;
2498*a81c3ea0SDavid du Colombier coherence();
2499*a81c3ea0SDavid du Colombier delay(100);
2500*a81c3ea0SDavid du Colombier iunlock(ctlr);
2501*a81c3ea0SDavid du Colombier }
2502*a81c3ea0SDavid du Colombier
2503*a81c3ea0SDavid du Colombier static int
reset(Hci * hp)2504*a81c3ea0SDavid du Colombier reset(Hci *hp)
2505*a81c3ea0SDavid du Colombier {
2506*a81c3ea0SDavid du Colombier int i;
2507*a81c3ea0SDavid du Colombier Ctlr *ctlr;
2508*a81c3ea0SDavid du Colombier Pcidev *p;
2509*a81c3ea0SDavid du Colombier static Lock resetlck;
2510*a81c3ea0SDavid du Colombier
2511*a81c3ea0SDavid du Colombier if(getconf("*nousbohci"))
2512*a81c3ea0SDavid du Colombier return -1;
2513*a81c3ea0SDavid du Colombier ilock(&resetlck);
2514*a81c3ea0SDavid du Colombier scanpci();
2515*a81c3ea0SDavid du Colombier
2516*a81c3ea0SDavid du Colombier /*
2517*a81c3ea0SDavid du Colombier * Any adapter matches if no hp->port is supplied,
2518*a81c3ea0SDavid du Colombier * otherwise the ports must match.
2519*a81c3ea0SDavid du Colombier */
2520*a81c3ea0SDavid du Colombier ctlr = nil;
2521*a81c3ea0SDavid du Colombier for(i = 0; i < Nhcis && ctlrs[i] != nil; i++){
2522*a81c3ea0SDavid du Colombier ctlr = ctlrs[i];
2523*a81c3ea0SDavid du Colombier if(ctlr->active == 0)
2524*a81c3ea0SDavid du Colombier if(hp->port == 0 || hp->port == (uintptr)ctlr->ohci){
2525*a81c3ea0SDavid du Colombier ctlr->active = 1;
2526*a81c3ea0SDavid du Colombier break;
2527*a81c3ea0SDavid du Colombier }
2528*a81c3ea0SDavid du Colombier }
2529*a81c3ea0SDavid du Colombier iunlock(&resetlck);
2530*a81c3ea0SDavid du Colombier if(ctlrs[i] == nil || i == Nhcis)
2531*a81c3ea0SDavid du Colombier return -1;
2532*a81c3ea0SDavid du Colombier if(ctlr->ohci->control == ~0)
2533*a81c3ea0SDavid du Colombier return -1;
2534*a81c3ea0SDavid du Colombier
2535*a81c3ea0SDavid du Colombier
2536*a81c3ea0SDavid du Colombier p = ctlr->pcidev;
2537*a81c3ea0SDavid du Colombier hp->aux = ctlr;
2538*a81c3ea0SDavid du Colombier hp->port = (uintptr)ctlr->ohci;
2539*a81c3ea0SDavid du Colombier hp->irq = ILpci;
2540*a81c3ea0SDavid du Colombier hp->tbdf = p->tbdf;
2541*a81c3ea0SDavid du Colombier ctlr->nports = hp->nports = ctlr->ohci->rhdesca & 0xff;
2542*a81c3ea0SDavid du Colombier
2543*a81c3ea0SDavid du Colombier ohcireset(ctlr);
2544*a81c3ea0SDavid du Colombier ohcimeminit(ctlr);
2545*a81c3ea0SDavid du Colombier
2546*a81c3ea0SDavid du Colombier /*
2547*a81c3ea0SDavid du Colombier * Linkage to the generic HCI driver.
2548*a81c3ea0SDavid du Colombier */
2549*a81c3ea0SDavid du Colombier hp->init = init;
2550*a81c3ea0SDavid du Colombier hp->dump = dump;
2551*a81c3ea0SDavid du Colombier hp->interrupt = interrupt;
2552*a81c3ea0SDavid du Colombier hp->epopen = epopen;
2553*a81c3ea0SDavid du Colombier hp->epclose = epclose;
2554*a81c3ea0SDavid du Colombier hp->epread = epread;
2555*a81c3ea0SDavid du Colombier hp->epwrite = epwrite;
2556*a81c3ea0SDavid du Colombier hp->seprintep = seprintep;
2557*a81c3ea0SDavid du Colombier hp->portenable = portenable;
2558*a81c3ea0SDavid du Colombier hp->portreset = portreset;
2559*a81c3ea0SDavid du Colombier hp->portstatus = portstatus;
2560*a81c3ea0SDavid du Colombier hp->shutdown = shutdown;
2561*a81c3ea0SDavid du Colombier hp->debug = usbdebug;
2562*a81c3ea0SDavid du Colombier hp->type = "ohci";
2563*a81c3ea0SDavid du Colombier return 0;
2564*a81c3ea0SDavid du Colombier }
2565*a81c3ea0SDavid du Colombier
2566*a81c3ea0SDavid du Colombier void
usbohcilink(void)2567*a81c3ea0SDavid du Colombier usbohcilink(void)
2568*a81c3ea0SDavid du Colombier {
2569*a81c3ea0SDavid du Colombier addhcitype("ohci", reset);
2570*a81c3ea0SDavid du Colombier }
2571